https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/146224
>From 43e2dc670d7c9ed5e23b5d26dff1e273c84b5a53 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Thu, 3 Jul 2025 02:15:22 +0800 Subject: [PATCH 1/4] [C23][Parser] Diagnostic for attribute declaration where statement is required Signed-off-by: yronglin <yronglin...@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 <yronglin...@gmail.com> Date: Thu, 3 Jul 2025 02:23:20 +0800 Subject: [PATCH 2/4] change c2x to c23 Signed-off-by: yronglin <yronglin...@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 <yronglin...@gmail.com> Date: Mon, 7 Jul 2025 22:21:14 +0800 Subject: [PATCH 3/4] Update clang/docs/ReleaseNotes.rst Co-authored-by: Mariya Podchishchaeva <mariya.podchishcha...@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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >From 83ea0b7971197e993a467f24bc6b3bd42bdbce00 Mon Sep 17 00:00:00 2001 From: "Wang, Yihan" <yronglin...@gmail.com> Date: Tue, 8 Jul 2025 00:03:19 +0800 Subject: [PATCH 4/4] Add a diagnostic group Signed-off-by: Wang, Yihan <yronglin...@gmail.com> --- clang/docs/ReleaseNotes.rst | 9 +++++---- clang/include/clang/Basic/DiagnosticGroups.td | 1 + clang/include/clang/Basic/DiagnosticParseKinds.td | 7 +++---- clang/lib/Parse/ParseStmt.cpp | 5 +++-- clang/test/Parser/statements.c | 12 ++++++------ clang/test/Sema/c2x-fallthrough.c | 10 +++++----- 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5f907b800bef4..d0288d0c88421 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -747,10 +747,11 @@ 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 emits a warning for these patterns. (#GH141659) +- 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 emits a warning for these patterns. (#GH141659) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 36fa3227fd6a6..341037282fa21 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -159,6 +159,7 @@ def BuiltinRequiresHeader : DiagGroup<"builtin-requires-header">; def C99Compat : DiagGroup<"c99-compat">; def C23Compat : DiagGroup<"c23-compat">; def : DiagGroup<"c2x-compat", [C23Compat]>; +def CAttributeExtensions : DiagGroup<"c-attribute-extension">; def CppKeywordInC : DiagGroup<"c++-keyword">; def DuplicateDeclSpecifier : DiagGroup<"duplicate-decl-specifier">; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 9115b60cb0ed0..9bbbbd4ed8186 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -276,10 +276,9 @@ 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 warn_attr_in_secondary_block : ExtWarn< + "ISO C does not allow an attribute list to appear here">, + InGroup<CAttributeExtensions>; def err_extraneous_token_before_semi : Error<"extraneous '%0' before ';'">; def err_expected_semi_after_method_proto : Error< diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 6080fb782dfbe..fdf91edd3deb9 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -14,6 +14,7 @@ #include "clang/AST/PrettyDeclStackTrace.h" #include "clang/Basic/Attributes.h" #include "clang/Basic/PrettyStackTrace.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TokenKinds.h" #include "clang/Parse/LoopHint.h" @@ -84,8 +85,8 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts, 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); + Diag(CXX11Attrs.Range.getBegin(), diag::warn_attr_in_secondary_block) + << CXX11Attrs.Range; if (CXX11Attrs.empty() || Res.isInvalid()) return Res; diff --git a/clang/test/Parser/statements.c b/clang/test/Parser/statements.c index ef331ac0448b0..22a6633bdf262 100644 --- a/clang/test/Parser/statements.c +++ b/clang/test/Parser/statements.c @@ -82,28 +82,28 @@ int test9(void) { #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}} + [[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}} 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}} + [[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}} 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}} + [[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}} } 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}} + [[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}} 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}} + [[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}} 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}} + [[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}} while (i > 0); } #endif // __STDC_VERSION__ >= 202311L diff --git a/clang/test/Sema/c2x-fallthrough.c b/clang/test/Sema/c2x-fallthrough.c index e37cbb7e2c35b..7d2f25ed5187b 100644 --- a/clang/test/Sema/c2x-fallthrough.c +++ b/clang/test/Sema/c2x-fallthrough.c @@ -17,21 +17,21 @@ void f(int n) { case 2: for (int n = 0; n != 10; ++n) [[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}} + // expected-warning {{ISO C does not allow an attribute list to appear here}} case 3: while (1) [[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}} + // expected-warning {{ISO C does not allow an attribute list to appear here}} case 4: while (0) [[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}} + // expected-warning {{ISO C does not allow an attribute list to appear here}} case 5: 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}} + // expected-warning {{ISO C does not allow an attribute list to appear here}} case 6: 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}} + // expected-warning {{ISO C does not allow an attribute list to appear here}} case 7: switch (n) { case 0: _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits