https://github.com/yronglin created 
https://github.com/llvm/llvm-project/pull/146224

Fixes: https://github.com/llvm/llvm-project/issues/141659

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.

Therefore, code like the following should give a diagnostic (at least with 
-std=c23 -pedantic), but Clang currently does not produce one:
```cpp
int main(void) {
    if (1)
        [[]];
}
```

>From b671feb6c9bc14bcfe15c420979b2826c8664b80 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin...@gmail.com>
Date: Sat, 28 Jun 2025 23:58:01 +0800
Subject: [PATCH] [C][Parser] Diagnostic for attribute declaration where
 statement is required

Signed-off-by: yronglin <yronglin...@gmail.com>
---
 clang/docs/ReleaseNotes.rst                   |   3 +
 .../clang/Basic/DiagnosticParseKinds.td       |   1 +
 clang/include/clang/Parse/Parser.h            | 668 +++++++++---------
 clang/lib/Parse/ParseStmt.cpp                 |  39 +-
 clang/test/Parser/statements.c                |  27 +
 clang/test/Sema/c2x-fallthrough.c             |  13 +-
 clang/test/Sema/c2x-fallthrough2.c            |  18 +
 clang/test/Sema/fallthrough.cpp               |  75 ++
 8 files changed, 496 insertions(+), 348 deletions(-)
 create mode 100644 clang/test/Sema/c2x-fallthrough2.c
 create mode 100644 clang/test/Sema/fallthrough.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 01f3b7a557a5c..78e20bd8900f8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -727,6 +727,9 @@ 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. Clang now reject this pattern and give a 
diagnostic. (#GH141659)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 6c30da376dafb..a8b77e0f4ebdf 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -276,6 +276,7 @@ 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 err_expected_stmt_before_semi : Error<"expected a statement before ';'">;
 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..7c85bbefe57a8 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -7168,13 +7168,15 @@ 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-/iteration-statement.
+    SecondaryBlock = 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(SecondaryBlock)
   };
 
   /// Act on an expression statement that might be the last statement in a
@@ -7246,337 +7248,339 @@ class Parser : public CodeCompletionHandler {
   ParseStatementOrDeclaration(StmtVector &Stmts, ParsedStmtContext StmtCtx,
                               SourceLocation *TrailingElseLoc = nullptr);
 
-  StmtResult ParseStatementOrDeclarationAfterAttributes(
-      StmtVector &Stmts, ParsedStmtContext StmtCtx,
-      SourceLocation *TrailingElseLoc, ParsedAttributes &DeclAttrs,
-      ParsedAttributes &DeclSpecAttrs);
-
-  /// Parse an expression statement.
-  StmtResult ParseExprStatement(ParsedStmtContext StmtCtx);
-
-  /// ParseLabeledStatement - We have an identifier and a ':' after it.
-  ///
-  /// \verbatim
-  ///       label:
-  ///         identifier ':'
-  /// [GNU]   identifier ':' attributes[opt]
-  ///
-  ///       labeled-statement:
-  ///         label statement
-  /// \endverbatim
-  ///
-  StmtResult ParseLabeledStatement(ParsedAttributes &Attrs,
-                                   ParsedStmtContext StmtCtx);
-
-  /// ParseCaseStatement
-  /// \verbatim
-  ///       labeled-statement:
-  ///         'case' constant-expression ':' statement
-  /// [GNU]   'case' constant-expression '...' constant-expression ':' 
statement
-  /// \endverbatim
-  ///
-  StmtResult ParseCaseStatement(ParsedStmtContext StmtCtx,
-                                bool MissingCase = false,
-                                ExprResult Expr = ExprResult());
-
-  /// ParseDefaultStatement
-  /// \verbatim
-  ///       labeled-statement:
-  ///         'default' ':' statement
-  /// \endverbatim
-  /// Note that this does not parse the 'statement' at the end.
-  ///
-  StmtResult ParseDefaultStatement(ParsedStmtContext StmtCtx);
-
-  StmtResult ParseCompoundStatement(bool isStmtExpr = false);
-
-  /// ParseCompoundStatement - Parse a "{}" block.
-  ///
-  /// \verbatim
-  ///       compound-statement: [C99 6.8.2]
-  ///         { block-item-list[opt] }
-  /// [GNU]   { label-declarations block-item-list } [TODO]
-  ///
-  ///       block-item-list:
-  ///         block-item
-  ///         block-item-list block-item
-  ///
-  ///       block-item:
-  ///         declaration
-  /// [GNU]   '__extension__' declaration
-  ///         statement
-  ///
-  /// [GNU] label-declarations:
-  /// [GNU]   label-declaration
-  /// [GNU]   label-declarations label-declaration
-  ///
-  /// [GNU] label-declaration:
-  /// [GNU]   '__label__' identifier-list ';'
-  /// \endverbatim
-  ///
-  StmtResult ParseCompoundStatement(bool isStmtExpr, unsigned ScopeFlags);
-
-  /// Parse any pragmas at the start of the compound expression. We handle 
these
-  /// separately since some pragmas (FP_CONTRACT) must appear before any C
-  /// statement in the compound, but may be intermingled with other pragmas.
-  void ParseCompoundStatementLeadingPragmas();
-
-  void DiagnoseLabelAtEndOfCompoundStatement();
-
-  /// Consume any extra semi-colons resulting in null statements,
-  /// returning true if any tok::semi were consumed.
-  bool ConsumeNullStmt(StmtVector &Stmts);
-
-  /// ParseCompoundStatementBody - Parse a sequence of statements optionally
-  /// followed by a label and invoke the ActOnCompoundStmt action.  This 
expects
-  /// the '{' to be the current token, and consume the '}' at the end of the
-  /// block.  It does not manipulate the scope stack.
-  StmtResult ParseCompoundStatementBody(bool isStmtExpr = false);
-
-  /// ParseParenExprOrCondition:
-  /// \verbatim
-  /// [C  ]     '(' expression ')'
-  /// [C++]     '(' condition ')'
-  /// [C++1z]   '(' init-statement[opt] condition ')'
-  /// \endverbatim
-  ///
-  /// This function parses and performs error recovery on the specified
-  /// condition or expression (depending on whether we're in C++ or C mode).
-  /// This function goes out of its way to recover well.  It returns true if
-  /// there was a parser error (the right paren couldn't be found), which
-  /// indicates that the caller should try to recover harder.  It returns false
-  /// if the condition is successfully parsed.  Note that a successful parse 
can
-  /// still have semantic errors in the condition. Additionally, it will assign
-  /// the location of the outer-most '(' and ')', to LParenLoc and RParenLoc,
-  /// respectively.
-  bool ParseParenExprOrCondition(StmtResult *InitStmt,
-                                 Sema::ConditionResult &CondResult,
-                                 SourceLocation Loc, Sema::ConditionKind CK,
-                                 SourceLocation &LParenLoc,
-                                 SourceLocation &RParenLoc);
-
-  /// ParseIfStatement
-  /// \verbatim
-  ///       if-statement: [C99 6.8.4.1]
-  ///         'if' '(' expression ')' statement
-  ///         'if' '(' expression ')' statement 'else' statement
-  /// [C++]   'if' '(' condition ')' statement
-  /// [C++]   'if' '(' condition ')' statement 'else' statement
-  /// [C++23] 'if' '!' [opt] consteval compound-statement
-  /// [C++23] 'if' '!' [opt] consteval compound-statement 'else' statement
-  /// \endverbatim
-  ///
-  StmtResult ParseIfStatement(SourceLocation *TrailingElseLoc);
-
-  /// ParseSwitchStatement
-  /// \verbatim
-  ///       switch-statement:
-  ///         'switch' '(' expression ')' statement
-  /// [C++]   'switch' '(' condition ')' statement
-  /// \endverbatim
-  StmtResult ParseSwitchStatement(SourceLocation *TrailingElseLoc);
-
-  /// ParseWhileStatement
-  /// \verbatim
-  ///       while-statement: [C99 6.8.5.1]
-  ///         'while' '(' expression ')' statement
-  /// [C++]   'while' '(' condition ')' statement
-  /// \endverbatim
-  StmtResult ParseWhileStatement(SourceLocation *TrailingElseLoc);
-
-  /// ParseDoStatement
-  /// \verbatim
-  ///       do-statement: [C99 6.8.5.2]
-  ///         'do' statement 'while' '(' expression ')' ';'
-  /// \endverbatim
-  /// Note: this lets the caller parse the end ';'.
-  StmtResult ParseDoStatement();
-
-  /// ParseForStatement
-  /// \verbatim
-  ///       for-statement: [C99 6.8.5.3]
-  ///         'for' '(' expr[opt] ';' expr[opt] ';' expr[opt] ')' statement
-  ///         'for' '(' declaration expr[opt] ';' expr[opt] ')' statement
-  /// [C++]   'for' '(' for-init-statement condition[opt] ';' expression[opt] 
')'
-  /// [C++]       statement
-  /// [C++0x] 'for'
-  ///             'co_await'[opt]    [Coroutines]
-  ///             '(' for-range-declaration ':' for-range-initializer ')'
-  ///             statement
-  /// [OBJC2] 'for' '(' declaration 'in' expr ')' statement
-  /// [OBJC2] 'for' '(' expr 'in' expr ')' statement
-  ///
-  /// [C++] for-init-statement:
-  /// [C++]   expression-statement
-  /// [C++]   simple-declaration
-  /// [C++23] alias-declaration
-  ///
-  /// [C++0x] for-range-declaration:
-  /// [C++0x]   attribute-specifier-seq[opt] type-specifier-seq declarator
-  /// [C++0x] for-range-initializer:
-  /// [C++0x]   expression
-  /// [C++0x]   braced-init-list            [TODO]
-  /// \endverbatim
-  StmtResult ParseForStatement(SourceLocation *TrailingElseLoc);
-
-  /// ParseGotoStatement
-  /// \verbatim
-  ///       jump-statement:
-  ///         'goto' identifier ';'
-  /// [GNU]   'goto' '*' expression ';'
-  /// \endverbatim
-  ///
-  /// Note: this lets the caller parse the end ';'.
-  ///
-  StmtResult ParseGotoStatement();
-
-  /// ParseContinueStatement
-  /// \verbatim
-  ///       jump-statement:
-  ///         'continue' ';'
-  /// \endverbatim
-  ///
-  /// Note: this lets the caller parse the end ';'.
-  ///
-  StmtResult ParseContinueStatement();
-
-  /// ParseBreakStatement
-  /// \verbatim
-  ///       jump-statement:
-  ///         'break' ';'
-  /// \endverbatim
-  ///
-  /// Note: this lets the caller parse the end ';'.
-  ///
-  StmtResult ParseBreakStatement();
-
-  /// ParseReturnStatement
-  /// \verbatim
-  ///       jump-statement:
-  ///         'return' expression[opt] ';'
-  ///         'return' braced-init-list ';'
-  ///         'co_return' expression[opt] ';'
-  ///         'co_return' braced-init-list ';'
-  /// \endverbatim
-  StmtResult ParseReturnStatement();
-
-  StmtResult ParsePragmaLoopHint(StmtVector &Stmts, ParsedStmtContext StmtCtx,
-                                 SourceLocation *TrailingElseLoc,
-                                 ParsedAttributes &Attrs);
-
-  void ParseMicrosoftIfExistsStatement(StmtVector &Stmts);
-
-  
//===--------------------------------------------------------------------===//
-  // C++ 6: Statements and Blocks
-
-  /// ParseCXXTryBlock - Parse a C++ try-block.
-  ///
-  /// \verbatim
-  ///       try-block:
-  ///         'try' compound-statement handler-seq
-  /// \endverbatim
-  ///
-  StmtResult ParseCXXTryBlock();
-
-  /// ParseCXXTryBlockCommon - Parse the common part of try-block and
-  /// function-try-block.
-  ///
-  /// \verbatim
-  ///       try-block:
-  ///         'try' compound-statement handler-seq
-  ///
-  ///       function-try-block:
-  ///         'try' ctor-initializer[opt] compound-statement handler-seq
-  ///
-  ///       handler-seq:
-  ///         handler handler-seq[opt]
-  ///
-  ///       [Borland] try-block:
-  ///         'try' compound-statement seh-except-block
-  ///         'try' compound-statement seh-finally-block
-  /// \endverbatim
-  ///
-  StmtResult ParseCXXTryBlockCommon(SourceLocation TryLoc, bool FnTry = false);
-
-  /// ParseCXXCatchBlock - Parse a C++ catch block, called handler in the
-  /// standard
-  ///
-  /// \verbatim
-  ///   handler:
-  ///     'catch' '(' exception-declaration ')' compound-statement
-  ///
-  ///   exception-declaration:
-  ///     attribute-specifier-seq[opt] type-specifier-seq declarator
-  ///     attribute-specifier-seq[opt] type-specifier-seq 
abstract-declarator[opt]
-  ///     '...'
-  /// \endverbatim
-  ///
-  StmtResult ParseCXXCatchBlock(bool FnCatch = false);
-
-  
//===--------------------------------------------------------------------===//
-  // MS: SEH Statements and Blocks
-
-  /// ParseSEHTryBlockCommon
-  ///
-  /// \verbatim
-  /// seh-try-block:
-  ///   '__try' compound-statement seh-handler
-  ///
-  /// seh-handler:
-  ///   seh-except-block
-  ///   seh-finally-block
-  /// \endverbatim
-  ///
-  StmtResult ParseSEHTryBlock();
-
-  /// ParseSEHExceptBlock - Handle __except
-  ///
-  /// \verbatim
-  /// seh-except-block:
-  ///   '__except' '(' seh-filter-expression ')' compound-statement
-  /// \endverbatim
-  ///
-  StmtResult ParseSEHExceptBlock(SourceLocation Loc);
-
-  /// ParseSEHFinallyBlock - Handle __finally
-  ///
-  /// \verbatim
-  /// seh-finally-block:
-  ///   '__finally' compound-statement
-  /// \endverbatim
-  ///
-  StmtResult ParseSEHFinallyBlock(SourceLocation Loc);
-
-  StmtResult ParseSEHLeaveStatement();
-
-  Decl *ParseFunctionStatementBody(Decl *Decl, ParseScope &BodyScope);
-
-  /// ParseFunctionTryBlock - Parse a C++ function-try-block.
-  ///
-  /// \verbatim
-  ///       function-try-block:
-  ///         'try' ctor-initializer[opt] compound-statement handler-seq
-  /// \endverbatim
-  ///
-  Decl *ParseFunctionTryBlock(Decl *Decl, ParseScope &BodyScope);
-
-  /// When in code-completion, skip parsing of the function/method body
-  /// unless the body contains the code-completion point.
-  ///
-  /// \returns true if the function body was skipped.
-  bool trySkippingFunctionBody();
-
-  /// isDeclarationStatement - Disambiguates between a declaration or an
-  /// expression statement, when parsing function bodies.
-  ///
-  /// \param DisambiguatingWithExpression - True to indicate that the purpose 
of
-  /// this check is to disambiguate between an expression and a declaration.
-  /// Returns true for declaration, false for expression.
-  bool isDeclarationStatement(bool DisambiguatingWithExpression = false) {
-    if (getLangOpts().CPlusPlus)
-      return isCXXDeclarationStatement(DisambiguatingWithExpression);
-    return isDeclarationSpecifier(ImplicitTypenameContext::No, true);
-  }
+    StmtResult ParseStatementOrDeclarationAfterAttributes(
+        StmtVector &Stmts, ParsedStmtContext StmtCtx,
+        SourceLocation *TrailingElseLoc, ParsedAttributes &DeclAttrs,
+        ParsedAttributes &DeclSpecAttrs);
+
+    /// Parse an expression statement.
+    StmtResult ParseExprStatement(ParsedStmtContext StmtCtx);
+
+    /// ParseLabeledStatement - We have an identifier and a ':' after it.
+    ///
+    /// \verbatim
+    ///       label:
+    ///         identifier ':'
+    /// [GNU]   identifier ':' attributes[opt]
+    ///
+    ///       labeled-statement:
+    ///         label statement
+    /// \endverbatim
+    ///
+    StmtResult ParseLabeledStatement(ParsedAttributes &Attrs,
+                                     ParsedStmtContext StmtCtx);
+
+    /// ParseCaseStatement
+    /// \verbatim
+    ///       labeled-statement:
+    ///         'case' constant-expression ':' statement
+    /// [GNU]   'case' constant-expression '...' constant-expression ':'
+    /// statement
+    /// \endverbatim
+    ///
+    StmtResult ParseCaseStatement(ParsedStmtContext StmtCtx,
+                                  bool MissingCase = false,
+                                  ExprResult Expr = ExprResult());
+
+    /// ParseDefaultStatement
+    /// \verbatim
+    ///       labeled-statement:
+    ///         'default' ':' statement
+    /// \endverbatim
+    /// Note that this does not parse the 'statement' at the end.
+    ///
+    StmtResult ParseDefaultStatement(ParsedStmtContext StmtCtx);
+
+    StmtResult ParseCompoundStatement(bool isStmtExpr = false);
+
+    /// ParseCompoundStatement - Parse a "{}" block.
+    ///
+    /// \verbatim
+    ///       compound-statement: [C99 6.8.2]
+    ///         { block-item-list[opt] }
+    /// [GNU]   { label-declarations block-item-list } [TODO]
+    ///
+    ///       block-item-list:
+    ///         block-item
+    ///         block-item-list block-item
+    ///
+    ///       block-item:
+    ///         declaration
+    /// [GNU]   '__extension__' declaration
+    ///         statement
+    ///
+    /// [GNU] label-declarations:
+    /// [GNU]   label-declaration
+    /// [GNU]   label-declarations label-declaration
+    ///
+    /// [GNU] label-declaration:
+    /// [GNU]   '__label__' identifier-list ';'
+    /// \endverbatim
+    ///
+    StmtResult ParseCompoundStatement(bool isStmtExpr, unsigned ScopeFlags);
+
+    /// Parse any pragmas at the start of the compound expression. We handle
+    /// these separately since some pragmas (FP_CONTRACT) must appear before 
any
+    /// C statement in the compound, but may be intermingled with other 
pragmas.
+    void ParseCompoundStatementLeadingPragmas();
+
+    void DiagnoseLabelAtEndOfCompoundStatement();
+
+    /// Consume any extra semi-colons resulting in null statements,
+    /// returning true if any tok::semi were consumed.
+    bool ConsumeNullStmt(StmtVector &Stmts);
+
+    /// ParseCompoundStatementBody - Parse a sequence of statements optionally
+    /// followed by a label and invoke the ActOnCompoundStmt action.  This
+    /// expects the '{' to be the current token, and consume the '}' at the end
+    /// of the block.  It does not manipulate the scope stack.
+    StmtResult ParseCompoundStatementBody(bool isStmtExpr = false);
+
+    /// ParseParenExprOrCondition:
+    /// \verbatim
+    /// [C  ]     '(' expression ')'
+    /// [C++]     '(' condition ')'
+    /// [C++1z]   '(' init-statement[opt] condition ')'
+    /// \endverbatim
+    ///
+    /// This function parses and performs error recovery on the specified
+    /// condition or expression (depending on whether we're in C++ or C mode).
+    /// This function goes out of its way to recover well.  It returns true if
+    /// there was a parser error (the right paren couldn't be found), which
+    /// indicates that the caller should try to recover harder.  It returns
+    /// false if the condition is successfully parsed.  Note that a successful
+    /// parse can still have semantic errors in the condition. Additionally, it
+    /// will assign the location of the outer-most '(' and ')', to LParenLoc 
and
+    /// RParenLoc, respectively.
+    bool ParseParenExprOrCondition(StmtResult *InitStmt,
+                                   Sema::ConditionResult &CondResult,
+                                   SourceLocation Loc, Sema::ConditionKind CK,
+                                   SourceLocation &LParenLoc,
+                                   SourceLocation &RParenLoc);
+
+    /// ParseIfStatement
+    /// \verbatim
+    ///       if-statement: [C99 6.8.4.1]
+    ///         'if' '(' expression ')' statement
+    ///         'if' '(' expression ')' statement 'else' statement
+    /// [C++]   'if' '(' condition ')' statement
+    /// [C++]   'if' '(' condition ')' statement 'else' statement
+    /// [C++23] 'if' '!' [opt] consteval compound-statement
+    /// [C++23] 'if' '!' [opt] consteval compound-statement 'else' statement
+    /// \endverbatim
+    ///
+    StmtResult ParseIfStatement(SourceLocation *TrailingElseLoc);
+
+    /// ParseSwitchStatement
+    /// \verbatim
+    ///       switch-statement:
+    ///         'switch' '(' expression ')' statement
+    /// [C++]   'switch' '(' condition ')' statement
+    /// \endverbatim
+    StmtResult ParseSwitchStatement(SourceLocation *TrailingElseLoc);
+
+    /// ParseWhileStatement
+    /// \verbatim
+    ///       while-statement: [C99 6.8.5.1]
+    ///         'while' '(' expression ')' statement
+    /// [C++]   'while' '(' condition ')' statement
+    /// \endverbatim
+    StmtResult ParseWhileStatement(SourceLocation *TrailingElseLoc);
+
+    /// ParseDoStatement
+    /// \verbatim
+    ///       do-statement: [C99 6.8.5.2]
+    ///         'do' statement 'while' '(' expression ')' ';'
+    /// \endverbatim
+    /// Note: this lets the caller parse the end ';'.
+    StmtResult ParseDoStatement();
+
+    /// ParseForStatement
+    /// \verbatim
+    ///       for-statement: [C99 6.8.5.3]
+    ///         'for' '(' expr[opt] ';' expr[opt] ';' expr[opt] ')' statement
+    ///         'for' '(' declaration expr[opt] ';' expr[opt] ')' statement
+    /// [C++]   'for' '(' for-init-statement condition[opt] ';' expression[opt]
+    /// ')' [C++]       statement [C++0x] 'for'
+    ///             'co_await'[opt]    [Coroutines]
+    ///             '(' for-range-declaration ':' for-range-initializer ')'
+    ///             statement
+    /// [OBJC2] 'for' '(' declaration 'in' expr ')' statement
+    /// [OBJC2] 'for' '(' expr 'in' expr ')' statement
+    ///
+    /// [C++] for-init-statement:
+    /// [C++]   expression-statement
+    /// [C++]   simple-declaration
+    /// [C++23] alias-declaration
+    ///
+    /// [C++0x] for-range-declaration:
+    /// [C++0x]   attribute-specifier-seq[opt] type-specifier-seq declarator
+    /// [C++0x] for-range-initializer:
+    /// [C++0x]   expression
+    /// [C++0x]   braced-init-list            [TODO]
+    /// \endverbatim
+    StmtResult ParseForStatement(SourceLocation *TrailingElseLoc);
+
+    /// ParseGotoStatement
+    /// \verbatim
+    ///       jump-statement:
+    ///         'goto' identifier ';'
+    /// [GNU]   'goto' '*' expression ';'
+    /// \endverbatim
+    ///
+    /// Note: this lets the caller parse the end ';'.
+    ///
+    StmtResult ParseGotoStatement();
+
+    /// ParseContinueStatement
+    /// \verbatim
+    ///       jump-statement:
+    ///         'continue' ';'
+    /// \endverbatim
+    ///
+    /// Note: this lets the caller parse the end ';'.
+    ///
+    StmtResult ParseContinueStatement();
+
+    /// ParseBreakStatement
+    /// \verbatim
+    ///       jump-statement:
+    ///         'break' ';'
+    /// \endverbatim
+    ///
+    /// Note: this lets the caller parse the end ';'.
+    ///
+    StmtResult ParseBreakStatement();
+
+    /// ParseReturnStatement
+    /// \verbatim
+    ///       jump-statement:
+    ///         'return' expression[opt] ';'
+    ///         'return' braced-init-list ';'
+    ///         'co_return' expression[opt] ';'
+    ///         'co_return' braced-init-list ';'
+    /// \endverbatim
+    StmtResult ParseReturnStatement();
+
+    StmtResult ParsePragmaLoopHint(StmtVector &Stmts, ParsedStmtContext 
StmtCtx,
+                                   SourceLocation *TrailingElseLoc,
+                                   ParsedAttributes &Attrs);
+
+    void ParseMicrosoftIfExistsStatement(StmtVector &Stmts);
+
+    
//===--------------------------------------------------------------------===//
+    // C++ 6: Statements and Blocks
+
+    /// ParseCXXTryBlock - Parse a C++ try-block.
+    ///
+    /// \verbatim
+    ///       try-block:
+    ///         'try' compound-statement handler-seq
+    /// \endverbatim
+    ///
+    StmtResult ParseCXXTryBlock();
+
+    /// ParseCXXTryBlockCommon - Parse the common part of try-block and
+    /// function-try-block.
+    ///
+    /// \verbatim
+    ///       try-block:
+    ///         'try' compound-statement handler-seq
+    ///
+    ///       function-try-block:
+    ///         'try' ctor-initializer[opt] compound-statement handler-seq
+    ///
+    ///       handler-seq:
+    ///         handler handler-seq[opt]
+    ///
+    ///       [Borland] try-block:
+    ///         'try' compound-statement seh-except-block
+    ///         'try' compound-statement seh-finally-block
+    /// \endverbatim
+    ///
+    StmtResult ParseCXXTryBlockCommon(SourceLocation TryLoc,
+                                      bool FnTry = false);
+
+    /// ParseCXXCatchBlock - Parse a C++ catch block, called handler in the
+    /// standard
+    ///
+    /// \verbatim
+    ///   handler:
+    ///     'catch' '(' exception-declaration ')' compound-statement
+    ///
+    ///   exception-declaration:
+    ///     attribute-specifier-seq[opt] type-specifier-seq declarator
+    ///     attribute-specifier-seq[opt] type-specifier-seq
+    ///     abstract-declarator[opt]
+    ///     '...'
+    /// \endverbatim
+    ///
+    StmtResult ParseCXXCatchBlock(bool FnCatch = false);
+
+    
//===--------------------------------------------------------------------===//
+    // MS: SEH Statements and Blocks
+
+    /// ParseSEHTryBlockCommon
+    ///
+    /// \verbatim
+    /// seh-try-block:
+    ///   '__try' compound-statement seh-handler
+    ///
+    /// seh-handler:
+    ///   seh-except-block
+    ///   seh-finally-block
+    /// \endverbatim
+    ///
+    StmtResult ParseSEHTryBlock();
+
+    /// ParseSEHExceptBlock - Handle __except
+    ///
+    /// \verbatim
+    /// seh-except-block:
+    ///   '__except' '(' seh-filter-expression ')' compound-statement
+    /// \endverbatim
+    ///
+    StmtResult ParseSEHExceptBlock(SourceLocation Loc);
+
+    /// ParseSEHFinallyBlock - Handle __finally
+    ///
+    /// \verbatim
+    /// seh-finally-block:
+    ///   '__finally' compound-statement
+    /// \endverbatim
+    ///
+    StmtResult ParseSEHFinallyBlock(SourceLocation Loc);
+
+    StmtResult ParseSEHLeaveStatement();
+
+    Decl *ParseFunctionStatementBody(Decl *Decl, ParseScope &BodyScope);
+
+    /// ParseFunctionTryBlock - Parse a C++ function-try-block.
+    ///
+    /// \verbatim
+    ///       function-try-block:
+    ///         'try' ctor-initializer[opt] compound-statement handler-seq
+    /// \endverbatim
+    ///
+    Decl *ParseFunctionTryBlock(Decl *Decl, ParseScope &BodyScope);
+
+    /// When in code-completion, skip parsing of the function/method body
+    /// unless the body contains the code-completion point.
+    ///
+    /// \returns true if the function body was skipped.
+    bool trySkippingFunctionBody();
+
+    /// isDeclarationStatement - Disambiguates between a declaration or an
+    /// expression statement, when parsing function bodies.
+    ///
+    /// \param DisambiguatingWithExpression - True to indicate that the purpose
+    /// of this check is to disambiguate between an expression and a
+    /// declaration. Returns true for declaration, false for expression.
+    bool isDeclarationStatement(bool DisambiguatingWithExpression = false) {
+      if (getLangOpts().CPlusPlus)
+        return isCXXDeclarationStatement(DisambiguatingWithExpression);
+      return isDeclarationSpecifier(ImplicitTypenameContext::No, true);
+    }
 
   /// isForInitDeclaration - Disambiguates between a declaration or an
   /// expression in the context of the C 'clause-1' or the C++
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 8217151a0259a..14f1691a1f392 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,11 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
   assert((CXX11Attrs.empty() || Res.isInvalid() || Res.isUsable()) &&
          "attributes on empty statement");
 
+  if (HasStdAttr && getLangOpts().C99 &&
+      (StmtCtx & ParsedStmtContext::SecondaryBlock) != ParsedStmtContext{} &&
+      isa_and_present<NullStmt>(Res.get()))
+    Diag(Res.get()->getBeginLoc(), diag::err_expected_stmt_before_semi);
+
   if (CXX11Attrs.empty() || Res.isInvalid())
     return Res;
 
@@ -1491,6 +1497,10 @@ StmtResult Parser::ParseIfStatement(SourceLocation 
*TrailingElseLoc) {
 
   SourceLocation InnerStatementTrailingElseLoc;
   StmtResult ThenStmt;
+  ParsedStmtContext StmtCtx = getLangOpts().C99
+                                  ? ParsedStmtContext::SecondaryBlock
+                                  : ParsedStmtContext::SubStmt;
+
   {
     bool ShouldEnter = ConstexprCondition && !*ConstexprCondition;
     Sema::ExpressionEvaluationContext Context =
@@ -1503,7 +1513,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 +1558,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 +1694,11 @@ StmtResult Parser::ParseSwitchStatement(SourceLocation 
*TrailingElseLoc) {
   if (C99orCXX)
     getCurScope()->decrementMSManglingNumber();
 
+  ParsedStmtContext StmtCtx = getLangOpts().C99
+                                  ? ParsedStmtContext::SecondaryBlock
+                                  : ParsedStmtContext::SubStmt;
   // Read the body statement.
-  StmtResult Body(ParseStatement(TrailingElseLoc));
+  StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));
 
   // Pop the scopes.
   InnerScope.Exit();
@@ -1754,9 +1767,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::SecondaryBlock
+                                  : ParsedStmtContext::SubStmt;
   // Read the body statement.
-  StmtResult Body(ParseStatement(TrailingElseLoc));
+  StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));
 
   if (Body.isUsable())
     MIChecker.Check();
@@ -1799,9 +1814,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::SecondaryBlock
+                                  : 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 +2238,11 @@ StmtResult Parser::ParseForStatement(SourceLocation 
*TrailingElseLoc) {
     getCurScope()->decrementMSManglingNumber();
 
   MisleadingIndentationChecker MIChecker(*this, MSK_for, ForLoc);
-
+  ParsedStmtContext StmtCtx = getLangOpts().C99
+                                  ? ParsedStmtContext::SecondaryBlock
+                                  : 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..58998b2c9476b 100644
--- a/clang/test/Parser/statements.c
+++ b/clang/test/Parser/statements.c
@@ -77,3 +77,30 @@ int test9(void) {
 
   return 4, // expected-error {{expected ';' after return statement}}
 }
+
+void attr_decl_in_selection_statement(int n) {
+  if (1)
+    [[]]; // expected-error {{expected a statement before ';'}}
+
+  if (1) {
+
+  } else
+    [[]]; // expected-error {{expected a statement before ';'}}
+
+
+  switch (n)
+    [[]]; // expected-error {{expected a statement before ';'}}
+}
+
+void attr_decl_in_iteration_statement(int n) {
+  int i;
+  for (i = 0; i < n; ++i)
+    [[]];     // expected-error {{expected a statement before ';'}}
+
+  while (i > 0)
+    [[]];     // expected-error {{expected a statement before ';'}}
+
+  do
+    [[]];     // expected-error {{expected a statement before ';'}}
+  while (i > 0);
+}
diff --git a/clang/test/Sema/c2x-fallthrough.c 
b/clang/test/Sema/c2x-fallthrough.c
index 092d5285f80ba..17ac22501a7cc 100644
--- a/clang/test/Sema/c2x-fallthrough.c
+++ b/clang/test/Sema/c2x-fallthrough.c
@@ -15,18 +15,19 @@ void f(int n) {
       return;
     }
   case 2:
+    // FIXME: Should we emit an error {{fallthrough annotation does not 
directly precede switch label}}?
     for (int n = 0; n != 10; ++n)
-      [[fallthrough]]; // expected-error {{does not directly precede switch 
label}}
+      [[fallthrough]]; // expected-error {{expected a statement before ';'}}
   case 3:
     while (1)
-      [[fallthrough]]; // expected-error {{does not directly precede switch 
label}}
+      [[fallthrough]]; // expected-error {{expected a statement before ';'}}
   case 4:
     while (0)
-      [[fallthrough]]; // expected-error {{does not directly precede switch 
label}}
+      [[fallthrough]]; // expected-error {{expected a statement before ';'}}
   case 5:
-    do [[fallthrough]]; while (1); // expected-error {{does not directly 
precede switch label}}
+    do [[fallthrough]]; while (1); // expected-error {{expected a statement 
before ';'}}
   case 6:
-    do [[fallthrough]]; while (0); // expected-error {{does not directly 
precede switch label}}
+    do [[fallthrough]]; while (0); // expected-error {{expected a statement 
before ';'}}
   case 7:
     switch (n) {
     case 0:
@@ -35,7 +36,7 @@ void f(int n) {
       [[fallthrough]];
     }
   case 8:
-    [[fallthrough]]; // expected-error {{does not directly precede switch 
label}}
+    [[fallthrough]]; // FIXME: The error {{does not directly precede switch 
label}} cannot reproduce in this translate unit, But it can be reproduced in a 
separate file.
     goto label;
   label:
   case 9:
diff --git a/clang/test/Sema/c2x-fallthrough2.c 
b/clang/test/Sema/c2x-fallthrough2.c
new file mode 100644
index 0000000000000..ab44eb93657ea
--- /dev/null
+++ b/clang/test/Sema/c2x-fallthrough2.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c2x -verify %s
+
+// This is the latest version of fallthrough that we support.
+_Static_assert(__has_c_attribute(fallthrough) == 201910L);
+
+void f(int n) {
+  switch (n) {
+  case 8:
+    [[fallthrough]]; // expected-error {{does not directly precede switch 
label}}
+    goto label;
+  label:
+  case 9:
+    n += 1;
+  case 10: // no warning, -Wimplicit-fallthrough is not enabled in this test, 
and does not need to
+           // be enabled for these diagnostics to be produced.
+    break;
+  }
+}
diff --git a/clang/test/Sema/fallthrough.cpp b/clang/test/Sema/fallthrough.cpp
new file mode 100644
index 0000000000000..8c8078ae211e0
--- /dev/null
+++ b/clang/test/Sema/fallthrough.cpp
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
+
+void f(int n) {
+  switch (n) {
+  case 0:
+    n += 1;
+    [[fallthrough]]; // ok
+  case 1:
+    if (n) {
+      [[fallthrough]]; // ok
+    } else {
+      return;
+    }
+  case 2:
+    for (int n = 0; n != 10; ++n)
+      [[fallthrough]]; // expected-error {{does not directly precede switch 
label}}
+  case 3:
+    while (1)
+      [[fallthrough]]; // expected-error {{does not directly precede switch 
label}}
+  case 4:
+    while (0)
+      [[fallthrough]]; // expected-error {{does not directly precede switch 
label}}
+  case 5:
+    do [[fallthrough]]; while (1); // expected-error {{does not directly 
precede switch label}}
+  case 6:
+    do [[fallthrough]]; while (0); // expected-error {{does not directly 
precede switch label}}
+  case 7:
+    switch (n) {
+    case 0:
+      // FIXME: This should be an error, even though the next thing we do is to
+      // fall through in an outer switch statement.
+      [[fallthrough]];
+    }
+  case 8:
+    [[fallthrough]]; // expected-error {{does not directly precede switch 
label}}
+    goto label;
+  label:
+  case 9:
+    n += 1;
+  case 10: // no warning, -Wimplicit-fallthrough is not enabled in this test, 
and does not need to
+           // be enabled for these diagnostics to be produced.
+    break;
+  }
+}
+
+[[fallthrough]] typedef int n1; // expected-error {{'fallthrough' attribute 
cannot be applied to a declaration}}
+typedef int [[fallthrough]] n2; // expected-error {{'fallthrough' attribute 
cannot be applied to types}}
+typedef int n3 [[fallthrough]]; // expected-error {{'fallthrough' attribute 
cannot be applied to a declaration}}
+
+enum [[fallthrough]] E { // expected-error {{'fallthrough' attribute cannot be 
applied to a declaration}}
+  One
+};
+struct [[fallthrough]] S { // expected-error {{'fallthrough' attribute cannot 
be applied to a declaration}}
+  int i;
+};
+
+[[fallthrough]] // expected-error {{'fallthrough' attribute cannot be applied 
to a declaration}}
+void g(void) {
+  [[fallthrough]] int n; // expected-error {{'fallthrough' attribute cannot be 
applied to a declaration}}
+  [[fallthrough]] ++n; // expected-error {{'fallthrough' attribute only 
applies to empty statements}}
+
+  switch (n) {
+    // FIXME: This should be an error.
+    [[fallthrough]];
+    return;
+
+  case 0:
+    [[fallthrough, fallthrough]]; // ok
+  case 1:
+    [[fallthrough(0)]]; // expected-error {{argument list}}
+  case 2:
+    break;
+  }
+}
+

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to