[clang] [C][Parser] Diagnostic for attribute declaration where statement is required (PR #146224)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jun 28 09:01:58 PDT 2025
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 <yronglin777 at 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 <yronglin777 at 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;
+ }
+}
+
More information about the cfe-commits
mailing list