-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[C23][Parser] Diagnostic for attribute declaration where statement is required #146224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang Author: None (yronglin) ChangesFixes: #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: int main(void) {
if (1)
[[]];
} Patch is 37.12 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/146224.diff 8 Files Affected:
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] ';' e...
[truncated]
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
The code format issue looks very strange, should we add an |
… required Signed-off-by: yronglin <[email protected]>
d46ebd6
to
43e2dc6
Compare
Signed-off-by: yronglin <[email protected]>
Thanks for your review! |
friendly ping~ |
Co-authored-by: Mariya Podchishchaeva <[email protected]>
Signed-off-by: Wang, Yihan <[email protected]>
Signed-off-by: Wang, Yihan <[email protected]>
clang/include/clang/Parse/Parser.h
Outdated
@@ -7168,13 +7168,16 @@ class Parser : public CodeCompletionHandler { | |||
AllowStandaloneOpenMPDirectives = 0x2, | |||
/// This context is at the top level of a GNU statement expression. | |||
InStmtExpr = 0x4, | |||
/// This context is the C99 secondary-block in selection or iteration | |||
/// statement. | |||
SecondaryBlockInC = 0x8, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does AllowDeclarationsInC
not suffice? I would expect that bit to not be set in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. We can just reuse AllowDeclarationsInC
. I have removed SecondaryBlockInC
.
Signed-off-by: Wang, Yihan <[email protected]>
Signed-off-by: Wang, Yihan <[email protected]>
Signed-off-by: Wang, Yihan <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Fixes: #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: