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