[clang] [C23][Parser] Diagnostic for attribute declaration where statement is required (PR #146224)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 7 07:21:22 PDT 2025
https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/146224
>From 43e2dc670d7c9ed5e23b5d26dff1e273c84b5a53 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 3 Jul 2025 02:15:22 +0800
Subject: [PATCH 1/3] [C23][Parser] Diagnostic for attribute declaration where
statement is required
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/docs/ReleaseNotes.rst | 4 ++
.../clang/Basic/DiagnosticParseKinds.td | 4 ++
clang/include/clang/Parse/Parser.h | 5 ++-
clang/lib/Parse/ParseStmt.cpp | 40 ++++++++++++++-----
clang/test/Parser/statements.c | 30 ++++++++++++++
clang/test/Sema/c2x-fallthrough.c | 15 ++++---
6 files changed, 82 insertions(+), 16 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3d893e0aa8e2c..60e7390c31e9b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -747,6 +747,10 @@ Bug Fixes in This Version
- Fixed an infinite recursion when checking constexpr destructors. (#GH141789)
- Fixed a crash when a malformed using declaration appears in a ``constexpr`` function. (#GH144264)
- Fixed a bug when use unicode character name in macro concatenation. (#GH145240)
+- In C23, something like [[/*possible attributes*/]]; is an attribute declaration, not a statement. So it is not
+ allowed by the syntax in places where a statement is required, specifically as the secondary block of a
+ selection or iteration statement. This differs from C++, since C++ allows declaration statements.
+ Clang now warning this patterns. (#GH141659)
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 6c30da376dafb..9115b60cb0ed0 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -276,6 +276,10 @@ def err_expected_while : Error<"expected 'while' in do/while loop">;
def err_expected_semi_after_stmt : Error<"expected ';' after %0 statement">;
def err_expected_semi_after_expr : Error<"expected ';' after expression">;
+def warn_expected_stmt_before_semi_in_secondary_block : Warning<
+ "expected a statement before ';' but got an attribute declaration, "
+ "it is not allowed by the syntax in places where a statement is required">,
+ InGroup<CXXCompat>;
def err_extraneous_token_before_semi : Error<"extraneous '%0' before ';'">;
def err_expected_semi_after_method_proto : Error<
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index a47e23ffbd357..cca4f14a2942a 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -7168,13 +7168,16 @@ class Parser : public CodeCompletionHandler {
AllowStandaloneOpenMPDirectives = 0x2,
/// This context is at the top level of a GNU statement expression.
InStmtExpr = 0x4,
+ /// This context is the C99 secondary-block in selection or iteration
+ /// statement.
+ SecondaryBlockInC = 0x8,
/// The context of a regular substatement.
SubStmt = 0,
/// The context of a compound-statement.
Compound = AllowDeclarationsInC | AllowStandaloneOpenMPDirectives,
- LLVM_MARK_AS_BITMASK_ENUM(InStmtExpr)
+ LLVM_MARK_AS_BITMASK_ENUM(SecondaryBlockInC)
};
/// Act on an expression statement that might be the last statement in a
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 8217151a0259a..6080fb782dfbe 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -63,7 +63,8 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
// at the start of the statement. Thus, we're not using MaybeParseAttributes
// here because we don't want to allow arbitrary orderings.
ParsedAttributes CXX11Attrs(AttrFactory);
- MaybeParseCXX11Attributes(CXX11Attrs, /*MightBeObjCMessageSend*/ true);
+ bool HasStdAttr =
+ MaybeParseCXX11Attributes(CXX11Attrs, /*MightBeObjCMessageSend*/ true);
ParsedAttributes GNUOrMSAttrs(AttrFactory);
if (getLangOpts().OpenCL)
MaybeParseGNUAttributes(GNUOrMSAttrs);
@@ -80,6 +81,12 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
assert((CXX11Attrs.empty() || Res.isInvalid() || Res.isUsable()) &&
"attributes on empty statement");
+ if (HasStdAttr && getLangOpts().C23 &&
+ (StmtCtx & ParsedStmtContext::SecondaryBlockInC) != ParsedStmtContext{} &&
+ isa_and_present<NullStmt>(Res.get()))
+ Diag(Res.get()->getBeginLoc(),
+ diag::warn_expected_stmt_before_semi_in_secondary_block);
+
if (CXX11Attrs.empty() || Res.isInvalid())
return Res;
@@ -1491,6 +1498,10 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
SourceLocation InnerStatementTrailingElseLoc;
StmtResult ThenStmt;
+ ParsedStmtContext StmtCtx = getLangOpts().C99
+ ? ParsedStmtContext::SecondaryBlockInC
+ : ParsedStmtContext::SubStmt;
+
{
bool ShouldEnter = ConstexprCondition && !*ConstexprCondition;
Sema::ExpressionEvaluationContext Context =
@@ -1503,7 +1514,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
EnterExpressionEvaluationContext PotentiallyDiscarded(
Actions, Context, nullptr,
Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter);
- ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc);
+ ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc, StmtCtx);
}
if (Tok.isNot(tok::kw_else))
@@ -1548,7 +1559,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
EnterExpressionEvaluationContext PotentiallyDiscarded(
Actions, Context, nullptr,
Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter);
- ElseStmt = ParseStatement();
+ ElseStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx);
if (ElseStmt.isUsable())
MIChecker.Check();
@@ -1684,8 +1695,11 @@ StmtResult Parser::ParseSwitchStatement(SourceLocation *TrailingElseLoc) {
if (C99orCXX)
getCurScope()->decrementMSManglingNumber();
+ ParsedStmtContext StmtCtx = getLangOpts().C99
+ ? ParsedStmtContext::SecondaryBlockInC
+ : ParsedStmtContext::SubStmt;
// Read the body statement.
- StmtResult Body(ParseStatement(TrailingElseLoc));
+ StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));
// Pop the scopes.
InnerScope.Exit();
@@ -1754,9 +1768,11 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) {
ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace));
MisleadingIndentationChecker MIChecker(*this, MSK_while, WhileLoc);
-
+ ParsedStmtContext StmtCtx = getLangOpts().C99
+ ? ParsedStmtContext::SecondaryBlockInC
+ : ParsedStmtContext::SubStmt;
// Read the body statement.
- StmtResult Body(ParseStatement(TrailingElseLoc));
+ StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));
if (Body.isUsable())
MIChecker.Check();
@@ -1799,9 +1815,11 @@ StmtResult Parser::ParseDoStatement() {
//
bool C99orCXX = getLangOpts().C99 || getLangOpts().CPlusPlus;
ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace));
-
+ ParsedStmtContext StmtCtx = getLangOpts().C99
+ ? ParsedStmtContext::SecondaryBlockInC
+ : ParsedStmtContext::SubStmt;
// Read the body statement.
- StmtResult Body(ParseStatement());
+ StmtResult Body(ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx));
// Pop the body scope if needed.
InnerScope.Exit();
@@ -2221,9 +2239,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
getCurScope()->decrementMSManglingNumber();
MisleadingIndentationChecker MIChecker(*this, MSK_for, ForLoc);
-
+ ParsedStmtContext StmtCtx = getLangOpts().C99
+ ? ParsedStmtContext::SecondaryBlockInC
+ : ParsedStmtContext::SubStmt;
// Read the body statement.
- StmtResult Body(ParseStatement(TrailingElseLoc));
+ StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));
if (Body.isUsable())
MIChecker.Check();
diff --git a/clang/test/Parser/statements.c b/clang/test/Parser/statements.c
index 2566da83d7c53..ef331ac0448b0 100644
--- a/clang/test/Parser/statements.c
+++ b/clang/test/Parser/statements.c
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-unreachable-code
+// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s -Wno-unreachable-code
void test1(void) {
{ ; { ;;}} ;;
@@ -77,3 +78,32 @@ int test9(void) {
return 4, // expected-error {{expected ';' after return statement}}
}
+
+#if __STDC_VERSION__ >= 202311L
+void attr_decl_in_selection_statement(int n) {
+ if (1)
+ [[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
+
+ if (1) {
+
+ } else
+ [[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
+
+
+ switch (n)
+ [[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
+}
+
+void attr_decl_in_iteration_statement(int n) {
+ int i;
+ for (i = 0; i < n; ++i)
+ [[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
+
+ while (i > 0)
+ [[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
+
+ do
+ [[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
+ while (i > 0);
+}
+#endif // __STDC_VERSION__ >= 202311L
diff --git a/clang/test/Sema/c2x-fallthrough.c b/clang/test/Sema/c2x-fallthrough.c
index 092d5285f80ba..d1d3b1e3727cc 100644
--- a/clang/test/Sema/c2x-fallthrough.c
+++ b/clang/test/Sema/c2x-fallthrough.c
@@ -16,17 +16,22 @@ void f(int n) {
}
case 2:
for (int n = 0; n != 10; ++n)
- [[fallthrough]]; // expected-error {{does not directly precede switch label}}
+ [[fallthrough]]; // expected-error {{does not directly precede switch label}} \
+ // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
case 3:
while (1)
- [[fallthrough]]; // expected-error {{does not directly precede switch label}}
+ [[fallthrough]]; // expected-error {{does not directly precede switch label}} \
+ // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
case 4:
while (0)
- [[fallthrough]]; // expected-error {{does not directly precede switch label}}
+ [[fallthrough]]; // expected-error {{does not directly precede switch label}} \
+ // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
case 5:
- do [[fallthrough]]; while (1); // expected-error {{does not directly precede switch label}}
+ do [[fallthrough]]; while (1); // expected-error {{does not directly precede switch label}} \
+ // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
case 6:
- do [[fallthrough]]; while (0); // expected-error {{does not directly precede switch label}}
+ do [[fallthrough]]; while (0); // expected-error {{does not directly precede switch label}} \
+ // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
case 7:
switch (n) {
case 0:
>From a4923f71671c8dd17395120a8c6efce40c61df7d Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 3 Jul 2025 02:23:20 +0800
Subject: [PATCH 2/3] change c2x to c23
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/test/Sema/c2x-fallthrough.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/Sema/c2x-fallthrough.c b/clang/test/Sema/c2x-fallthrough.c
index d1d3b1e3727cc..e37cbb7e2c35b 100644
--- a/clang/test/Sema/c2x-fallthrough.c
+++ b/clang/test/Sema/c2x-fallthrough.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -std=c2x -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c23 -verify %s
// This is the latest version of fallthrough that we support.
_Static_assert(__has_c_attribute(fallthrough) == 201910L);
>From a07b1a7d83572e54ffe754839e87493ce5b7447a Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 7 Jul 2025 22:21:14 +0800
Subject: [PATCH 3/3] Update clang/docs/ReleaseNotes.rst
Co-authored-by: Mariya Podchishchaeva <mariya.podchishchaeva at intel.com>
---
clang/docs/ReleaseNotes.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 60e7390c31e9b..5f907b800bef4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -750,7 +750,7 @@ Bug Fixes in This Version
- In C23, something like [[/*possible attributes*/]]; is an attribute declaration, not a statement. So it is not
allowed by the syntax in places where a statement is required, specifically as the secondary block of a
selection or iteration statement. This differs from C++, since C++ allows declaration statements.
- Clang now warning this patterns. (#GH141659)
+ Clang now emits a warning for these patterns. (#GH141659)
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
More information about the cfe-commits
mailing list