Skip to content

Commit 43e2dc6

Browse files
committed
[C23][Parser] Diagnostic for attribute declaration where statement is required
Signed-off-by: yronglin <[email protected]>
1 parent f4b938b commit 43e2dc6

File tree

6 files changed

+82
-16
lines changed

6 files changed

+82
-16
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,10 @@ Bug Fixes in This Version
747747
- Fixed an infinite recursion when checking constexpr destructors. (#GH141789)
748748
- Fixed a crash when a malformed using declaration appears in a ``constexpr`` function. (#GH144264)
749749
- Fixed a bug when use unicode character name in macro concatenation. (#GH145240)
750+
- In C23, something like [[/*possible attributes*/]]; is an attribute declaration, not a statement. So it is not
751+
allowed by the syntax in places where a statement is required, specifically as the secondary block of a
752+
selection or iteration statement. This differs from C++, since C++ allows declaration statements.
753+
Clang now warning this patterns. (#GH141659)
750754

751755
Bug Fixes to Compiler Builtins
752756
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ def err_expected_while : Error<"expected 'while' in do/while loop">;
276276

277277
def err_expected_semi_after_stmt : Error<"expected ';' after %0 statement">;
278278
def err_expected_semi_after_expr : Error<"expected ';' after expression">;
279+
def warn_expected_stmt_before_semi_in_secondary_block : Warning<
280+
"expected a statement before ';' but got an attribute declaration, "
281+
"it is not allowed by the syntax in places where a statement is required">,
282+
InGroup<CXXCompat>;
279283
def err_extraneous_token_before_semi : Error<"extraneous '%0' before ';'">;
280284

281285
def err_expected_semi_after_method_proto : Error<

clang/include/clang/Parse/Parser.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7168,13 +7168,16 @@ class Parser : public CodeCompletionHandler {
71687168
AllowStandaloneOpenMPDirectives = 0x2,
71697169
/// This context is at the top level of a GNU statement expression.
71707170
InStmtExpr = 0x4,
7171+
/// This context is the C99 secondary-block in selection or iteration
7172+
/// statement.
7173+
SecondaryBlockInC = 0x8,
71717174

71727175
/// The context of a regular substatement.
71737176
SubStmt = 0,
71747177
/// The context of a compound-statement.
71757178
Compound = AllowDeclarationsInC | AllowStandaloneOpenMPDirectives,
71767179

7177-
LLVM_MARK_AS_BITMASK_ENUM(InStmtExpr)
7180+
LLVM_MARK_AS_BITMASK_ENUM(SecondaryBlockInC)
71787181
};
71797182

71807183
/// Act on an expression statement that might be the last statement in a

clang/lib/Parse/ParseStmt.cpp

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
6363
// at the start of the statement. Thus, we're not using MaybeParseAttributes
6464
// here because we don't want to allow arbitrary orderings.
6565
ParsedAttributes CXX11Attrs(AttrFactory);
66-
MaybeParseCXX11Attributes(CXX11Attrs, /*MightBeObjCMessageSend*/ true);
66+
bool HasStdAttr =
67+
MaybeParseCXX11Attributes(CXX11Attrs, /*MightBeObjCMessageSend*/ true);
6768
ParsedAttributes GNUOrMSAttrs(AttrFactory);
6869
if (getLangOpts().OpenCL)
6970
MaybeParseGNUAttributes(GNUOrMSAttrs);
@@ -80,6 +81,12 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
8081
assert((CXX11Attrs.empty() || Res.isInvalid() || Res.isUsable()) &&
8182
"attributes on empty statement");
8283

84+
if (HasStdAttr && getLangOpts().C23 &&
85+
(StmtCtx & ParsedStmtContext::SecondaryBlockInC) != ParsedStmtContext{} &&
86+
isa_and_present<NullStmt>(Res.get()))
87+
Diag(Res.get()->getBeginLoc(),
88+
diag::warn_expected_stmt_before_semi_in_secondary_block);
89+
8390
if (CXX11Attrs.empty() || Res.isInvalid())
8491
return Res;
8592

@@ -1491,6 +1498,10 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
14911498

14921499
SourceLocation InnerStatementTrailingElseLoc;
14931500
StmtResult ThenStmt;
1501+
ParsedStmtContext StmtCtx = getLangOpts().C99
1502+
? ParsedStmtContext::SecondaryBlockInC
1503+
: ParsedStmtContext::SubStmt;
1504+
14941505
{
14951506
bool ShouldEnter = ConstexprCondition && !*ConstexprCondition;
14961507
Sema::ExpressionEvaluationContext Context =
@@ -1503,7 +1514,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
15031514
EnterExpressionEvaluationContext PotentiallyDiscarded(
15041515
Actions, Context, nullptr,
15051516
Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter);
1506-
ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc);
1517+
ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc, StmtCtx);
15071518
}
15081519

15091520
if (Tok.isNot(tok::kw_else))
@@ -1548,7 +1559,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
15481559
EnterExpressionEvaluationContext PotentiallyDiscarded(
15491560
Actions, Context, nullptr,
15501561
Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter);
1551-
ElseStmt = ParseStatement();
1562+
ElseStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx);
15521563

15531564
if (ElseStmt.isUsable())
15541565
MIChecker.Check();
@@ -1684,8 +1695,11 @@ StmtResult Parser::ParseSwitchStatement(SourceLocation *TrailingElseLoc) {
16841695
if (C99orCXX)
16851696
getCurScope()->decrementMSManglingNumber();
16861697

1698+
ParsedStmtContext StmtCtx = getLangOpts().C99
1699+
? ParsedStmtContext::SecondaryBlockInC
1700+
: ParsedStmtContext::SubStmt;
16871701
// Read the body statement.
1688-
StmtResult Body(ParseStatement(TrailingElseLoc));
1702+
StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));
16891703

16901704
// Pop the scopes.
16911705
InnerScope.Exit();
@@ -1754,9 +1768,11 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) {
17541768
ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace));
17551769

17561770
MisleadingIndentationChecker MIChecker(*this, MSK_while, WhileLoc);
1757-
1771+
ParsedStmtContext StmtCtx = getLangOpts().C99
1772+
? ParsedStmtContext::SecondaryBlockInC
1773+
: ParsedStmtContext::SubStmt;
17581774
// Read the body statement.
1759-
StmtResult Body(ParseStatement(TrailingElseLoc));
1775+
StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));
17601776

17611777
if (Body.isUsable())
17621778
MIChecker.Check();
@@ -1799,9 +1815,11 @@ StmtResult Parser::ParseDoStatement() {
17991815
//
18001816
bool C99orCXX = getLangOpts().C99 || getLangOpts().CPlusPlus;
18011817
ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace));
1802-
1818+
ParsedStmtContext StmtCtx = getLangOpts().C99
1819+
? ParsedStmtContext::SecondaryBlockInC
1820+
: ParsedStmtContext::SubStmt;
18031821
// Read the body statement.
1804-
StmtResult Body(ParseStatement());
1822+
StmtResult Body(ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx));
18051823

18061824
// Pop the body scope if needed.
18071825
InnerScope.Exit();
@@ -2221,9 +2239,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
22212239
getCurScope()->decrementMSManglingNumber();
22222240

22232241
MisleadingIndentationChecker MIChecker(*this, MSK_for, ForLoc);
2224-
2242+
ParsedStmtContext StmtCtx = getLangOpts().C99
2243+
? ParsedStmtContext::SecondaryBlockInC
2244+
: ParsedStmtContext::SubStmt;
22252245
// Read the body statement.
2226-
StmtResult Body(ParseStatement(TrailingElseLoc));
2246+
StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));
22272247

22282248
if (Body.isUsable())
22292249
MIChecker.Check();

clang/test/Parser/statements.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-unreachable-code
2+
// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s -Wno-unreachable-code
23

34
void test1(void) {
45
{ ; { ;;}} ;;
@@ -77,3 +78,32 @@ int test9(void) {
7778

7879
return 4, // expected-error {{expected ';' after return statement}}
7980
}
81+
82+
#if __STDC_VERSION__ >= 202311L
83+
void attr_decl_in_selection_statement(int n) {
84+
if (1)
85+
[[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
86+
87+
if (1) {
88+
89+
} else
90+
[[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
91+
92+
93+
switch (n)
94+
[[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
95+
}
96+
97+
void attr_decl_in_iteration_statement(int n) {
98+
int i;
99+
for (i = 0; i < n; ++i)
100+
[[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
101+
102+
while (i > 0)
103+
[[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
104+
105+
do
106+
[[]]; // expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
107+
while (i > 0);
108+
}
109+
#endif // __STDC_VERSION__ >= 202311L

clang/test/Sema/c2x-fallthrough.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,22 @@ void f(int n) {
1616
}
1717
case 2:
1818
for (int n = 0; n != 10; ++n)
19-
[[fallthrough]]; // expected-error {{does not directly precede switch label}}
19+
[[fallthrough]]; // expected-error {{does not directly precede switch label}} \
20+
// expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
2021
case 3:
2122
while (1)
22-
[[fallthrough]]; // expected-error {{does not directly precede switch label}}
23+
[[fallthrough]]; // expected-error {{does not directly precede switch label}} \
24+
// expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
2325
case 4:
2426
while (0)
25-
[[fallthrough]]; // expected-error {{does not directly precede switch label}}
27+
[[fallthrough]]; // expected-error {{does not directly precede switch label}} \
28+
// expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
2629
case 5:
27-
do [[fallthrough]]; while (1); // expected-error {{does not directly precede switch label}}
30+
do [[fallthrough]]; while (1); // expected-error {{does not directly precede switch label}} \
31+
// expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
2832
case 6:
29-
do [[fallthrough]]; while (0); // expected-error {{does not directly precede switch label}}
33+
do [[fallthrough]]; while (0); // expected-error {{does not directly precede switch label}} \
34+
// expected-warning {{expected a statement before ';' but got an attribute declaration, it is not allowed by the syntax in places where a statement is required}}
3035
case 7:
3136
switch (n) {
3237
case 0:

0 commit comments

Comments
 (0)