[llvm-branch-commits] [clang] release/23.x: [clang][Parser] Warn when the body of expansion statement is not a compound statement (#209229) (PR #210908)

Douglas Yung via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Jul 22 06:59:03 PDT 2026


https://github.com/dyung updated https://github.com/llvm/llvm-project/pull/210908

>From 2f8eaf6ef04cb81fc98c30c1a8dd7fa1a0f4c43e Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Tue, 21 Jul 2026 16:09:56 +0800
Subject: [PATCH] [clang][Parser] Warn when the body of expansion statement is
 not a compound statement (#209229)

<https://eel.is/c++draft/stmt.expand#nt:expansion-statement>:

_expansion-statement_:
template for ( _init-statement<sub>opt</sub>_ _for-range-declaration_ :
_expansion-initializer_ ) _compound-statement_

(cherry picked from commit d13b8625eef0941f48b5af74e9fab953b6430eb2)
---
 clang/include/clang/Basic/DiagnosticGroups.td |  1 +
 .../clang/Basic/DiagnosticParseKinds.td       |  6 +++
 clang/lib/Parse/ParseStmt.cpp                 | 11 +++++
 ...sion-statements-non-compound-stmt-body.cpp | 47 +++++++++++++++++++
 4 files changed, 65 insertions(+)
 create mode 100644 clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 79583534b9bbd..b7072634cccf3 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -350,6 +350,7 @@ def ExtraTokens : DiagGroup<"extra-tokens">;
 def CXX98CompatExtraSemi : DiagGroup<"c++98-compat-extra-semi">;
 def CXX11ExtraSemi : DiagGroup<"c++11-extra-semi">;
 def EmptyInitStatement : DiagGroup<"empty-init-stmt">;
+def ExpansionStmtBody : DiagGroup<"expansion-stmt-missing-braces">;
 def ExportUnnamed : DiagGroup<"export-unnamed">;
 def ExtraSemiStmt : DiagGroup<"extra-semi-stmt", [EmptyInitStatement]>;
 def ExtraSemi : DiagGroup<"extra-semi", [CXX98CompatExtraSemi,
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 55b26deed0750..7671f003c8b80 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -457,6 +457,12 @@ 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_body_not_compound_stmt : Extension<
+  "ISO C++ requires the body of an expansion statement to be a compound statement">,
+  InGroup<ExpansionStmtBody>;
+def ext_expansion_stmt_body_attr : Extension<
+  "ISO C++ forbids attributes before the compound statement of an expansion statement">,
+  InGroup<ExpansionStmtBody>;
 
 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..44b51a985f2ff 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2329,6 +2329,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
   // the other parts.
   getCurScope()->EnterLoopBody(PrecedingLabel);
 
+  bool BodyStartsWithAttr = Tok.isOneOf(tok::l_square, tok::kw___attribute);
+  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.
@@ -2379,6 +2382,14 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
       return StmtError();
     }
 
+    // attribute-specifier without attribute (`[[]]`) isn't in AST.
+    // `__declspec()` is only applied to declarations, so we can ignore it.
+    if (!isa<CompoundStmt>(Body.get()) || BodyStartsWithAttr)
+      Diag(BodyBeginLoc,
+           isa<CompoundStmt>(Body.get()->stripLabelLikeStatements())
+               ? diag::ext_expansion_stmt_body_attr
+               : diag::ext_expansion_stmt_body_not_compound_stmt);
+
     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..5aae6a5305db8
--- /dev/null
+++ b/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
@@ -0,0 +1,47 @@
+// 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 the body of an expansion statement to be a compound statement}}
+      ; // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
+  template for (int x : {1})
+    if (x) // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
+      ;
+  template for (int x : {1})
+    switch (x) // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
+      ;
+  template for (int x : {1})
+    for (;;) // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
+      ;
+  template for (int x : {1})
+    while (x) // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
+      ;
+  template for (int x : {1})
+    do // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
+      ;
+    while (x);
+  template for (int x : {1})
+    return; // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
+  template for (int x : {1})
+    [] {}(); // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
+  template for (int x : {1})
+    [ // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
+      []] if (x)
+      ;
+  template for (int x : {1})
+    [ // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
+      [likely]] if (x)
+      ;
+  template for (int x : {1})
+    [ // expected-warning {{ISO C++ forbids attributes before the compound statement of an expansion statement}}
+      []] {}
+  template for (int x : {1})
+    [ // expected-warning {{ISO C++ forbids attributes before the compound statement of an expansion statement}}
+      [likely]] {}
+  template for (int x : {1})
+    __attribute__ // expected-warning {{ISO C++ forbids attributes before the compound statement of an expansion statement}}
+      (()) {}
+  template for (int x : {1})
+    foo: {} // expected-error {{labels are not allowed in expansion statements}}
+}



More information about the llvm-branch-commits mailing list