[clang] [clang][Parser] Warn when the body of expansion statement is not a compound statement (PR #209229)
Yanzuo Liu via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 14 07:01:51 PDT 2026
https://github.com/zwuis updated https://github.com/llvm/llvm-project/pull/209229
>From 6858ff8a03b5f20eb262b2a404974079a9b33c4f Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Mon, 13 Jul 2026 23:57:51 +0800
Subject: [PATCH 1/3] Warn when the body of expansion statement is not a
compound statement
---
.../clang/Basic/DiagnosticParseKinds.td | 3 +
clang/lib/Parse/ParseStmt.cpp | 9 +-
.../Parser/cxx2c-expansion-statements.cpp | 115 +++++++++++++-----
3 files changed, 96 insertions(+), 31 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 55b26deed0750..389900040399a 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -457,6 +457,9 @@ def err_expansion_stmt_requires_cxx2c : Error<
"expansion statements are only supported in C++2c">;
def err_for_template : Error<
"'for template' is invalid; use 'template for' instead">;
+def ext_expansion_stmt_requires_braced_body : ExtWarn<
+ "ISO C++ requires a compound statement to be the body of expansion statement">,
+ InGroup<DiagGroup<"expansion-stmt-braced-body">>, DefaultIgnore;
def err_expected_case_before_expression: Error<
"expected 'case' keyword before expression">;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index bdaea72cf52a1..bbde87794b652 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2329,6 +2329,10 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// the other parts.
getCurScope()->EnterLoopBody(PrecedingLabel);
+ bool BodyIsCompoundStmt = Tok.is(tok::l_brace);
+ // attribute-specifier without attribute (`[[]]`) isn't in AST.
+ SourceLocation BodyBeginLoc = Tok.getLocation();
+
// C99 6.8.5p5 - In C99, the body of the for statement is a scope, even if
// there is no compound stmt. C90 does not have this clause. We only do this
// if the body isn't a compound statement to avoid push/pop in common cases.
@@ -2341,7 +2345,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// for-init-statement/condition and a new scope for substatement in C++.
//
ParseScope InnerScope(this, Scope::DeclScope, C99orCXXorObjC,
- Tok.is(tok::l_brace));
+ BodyIsCompoundStmt);
// The body of the for loop has the same local mangling number as the
// for-init-statement.
@@ -2379,6 +2383,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
return StmtError();
}
+ if (!BodyIsCompoundStmt)
+ Diag(BodyBeginLoc, diag::ext_expansion_stmt_requires_braced_body);
+
return Actions.FinishCXXExpansionStmt(ForRangeStmt.get(), Body.get());
}
diff --git a/clang/test/Parser/cxx2c-expansion-statements.cpp b/clang/test/Parser/cxx2c-expansion-statements.cpp
index 736f9fead383c..2ca94270cd60a 100644
--- a/clang/test/Parser/cxx2c-expansion-statements.cpp
+++ b/clang/test/Parser/cxx2c-expansion-statements.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify=expected,brace -Wexpansion-stmt-braced-body
namespace std {
template <typename T>
struct initializer_list {
@@ -14,31 +15,82 @@ void bad() {
template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (int x;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
- template for (x : {1}); // expected-error {{expansion statement requires type for expansion variable}}
+ template for (x : {1});
+ // expected-error at -1 {{expansion statement requires type for expansion variable}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
template for (: {1}); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (auto y : {1})]; // expected-error {{expected expression}}
- template for (auto y : {1}; // expected-error {{expected ')'}} expected-note {{to match this '('}}
- template for (extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
- template for (register auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'register'}} expected-error {{ISO C++17 does not allow 'register' storage class specifier}}
- template for (__private_extern__ auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
- template for (extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
- template for (static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
- template for (thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
- template for (static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
- template for (__thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
- template for (static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
- template for (constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
- template for (consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}}
- template for (int x; extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
- template for (int x; extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
- template for (int x; static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
- template for (int x; thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
- template for (int x; static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
- template for (int x; __thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
- template for (int x; static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
- template for (int x; constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
- template for (int x; consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}}
- template for (auto y : {abc, -+, }); // expected-error {{use of undeclared identifier 'abc'}} expected-error {{expected expression}}
+ template for (auto y : {1};
+ // expected-error at -1 {{expected ')'}}
+ // expected-note at -2 {{to match this '('}}
+ // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (extern auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (register auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'register'}}
+ // expected-error at -2 {{ISO C++17 does not allow 'register' storage class specifier}}
+ // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (__private_extern__ auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (extern static auto y : {1, 2});
+ // expected-error at -1 {{cannot combine with previous 'extern' declaration specifier}}
+ // expected-error at -2 {{expansion variable 'y' may not be declared 'extern'}}
+ // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (static auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (thread_local auto y : {1, 2});
+ // expected-error at -1 {{'thread_local' variables must have global storage}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (static thread_local auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'thread_local'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (__thread auto y : {1, 2});
+ // expected-error at -1 {{'__thread' variables must have global storage}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (static __thread auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (constinit auto y : {1, 2});
+ // expected-error at -1 {{local variable cannot be declared 'constinit'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (consteval auto y : {1, 2});
+ // expected-error at -1 {{consteval can only be used in function declarations}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; extern auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; extern static auto y : {1, 2});
+ // expected-error at -1 {{cannot combine with previous 'extern' declaration specifier}}
+ // expected-error at -2 {{expansion variable 'y' may not be declared 'extern'}}
+ // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; static auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; thread_local auto y : {1, 2});
+ // expected-error at -1 {{'thread_local' variables must have global storage}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; static thread_local auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'thread_local'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; __thread auto y : {1, 2});
+ // expected-error at -1 {{'__thread' variables must have global storage}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; static __thread auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; constinit auto y : {1, 2});
+ // expected-error at -1 {{local variable cannot be declared 'constinit'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; consteval auto y : {1, 2});
+ // expected-error at -1 {{consteval can only be used in function declarations}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (auto y : {abc, -+, });
+ // expected-error at -1 {{use of undeclared identifier 'abc'}}
+ // expected-error at -2 {{expected expression}}
+ // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
template for (3 : "error") // expected-error {{expansion statement declaration must declare a variable}} \
expected-error {{expansion statement must use the syntax of a range-based for loop}}
;
@@ -46,18 +98,21 @@ void bad() {
; // Semicolon for synchronisation; otherwise, the parser skips over next statement...
template do {} while (true); // expected-error {{expected '<' after 'template'}}
for template (int x : {}) {} // expected-error {{'for template' is invalid; use 'template for' instead}}
+ template for (int x : {1})
+ [ // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ []] {}
}
void good() {
- template for (auto y : {});
- template for (auto y : {1, 2});
- template for (int x; auto y : {1, 2});
- template for (int x; int y : {1, 2});
- template for (int x; constexpr auto y : {1, 2});
- template for (int x; constexpr int y : {1, 2});
+ template for (auto y : {}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; int y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; constexpr auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; constexpr int y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
template for (constexpr int a : {1, 2}) {
template for (constexpr int b : {1, 2}) {
- template for (constexpr int c : {1, 2});
+ template for (constexpr int c : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
}
}
}
>From fac01ce301392ae1b76057cf9935a6b00519a197 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Tue, 14 Jul 2026 16:34:49 +0800
Subject: [PATCH 2/3] Address review comments
---
.../clang/Basic/DiagnosticParseKinds.td | 6 +-
clang/lib/Parse/ParseStmt.cpp | 8 +-
...sion-statements-non-compound-stmt-body.cpp | 31 +++++
.../Parser/cxx2c-expansion-statements.cpp | 115 +++++-------------
4 files changed, 68 insertions(+), 92 deletions(-)
create mode 100644 clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 389900040399a..88d08b3c4a03b 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -457,9 +457,9 @@ def err_expansion_stmt_requires_cxx2c : Error<
"expansion statements are only supported in C++2c">;
def err_for_template : Error<
"'for template' is invalid; use 'template for' instead">;
-def ext_expansion_stmt_requires_braced_body : ExtWarn<
- "ISO C++ requires a compound statement to be the body of expansion statement">,
- InGroup<DiagGroup<"expansion-stmt-braced-body">>, DefaultIgnore;
+def ext_expansion_stmt_missing_braces : Extension<
+ "ISO C++ requires a compound statement to be the body of an expansion statement">,
+ InGroup<DiagGroup<"expansion-stmt-missing-braces">>;
def err_expected_case_before_expression: Error<
"expected 'case' keyword before expression">;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index bbde87794b652..bebcba038a20e 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2329,8 +2329,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// the other parts.
getCurScope()->EnterLoopBody(PrecedingLabel);
- bool BodyIsCompoundStmt = Tok.is(tok::l_brace);
// attribute-specifier without attribute (`[[]]`) isn't in AST.
+ bool BodyStartsWithSquare = Tok.is(tok::l_square);
SourceLocation BodyBeginLoc = Tok.getLocation();
// C99 6.8.5p5 - In C99, the body of the for statement is a scope, even if
@@ -2345,7 +2345,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// for-init-statement/condition and a new scope for substatement in C++.
//
ParseScope InnerScope(this, Scope::DeclScope, C99orCXXorObjC,
- BodyIsCompoundStmt);
+ Tok.is(tok::l_brace));
// The body of the for loop has the same local mangling number as the
// for-init-statement.
@@ -2383,8 +2383,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
return StmtError();
}
- if (!BodyIsCompoundStmt)
- Diag(BodyBeginLoc, diag::ext_expansion_stmt_requires_braced_body);
+ if (!isa<CompoundStmt>(Body.get()) || BodyStartsWithSquare)
+ Diag(BodyBeginLoc, diag::ext_expansion_stmt_missing_braces);
return Actions.FinishCXXExpansionStmt(ForRangeStmt.get(), Body.get());
}
diff --git a/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp b/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
new file mode 100644
index 0000000000000..fb434f62da902
--- /dev/null
+++ b/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -Wexpansion-stmt-missing-braces -verify
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -Wpedantic -verify
+
+void f() {
+ template for (int x : {1})
+ template for (int y : {1}) // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ; // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ template for (int x : {1})
+ if (x) // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ;
+ template for (int x : {1})
+ switch (x) // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ;
+ template for (int x : {1})
+ for (;;) // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ;
+ template for (int x : {1})
+ while (x) // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ;
+ template for (int x : {1})
+ do // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ;
+ while (x);
+ template for (int x : {1})
+ return; // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ template for (int x : {1})
+ [ // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ []] {}
+ template for (int x : {1})
+ foo: {} // expected-error {{labels are not allowed in expansion statements}}
+}
diff --git a/clang/test/Parser/cxx2c-expansion-statements.cpp b/clang/test/Parser/cxx2c-expansion-statements.cpp
index 2ca94270cd60a..736f9fead383c 100644
--- a/clang/test/Parser/cxx2c-expansion-statements.cpp
+++ b/clang/test/Parser/cxx2c-expansion-statements.cpp
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
-// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify=expected,brace -Wexpansion-stmt-braced-body
namespace std {
template <typename T>
struct initializer_list {
@@ -15,82 +14,31 @@ void bad() {
template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (int x;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
- template for (x : {1});
- // expected-error at -1 {{expansion statement requires type for expansion variable}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (x : {1}); // expected-error {{expansion statement requires type for expansion variable}}
template for (: {1}); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (auto y : {1})]; // expected-error {{expected expression}}
- template for (auto y : {1};
- // expected-error at -1 {{expected ')'}}
- // expected-note at -2 {{to match this '('}}
- // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (extern auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (register auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'register'}}
- // expected-error at -2 {{ISO C++17 does not allow 'register' storage class specifier}}
- // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (__private_extern__ auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (extern static auto y : {1, 2});
- // expected-error at -1 {{cannot combine with previous 'extern' declaration specifier}}
- // expected-error at -2 {{expansion variable 'y' may not be declared 'extern'}}
- // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (static auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (thread_local auto y : {1, 2});
- // expected-error at -1 {{'thread_local' variables must have global storage}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (static thread_local auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'thread_local'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (__thread auto y : {1, 2});
- // expected-error at -1 {{'__thread' variables must have global storage}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (static __thread auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (constinit auto y : {1, 2});
- // expected-error at -1 {{local variable cannot be declared 'constinit'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (consteval auto y : {1, 2});
- // expected-error at -1 {{consteval can only be used in function declarations}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; extern auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; extern static auto y : {1, 2});
- // expected-error at -1 {{cannot combine with previous 'extern' declaration specifier}}
- // expected-error at -2 {{expansion variable 'y' may not be declared 'extern'}}
- // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; static auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; thread_local auto y : {1, 2});
- // expected-error at -1 {{'thread_local' variables must have global storage}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; static thread_local auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'thread_local'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; __thread auto y : {1, 2});
- // expected-error at -1 {{'__thread' variables must have global storage}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; static __thread auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; constinit auto y : {1, 2});
- // expected-error at -1 {{local variable cannot be declared 'constinit'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; consteval auto y : {1, 2});
- // expected-error at -1 {{consteval can only be used in function declarations}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (auto y : {abc, -+, });
- // expected-error at -1 {{use of undeclared identifier 'abc'}}
- // expected-error at -2 {{expected expression}}
- // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (auto y : {1}; // expected-error {{expected ')'}} expected-note {{to match this '('}}
+ template for (extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (register auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'register'}} expected-error {{ISO C++17 does not allow 'register' storage class specifier}}
+ template for (__private_extern__ auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
+ template for (static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
+ template for (__thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
+ template for (static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
+ template for (consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}}
+ template for (int x; extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (int x; extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (int x; static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (int x; thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
+ template for (int x; static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
+ template for (int x; __thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
+ template for (int x; static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (int x; constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
+ template for (int x; consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}}
+ template for (auto y : {abc, -+, }); // expected-error {{use of undeclared identifier 'abc'}} expected-error {{expected expression}}
template for (3 : "error") // expected-error {{expansion statement declaration must declare a variable}} \
expected-error {{expansion statement must use the syntax of a range-based for loop}}
;
@@ -98,21 +46,18 @@ void bad() {
; // Semicolon for synchronisation; otherwise, the parser skips over next statement...
template do {} while (true); // expected-error {{expected '<' after 'template'}}
for template (int x : {}) {} // expected-error {{'for template' is invalid; use 'template for' instead}}
- template for (int x : {1})
- [ // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- []] {}
}
void good() {
- template for (auto y : {}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; int y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; constexpr auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; constexpr int y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (auto y : {});
+ template for (auto y : {1, 2});
+ template for (int x; auto y : {1, 2});
+ template for (int x; int y : {1, 2});
+ template for (int x; constexpr auto y : {1, 2});
+ template for (int x; constexpr int y : {1, 2});
template for (constexpr int a : {1, 2}) {
template for (constexpr int b : {1, 2}) {
- template for (constexpr int c : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (constexpr int c : {1, 2});
}
}
}
>From 6e840d8d898306d4daf30f5bfc9c0264c599f2b9 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Tue, 14 Jul 2026 22:00:15 +0800
Subject: [PATCH 3/3] Move comment to where the variable is used
---
clang/lib/Parse/ParseStmt.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index bebcba038a20e..b72f28e4aac6d 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2329,7 +2329,6 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// the other parts.
getCurScope()->EnterLoopBody(PrecedingLabel);
- // attribute-specifier without attribute (`[[]]`) isn't in AST.
bool BodyStartsWithSquare = Tok.is(tok::l_square);
SourceLocation BodyBeginLoc = Tok.getLocation();
@@ -2383,6 +2382,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
return StmtError();
}
+ // attribute-specifier without attribute (`[[]]`) isn't in AST.
if (!isa<CompoundStmt>(Body.get()) || BodyStartsWithSquare)
Diag(BodyBeginLoc, diag::ext_expansion_stmt_missing_braces);
More information about the cfe-commits
mailing list