[clang] [clang] more useful error message for decomposition declaration missing initializer (PR #127924)
Kevin Bravo via cfe-commits
cfe-commits at lists.llvm.org
Sat Jun 27 14:23:25 PDT 2026
https://github.com/kbrav updated https://github.com/llvm/llvm-project/pull/127924
>From 7f7b9b3f2e7324bd290decb7151c9432875b1dd6 Mon Sep 17 00:00:00 2001
From: kbrav <kevin at halys.dev>
Date: Wed, 19 Feb 2025 19:05:05 -0500
Subject: [PATCH 1/9] [clang] more useful error message for decomposition
declaration missing initializer (#90107)
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/lib/Sema/SemaDecl.cpp | 12 +++++++++++-
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index feef50812eca9..ad36ae898b147 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -555,7 +555,7 @@ def err_decomp_decl_template : Error<
def err_decomp_decl_not_alone : Error<
"decomposition declaration must be the only declaration in its group">;
def err_decomp_decl_requires_init : Error<
- "decomposition declaration %0 requires an initializer">;
+ "decomposition declaration %0 requires an initializer, but got %1 instead">;
def err_decomp_decl_wrong_number_bindings : Error<
"type %0 decomposes into %3 %plural{1:element|:elements}2, but "
"%select{%plural{0:no|:only %1}1|%1}4 "
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 362df485a025c..c62041c8c5e93 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14058,7 +14058,17 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
// C++1z [dcl.dcl]p1 grammar implies that an initializer is mandatory.
if (isa<DecompositionDecl>(RealDecl)) {
- Diag(Var->getLocation(), diag::err_decomp_decl_requires_init) << Var;
+ Preprocessor &PP = getPreprocessor();
+ SourceManager &SM = Context.getSourceManager();
+ LangOptions LO = Context.getLangOpts();
+
+ // Lexer previously checked for '=' and didn't find it
+ // Highlight the token found in its place in the error message
+ Token Tok;
+ Lexer::getRawToken(PP.getLastCachedTokenLocation(), Tok, SM, LO);
+
+ Diag(Tok.getLocation(), diag::err_decomp_decl_requires_init)
+ << Var << Lexer::getSpelling(Tok, SM, LO);
Var->setInvalidDecl();
return;
}
>From cd657462ec40663896fa60fdabf565625188958f Mon Sep 17 00:00:00 2001
From: kbrav <kevin at halys.dev>
Date: Wed, 19 Feb 2025 19:51:19 -0500
Subject: [PATCH 2/9] run clang-format
---
clang/lib/Sema/SemaDecl.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index c62041c8c5e93..724da72b8115e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14058,9 +14058,9 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
// C++1z [dcl.dcl]p1 grammar implies that an initializer is mandatory.
if (isa<DecompositionDecl>(RealDecl)) {
- Preprocessor &PP = getPreprocessor();
+ Preprocessor &PP = getPreprocessor();
SourceManager &SM = Context.getSourceManager();
- LangOptions LO = Context.getLangOpts();
+ LangOptions LO = Context.getLangOpts();
// Lexer previously checked for '=' and didn't find it
// Highlight the token found in its place in the error message
@@ -14068,7 +14068,7 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
Lexer::getRawToken(PP.getLastCachedTokenLocation(), Tok, SM, LO);
Diag(Tok.getLocation(), diag::err_decomp_decl_requires_init)
- << Var << Lexer::getSpelling(Tok, SM, LO);
+ << Var << Lexer::getSpelling(Tok, SM, LO);
Var->setInvalidDecl();
return;
}
>From feac0003628dd18a54a8c1f101bd21be4e714534 Mon Sep 17 00:00:00 2001
From: kbrav <kevin at halys.dev>
Date: Tue, 25 Feb 2025 02:01:22 -0500
Subject: [PATCH 3/9] highlight token after decomposition decl in parser
---
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/Basic/DiagnosticParseKinds.td | 1 +
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/lib/Parse/ParseDecl.cpp | 2 ++
clang/lib/Sema/SemaDecl.cpp | 12 +-----------
clang/test/PCH/cxx1z-decomposition.cpp | 2 +-
clang/test/Parser/cxx1z-decomposition.cpp | 9 +++++----
clang/test/SemaCXX/cxx1z-decomposition.cpp | 2 +-
8 files changed, 13 insertions(+), 18 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 657340c170503..109abdef4ad01 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -193,6 +193,7 @@ Improvements to Clang's diagnostics
under the subgroup ``-Wunsafe-buffer-usage-in-libc-call``.
- Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
``-Wno-error=parentheses``.
+- Added a clearer diagnostic for uninitialized decomposition declarations.
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index c513dab810d1f..f80670949e58a 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -497,6 +497,7 @@ def err_expected_coloncolon_after_super : Error<
def ext_decomp_decl_empty : ExtWarn<
"ISO C++17 does not allow a decomposition group to be empty">,
InGroup<DiagGroup<"empty-decomposition">>;
+def err_expected_init : Error<"expected initializer before '%0'">;
def err_function_parameter_limit_exceeded : Error<
"too many function parameters; subsequent parameters will be ignored">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 980960095d68a..51301d95e55b9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -555,7 +555,7 @@ def err_decomp_decl_template : Error<
def err_decomp_decl_not_alone : Error<
"decomposition declaration must be the only declaration in its group">;
def err_decomp_decl_requires_init : Error<
- "decomposition declaration %0 requires an initializer, but got %1 instead">;
+ "decomposition declaration %0 requires an initializer">;
def err_decomp_decl_wrong_number_bindings : Error<
"type %0 decomposes into %3 %plural{1:element|:elements}2, but "
"%select{%plural{0:no|:only %1}1|%1}4 "
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7ae136af47391..1cb871493aaeb 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2932,6 +2932,8 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
break;
}
case InitKind::Uninitialized: {
+ if (D.isDecompositionDeclarator())
+ Diag(Tok, diag::err_expected_init) << PP.getSpelling(Tok);
Actions.ActOnUninitializedDecl(ThisDecl);
break;
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 54bd22a4f5254..285bd27a35a76 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14065,17 +14065,7 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
// C++1z [dcl.dcl]p1 grammar implies that an initializer is mandatory.
if (isa<DecompositionDecl>(RealDecl)) {
- Preprocessor &PP = getPreprocessor();
- SourceManager &SM = Context.getSourceManager();
- LangOptions LO = Context.getLangOpts();
-
- // Lexer previously checked for '=' and didn't find it
- // Highlight the token found in its place in the error message
- Token Tok;
- Lexer::getRawToken(PP.getLastCachedTokenLocation(), Tok, SM, LO);
-
- Diag(Tok.getLocation(), diag::err_decomp_decl_requires_init)
- << Var << Lexer::getSpelling(Tok, SM, LO);
+ Diag(Var->getLocation(), diag::err_decomp_decl_requires_init) << Var;
Var->setInvalidDecl();
return;
}
diff --git a/clang/test/PCH/cxx1z-decomposition.cpp b/clang/test/PCH/cxx1z-decomposition.cpp
index 914ce80c550d1..e6d56bfefd3d0 100644
--- a/clang/test/PCH/cxx1z-decomposition.cpp
+++ b/clang/test/PCH/cxx1z-decomposition.cpp
@@ -22,7 +22,7 @@ constexpr int foo(Q &&q) {
return a * 10 + b;
}
-auto [noinit]; // expected-error{{decomposition declaration '[noinit]' requires an initializer}}
+auto [noinit]; // expected-error{{decomposition declaration '[noinit]' requires an initializer}} expected-error{{expected initializer before ';'}}
#else
diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp
index acf3f99069185..94d3f15f2e498 100644
--- a/clang/test/Parser/cxx1z-decomposition.cpp
+++ b/clang/test/Parser/cxx1z-decomposition.cpp
@@ -103,10 +103,10 @@ namespace BadSpecifiers {
// FIXME: This error is not very good.
auto [d]() = s; // expected-error {{expected ';'}} expected-error {{expected expression}}
- auto [e][1] = s; // expected-error {{expected ';'}} expected-error {{requires an initializer}}
+ auto [e][1] = s; // expected-error {{expected ';'}} expected-error {{requires an initializer}} expected-error {{expected initializer before '['}}
// FIXME: This should fire the 'misplaced array declarator' diagnostic.
- int [K] arr = {0}; // expected-error {{expected ';'}} expected-error {{cannot be declared with type 'int'}} expected-error {{decomposition declaration '[K]' requires an initializer}}
+ int [K] arr = {0}; // expected-error {{expected ';'}} expected-error {{cannot be declared with type 'int'}} expected-error {{decomposition declaration '[K]' requires an initializer}} expected-error {{expected initializer before 'arr'}}
int [5] arr = {0}; // expected-error {{place the brackets after the name}}
auto *[f] = s; // expected-error {{cannot be declared with type 'auto *'}} expected-error {{incompatible initializer}}
@@ -120,7 +120,7 @@ namespace BadSpecifiers {
[[]] auto [ok_3] = s;
alignas(S) auto [ok_4] = s;
- auto [bad_attr_2] [[]] = s; // expected-error {{expected ';'}} expected-error {{}}
+ auto [bad_attr_2] [[]] = s; // expected-error {{expected ';'}} expected-error {{}} expected-error {{decomposition declaration '[bad_attr_2]' requires an initializer}}
}
}
@@ -144,7 +144,7 @@ namespace Init {
void f() {
int arr[1];
struct S { int n; };
- auto &[bad1]; // expected-error {{decomposition declaration '[bad1]' requires an initializer}}
+ auto &[bad1]; // expected-error {{decomposition declaration '[bad1]' requires an initializer}} expected-error {{expected initializer before ';'}}
const auto &[bad2](S{}, S{}); // expected-error {{initializer for variable '[bad2]' with type 'const auto &' contains multiple expressions}}
const auto &[bad3](); // expected-error {{expected expression}}
auto &[good1] = arr;
@@ -152,6 +152,7 @@ namespace Init {
const auto &[good3](S{});
S [goodish3] = { 4 }; // expected-error {{cannot be declared with type 'S'}}
S [goodish4] { 4 }; // expected-error {{cannot be declared with type 'S'}}
+ auto [A, B] C = {1, 2}; // expected-error{{expected initializer before 'C'}} expected-error{{decomposition declaration '[A, B]' requires an initializer}} expected-error{{expected ';' at end of declaration}}
}
}
diff --git a/clang/test/SemaCXX/cxx1z-decomposition.cpp b/clang/test/SemaCXX/cxx1z-decomposition.cpp
index 95c64bc3b8bff..f68d87446d33a 100644
--- a/clang/test/SemaCXX/cxx1z-decomposition.cpp
+++ b/clang/test/SemaCXX/cxx1z-decomposition.cpp
@@ -121,7 +121,7 @@ void for_range() {
}
int error_recovery() {
- auto [foobar]; // expected-error {{requires an initializer}}
+ auto [foobar]; // expected-error {{requires an initializer}} expected-error {{expected initializer before ';'}}
return foobar_; // expected-error {{undeclared identifier 'foobar_'}}
}
>From 8bf9b8304a6e8f4e4a6e96bec9ccf48ebb11a6bd Mon Sep 17 00:00:00 2001
From: kbrav <kevin at halys.dev>
Date: Fri, 28 Feb 2025 19:54:07 -0500
Subject: [PATCH 4/9] test uninitialized decomposition decl error with
templates
---
clang/test/Parser/cxx1z-decomposition.cpp | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp
index 94d3f15f2e498..9b7a320ad7dd1 100644
--- a/clang/test/Parser/cxx1z-decomposition.cpp
+++ b/clang/test/Parser/cxx1z-decomposition.cpp
@@ -140,8 +140,10 @@ namespace Template {
template<typename T> auto [a, b, c] = n; // expected-error {{decomposition declaration template not supported}}
}
+#define MYC C
+
namespace Init {
- void f() {
+ template<typename T> T f(T t) {
int arr[1];
struct S { int n; };
auto &[bad1]; // expected-error {{decomposition declaration '[bad1]' requires an initializer}} expected-error {{expected initializer before ';'}}
@@ -153,6 +155,8 @@ namespace Init {
S [goodish3] = { 4 }; // expected-error {{cannot be declared with type 'S'}}
S [goodish4] { 4 }; // expected-error {{cannot be declared with type 'S'}}
auto [A, B] C = {1, 2}; // expected-error{{expected initializer before 'C'}} expected-error{{decomposition declaration '[A, B]' requires an initializer}} expected-error{{expected ';' at end of declaration}}
+ T t1 = t; // check that uninitialized decomposition declaration error works with templates and macros
+ auto [t0, t2] MYC = {t, t1}; // expected-error{{expected initializer before 'C'}} expected-error{{decomposition declaration '[t0, t2]' requires an initializer}} expected-error{{expected ';' at end of declaration}}
}
}
>From a940d4934c6232f7cc078837da00a37b32fbc9eb Mon Sep 17 00:00:00 2001
From: kbrav <kevin at halys.dev>
Date: Mon, 3 Mar 2025 06:42:10 -0500
Subject: [PATCH 5/9] make uninitialized decomposition declaration errors less
redundant
---
clang/include/clang/Basic/DiagnosticParseKinds.td | 2 +-
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 --
clang/lib/Parse/ParseDecl.cpp | 2 +-
clang/lib/Sema/SemaDecl.cpp | 1 -
clang/test/PCH/cxx1z-decomposition.cpp | 2 +-
clang/test/Parser/cxx1z-decomposition.cpp | 12 ++++++------
clang/test/SemaCXX/cxx1z-decomposition.cpp | 2 +-
7 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index f80670949e58a..bd2c6ad77340f 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -497,7 +497,7 @@ def err_expected_coloncolon_after_super : Error<
def ext_decomp_decl_empty : ExtWarn<
"ISO C++17 does not allow a decomposition group to be empty">,
InGroup<DiagGroup<"empty-decomposition">>;
-def err_expected_init : Error<"expected initializer before '%0'">;
+def err_decomp_decl_expected_init : Error<"decomposition declaration expected initializer before '%0'">;
def err_function_parameter_limit_exceeded : Error<
"too many function parameters; subsequent parameters will be ignored">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 51301d95e55b9..7bfcdded70d6d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -554,8 +554,6 @@ def err_decomp_decl_template : Error<
"decomposition declaration template not supported">;
def err_decomp_decl_not_alone : Error<
"decomposition declaration must be the only declaration in its group">;
-def err_decomp_decl_requires_init : Error<
- "decomposition declaration %0 requires an initializer">;
def err_decomp_decl_wrong_number_bindings : Error<
"type %0 decomposes into %3 %plural{1:element|:elements}2, but "
"%select{%plural{0:no|:only %1}1|%1}4 "
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 1cb871493aaeb..487580d6f2dcc 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2933,7 +2933,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
}
case InitKind::Uninitialized: {
if (D.isDecompositionDeclarator())
- Diag(Tok, diag::err_expected_init) << PP.getSpelling(Tok);
+ Diag(Tok, diag::err_decomp_decl_expected_init) << PP.getSpelling(Tok);
Actions.ActOnUninitializedDecl(ThisDecl);
break;
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 285bd27a35a76..5860e80d45a8a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14065,7 +14065,6 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
// C++1z [dcl.dcl]p1 grammar implies that an initializer is mandatory.
if (isa<DecompositionDecl>(RealDecl)) {
- Diag(Var->getLocation(), diag::err_decomp_decl_requires_init) << Var;
Var->setInvalidDecl();
return;
}
diff --git a/clang/test/PCH/cxx1z-decomposition.cpp b/clang/test/PCH/cxx1z-decomposition.cpp
index e6d56bfefd3d0..13e77f0b16e73 100644
--- a/clang/test/PCH/cxx1z-decomposition.cpp
+++ b/clang/test/PCH/cxx1z-decomposition.cpp
@@ -22,7 +22,7 @@ constexpr int foo(Q &&q) {
return a * 10 + b;
}
-auto [noinit]; // expected-error{{decomposition declaration '[noinit]' requires an initializer}} expected-error{{expected initializer before ';'}}
+auto [noinit]; // expected-error{{decomposition declaration expected initializer before ';'}}
#else
diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp
index 9b7a320ad7dd1..a4dfb4370c661 100644
--- a/clang/test/Parser/cxx1z-decomposition.cpp
+++ b/clang/test/Parser/cxx1z-decomposition.cpp
@@ -103,10 +103,10 @@ namespace BadSpecifiers {
// FIXME: This error is not very good.
auto [d]() = s; // expected-error {{expected ';'}} expected-error {{expected expression}}
- auto [e][1] = s; // expected-error {{expected ';'}} expected-error {{requires an initializer}} expected-error {{expected initializer before '['}}
+ auto [e][1] = s; // expected-error {{expected ';'}} expected-error {{expected initializer before '['}}
// FIXME: This should fire the 'misplaced array declarator' diagnostic.
- int [K] arr = {0}; // expected-error {{expected ';'}} expected-error {{cannot be declared with type 'int'}} expected-error {{decomposition declaration '[K]' requires an initializer}} expected-error {{expected initializer before 'arr'}}
+ int [K] arr = {0}; // expected-error {{expected ';'}} expected-error {{cannot be declared with type 'int'}} expected-error {{expected initializer before 'arr'}}
int [5] arr = {0}; // expected-error {{place the brackets after the name}}
auto *[f] = s; // expected-error {{cannot be declared with type 'auto *'}} expected-error {{incompatible initializer}}
@@ -120,7 +120,7 @@ namespace BadSpecifiers {
[[]] auto [ok_3] = s;
alignas(S) auto [ok_4] = s;
- auto [bad_attr_2] [[]] = s; // expected-error {{expected ';'}} expected-error {{}} expected-error {{decomposition declaration '[bad_attr_2]' requires an initializer}}
+ auto [bad_attr_2] [[]] = s; // expected-error {{expected ';'}} expected-error {{}}
}
}
@@ -146,7 +146,7 @@ namespace Init {
template<typename T> T f(T t) {
int arr[1];
struct S { int n; };
- auto &[bad1]; // expected-error {{decomposition declaration '[bad1]' requires an initializer}} expected-error {{expected initializer before ';'}}
+ auto &[bad1]; // expected-error {{decomposition declaration expected initializer before ';'}}
const auto &[bad2](S{}, S{}); // expected-error {{initializer for variable '[bad2]' with type 'const auto &' contains multiple expressions}}
const auto &[bad3](); // expected-error {{expected expression}}
auto &[good1] = arr;
@@ -154,9 +154,9 @@ namespace Init {
const auto &[good3](S{});
S [goodish3] = { 4 }; // expected-error {{cannot be declared with type 'S'}}
S [goodish4] { 4 }; // expected-error {{cannot be declared with type 'S'}}
- auto [A, B] C = {1, 2}; // expected-error{{expected initializer before 'C'}} expected-error{{decomposition declaration '[A, B]' requires an initializer}} expected-error{{expected ';' at end of declaration}}
+ auto [A, B] C = {1, 2}; // expected-error{{decomposition declaration expected initializer before 'C'}} expected-error{{expected ';' at end of declaration}}
T t1 = t; // check that uninitialized decomposition declaration error works with templates and macros
- auto [t0, t2] MYC = {t, t1}; // expected-error{{expected initializer before 'C'}} expected-error{{decomposition declaration '[t0, t2]' requires an initializer}} expected-error{{expected ';' at end of declaration}}
+ auto [t0, t2] MYC = {t, t1}; // expected-error{{decomposition declaration expected initializer before 'C'}} expected-error{{expected ';' at end of declaration}}
}
}
diff --git a/clang/test/SemaCXX/cxx1z-decomposition.cpp b/clang/test/SemaCXX/cxx1z-decomposition.cpp
index f68d87446d33a..c678e9cac38f3 100644
--- a/clang/test/SemaCXX/cxx1z-decomposition.cpp
+++ b/clang/test/SemaCXX/cxx1z-decomposition.cpp
@@ -121,7 +121,7 @@ void for_range() {
}
int error_recovery() {
- auto [foobar]; // expected-error {{requires an initializer}} expected-error {{expected initializer before ';'}}
+ auto [foobar]; // expected-error {{decomposition declaration expected initializer before ';'}}
return foobar_; // expected-error {{undeclared identifier 'foobar_'}}
}
>From 05b5b634cea48ffbbaf37378a694f6bfc2f1c23f Mon Sep 17 00:00:00 2001
From: kbrav <kevin at halys.dev>
Date: Mon, 3 Mar 2025 13:36:43 -0500
Subject: [PATCH 6/9] highlight the declaration
---
clang/include/clang/Basic/DiagnosticParseKinds.td | 2 +-
clang/lib/Parse/ParseDecl.cpp | 5 +++--
clang/test/PCH/cxx1z-decomposition.cpp | 2 +-
clang/test/Parser/cxx1z-decomposition.cpp | 6 +++---
clang/test/SemaCXX/cxx1z-decomposition.cpp | 2 +-
5 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index bd2c6ad77340f..71abda07ef3f3 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -497,7 +497,7 @@ def err_expected_coloncolon_after_super : Error<
def ext_decomp_decl_empty : ExtWarn<
"ISO C++17 does not allow a decomposition group to be empty">,
InGroup<DiagGroup<"empty-decomposition">>;
-def err_decomp_decl_expected_init : Error<"decomposition declaration expected initializer before '%0'">;
+def err_decomp_decl_expected_init : Error<"decomposition declaration %0 expected initializer before '%1'">;
def err_function_parameter_limit_exceeded : Error<
"too many function parameters; subsequent parameters will be ignored">;
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 487580d6f2dcc..9566668a1860c 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2932,9 +2932,10 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
break;
}
case InitKind::Uninitialized: {
- if (D.isDecompositionDeclarator())
- Diag(Tok, diag::err_decomp_decl_expected_init) << PP.getSpelling(Tok);
Actions.ActOnUninitializedDecl(ThisDecl);
+ if (D.isDecompositionDeclarator())
+ Diag(Tok, diag::err_decomp_decl_expected_init)
+ << dyn_cast<VarDecl>(ThisDecl) << PP.getSpelling(Tok);
break;
}
}
diff --git a/clang/test/PCH/cxx1z-decomposition.cpp b/clang/test/PCH/cxx1z-decomposition.cpp
index 13e77f0b16e73..be8999bc10480 100644
--- a/clang/test/PCH/cxx1z-decomposition.cpp
+++ b/clang/test/PCH/cxx1z-decomposition.cpp
@@ -22,7 +22,7 @@ constexpr int foo(Q &&q) {
return a * 10 + b;
}
-auto [noinit]; // expected-error{{decomposition declaration expected initializer before ';'}}
+auto [noinit]; // expected-error{{decomposition declaration '[noinit]' expected initializer before ';'}}
#else
diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp
index a4dfb4370c661..e507d0885fa85 100644
--- a/clang/test/Parser/cxx1z-decomposition.cpp
+++ b/clang/test/Parser/cxx1z-decomposition.cpp
@@ -146,7 +146,7 @@ namespace Init {
template<typename T> T f(T t) {
int arr[1];
struct S { int n; };
- auto &[bad1]; // expected-error {{decomposition declaration expected initializer before ';'}}
+ auto &[bad1]; // expected-error {{decomposition declaration '[bad1]' expected initializer before ';'}}
const auto &[bad2](S{}, S{}); // expected-error {{initializer for variable '[bad2]' with type 'const auto &' contains multiple expressions}}
const auto &[bad3](); // expected-error {{expected expression}}
auto &[good1] = arr;
@@ -154,9 +154,9 @@ namespace Init {
const auto &[good3](S{});
S [goodish3] = { 4 }; // expected-error {{cannot be declared with type 'S'}}
S [goodish4] { 4 }; // expected-error {{cannot be declared with type 'S'}}
- auto [A, B] C = {1, 2}; // expected-error{{decomposition declaration expected initializer before 'C'}} expected-error{{expected ';' at end of declaration}}
+ auto [A, B] C = {1, 2}; // expected-error{{decomposition declaration '[A, B]' expected initializer before 'C'}} expected-error{{expected ';' at end of declaration}}
T t1 = t; // check that uninitialized decomposition declaration error works with templates and macros
- auto [t0, t2] MYC = {t, t1}; // expected-error{{decomposition declaration expected initializer before 'C'}} expected-error{{expected ';' at end of declaration}}
+ auto [t0, t2] MYC = {t, t1}; // expected-error{{decomposition declaration '[t0, t2]' expected initializer before 'C'}} expected-error{{expected ';' at end of declaration}}
}
}
diff --git a/clang/test/SemaCXX/cxx1z-decomposition.cpp b/clang/test/SemaCXX/cxx1z-decomposition.cpp
index c678e9cac38f3..ccef39f320f7f 100644
--- a/clang/test/SemaCXX/cxx1z-decomposition.cpp
+++ b/clang/test/SemaCXX/cxx1z-decomposition.cpp
@@ -121,7 +121,7 @@ void for_range() {
}
int error_recovery() {
- auto [foobar]; // expected-error {{decomposition declaration expected initializer before ';'}}
+ auto [foobar]; // expected-error {{decomposition declaration '[foobar]' expected initializer before ';'}}
return foobar_; // expected-error {{undeclared identifier 'foobar_'}}
}
>From 96e8723fbf341aa83b2b32df31acb0c3d0e9f86e Mon Sep 17 00:00:00 2001
From: kbrav <kevin at halys.dev>
Date: Mon, 3 Mar 2025 13:37:28 -0500
Subject: [PATCH 7/9] format
---
clang/lib/Parse/ParseDecl.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 9566668a1860c..65fa3620f952b 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2935,7 +2935,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
Actions.ActOnUninitializedDecl(ThisDecl);
if (D.isDecompositionDeclarator())
Diag(Tok, diag::err_decomp_decl_expected_init)
- << dyn_cast<VarDecl>(ThisDecl) << PP.getSpelling(Tok);
+ << dyn_cast<VarDecl>(ThisDecl) << PP.getSpelling(Tok);
break;
}
}
>From 344cec303d71bcd8ebc6791e4df23e383ff43f58 Mon Sep 17 00:00:00 2001
From: kbrav <kevin at halys.dev>
Date: Tue, 4 Mar 2025 13:35:22 -0500
Subject: [PATCH 8/9] clearer wording
---
clang/include/clang/Basic/DiagnosticParseKinds.td | 2 +-
clang/test/PCH/cxx1z-decomposition.cpp | 2 +-
clang/test/Parser/cxx1z-decomposition.cpp | 10 +++++-----
clang/test/SemaCXX/cxx1z-decomposition.cpp | 2 +-
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 71abda07ef3f3..bc41d8d771404 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -497,7 +497,7 @@ def err_expected_coloncolon_after_super : Error<
def ext_decomp_decl_empty : ExtWarn<
"ISO C++17 does not allow a decomposition group to be empty">,
InGroup<DiagGroup<"empty-decomposition">>;
-def err_decomp_decl_expected_init : Error<"decomposition declaration %0 expected initializer before '%1'">;
+def err_decomp_decl_expected_init : Error<"decomposition declaration %0 requires an initializer; got '%1'">;
def err_function_parameter_limit_exceeded : Error<
"too many function parameters; subsequent parameters will be ignored">;
diff --git a/clang/test/PCH/cxx1z-decomposition.cpp b/clang/test/PCH/cxx1z-decomposition.cpp
index be8999bc10480..5bd02963bdafa 100644
--- a/clang/test/PCH/cxx1z-decomposition.cpp
+++ b/clang/test/PCH/cxx1z-decomposition.cpp
@@ -22,7 +22,7 @@ constexpr int foo(Q &&q) {
return a * 10 + b;
}
-auto [noinit]; // expected-error{{decomposition declaration '[noinit]' expected initializer before ';'}}
+auto [noinit]; // expected-error{{decomposition declaration '[noinit]' requires an initializer; got ';'}}
#else
diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp
index e507d0885fa85..88f9be292dcf2 100644
--- a/clang/test/Parser/cxx1z-decomposition.cpp
+++ b/clang/test/Parser/cxx1z-decomposition.cpp
@@ -103,10 +103,10 @@ namespace BadSpecifiers {
// FIXME: This error is not very good.
auto [d]() = s; // expected-error {{expected ';'}} expected-error {{expected expression}}
- auto [e][1] = s; // expected-error {{expected ';'}} expected-error {{expected initializer before '['}}
+ auto [e][1] = s; // expected-error {{expected ';'}} expected-error {{decomposition declaration '[e]' requires an initializer; got '['}}
// FIXME: This should fire the 'misplaced array declarator' diagnostic.
- int [K] arr = {0}; // expected-error {{expected ';'}} expected-error {{cannot be declared with type 'int'}} expected-error {{expected initializer before 'arr'}}
+ int [K] arr = {0}; // expected-error {{expected ';'}} expected-error {{cannot be declared with type 'int'}} expected-error {{decomposition declaration '[K]' requires an initializer; got 'arr'}}
int [5] arr = {0}; // expected-error {{place the brackets after the name}}
auto *[f] = s; // expected-error {{cannot be declared with type 'auto *'}} expected-error {{incompatible initializer}}
@@ -146,7 +146,7 @@ namespace Init {
template<typename T> T f(T t) {
int arr[1];
struct S { int n; };
- auto &[bad1]; // expected-error {{decomposition declaration '[bad1]' expected initializer before ';'}}
+ auto &[bad1]; // expected-error {{decomposition declaration '[bad1]' requires an initializer; got ';'}}
const auto &[bad2](S{}, S{}); // expected-error {{initializer for variable '[bad2]' with type 'const auto &' contains multiple expressions}}
const auto &[bad3](); // expected-error {{expected expression}}
auto &[good1] = arr;
@@ -154,9 +154,9 @@ namespace Init {
const auto &[good3](S{});
S [goodish3] = { 4 }; // expected-error {{cannot be declared with type 'S'}}
S [goodish4] { 4 }; // expected-error {{cannot be declared with type 'S'}}
- auto [A, B] C = {1, 2}; // expected-error{{decomposition declaration '[A, B]' expected initializer before 'C'}} expected-error{{expected ';' at end of declaration}}
+ auto [A, B] C = {1, 2}; // expected-error{{decomposition declaration '[A, B]' requires an initializer; got 'C'}} expected-error{{expected ';' at end of declaration}}
T t1 = t; // check that uninitialized decomposition declaration error works with templates and macros
- auto [t0, t2] MYC = {t, t1}; // expected-error{{decomposition declaration '[t0, t2]' expected initializer before 'C'}} expected-error{{expected ';' at end of declaration}}
+ auto [t0, t2] MYC = {t, t1}; // expected-error{{decomposition declaration '[t0, t2]' requires an initializer; got 'C'}} expected-error{{expected ';' at end of declaration}}
}
}
diff --git a/clang/test/SemaCXX/cxx1z-decomposition.cpp b/clang/test/SemaCXX/cxx1z-decomposition.cpp
index ccef39f320f7f..5020a84278e11 100644
--- a/clang/test/SemaCXX/cxx1z-decomposition.cpp
+++ b/clang/test/SemaCXX/cxx1z-decomposition.cpp
@@ -121,7 +121,7 @@ void for_range() {
}
int error_recovery() {
- auto [foobar]; // expected-error {{decomposition declaration '[foobar]' expected initializer before ';'}}
+ auto [foobar]; // expected-error {{decomposition declaration '[foobar]' requires an initializer; got ';'}}
return foobar_; // expected-error {{undeclared identifier 'foobar_'}}
}
>From 1673b01414cf8ddf2a07b3235ceddb38e14e3366 Mon Sep 17 00:00:00 2001
From: kbrav <kevin at halys.dev>
Date: Sat, 27 Jun 2026 17:10:10 -0400
Subject: [PATCH 9/9] track right square bracket during semantic analysis for
decomposition declaration point the carat to the token the right square on
error for uninitialized decomposition declaration
---
clang/include/clang/AST/DeclCXX.h | 12 +++++++++---
clang/include/clang/Basic/DiagnosticParseKinds.td | 1 -
clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 +++
clang/lib/AST/ASTImporter.cpp | 3 ++-
clang/lib/AST/DeclCXX.cpp | 7 +++++--
clang/lib/Parse/ParseDecl.cpp | 3 ---
clang/lib/Sema/SemaDecl.cpp | 9 +++++++--
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 4 ++--
clang/test/PCH/cxx1z-decomposition.cpp | 2 +-
clang/test/Parser/cxx1z-decomposition.cpp | 10 +++++-----
10 files changed, 34 insertions(+), 20 deletions(-)
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 28d171253dc03..8c4017a80e485 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4265,15 +4265,17 @@ class BindingDecl : public ValueDecl {
class DecompositionDecl final
: public VarDecl,
private llvm::TrailingObjects<DecompositionDecl, BindingDecl *> {
+ /// The closing bracket (before the initializer is expected).
+ SourceLocation RSquareLoc;
/// The number of BindingDecl*s following this object.
unsigned NumBindings;
DecompositionDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
- SourceLocation LSquareLoc, QualType T,
- TypeSourceInfo *TInfo, StorageClass SC,
+ SourceLocation LSquareLoc, SourceLocation RSquareLoc,
+ QualType T, TypeSourceInfo *TInfo, StorageClass SC,
ArrayRef<BindingDecl *> Bindings)
: VarDecl(Decomposition, C, DC, StartLoc, LSquareLoc, nullptr, T, TInfo,
- SC),
+ SC), RSquareLoc(RSquareLoc),
NumBindings(Bindings.size()) {
llvm::uninitialized_copy(Bindings, getTrailingObjects());
for (auto *B : Bindings) {
@@ -4295,6 +4297,7 @@ class DecompositionDecl final
static DecompositionDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation StartLoc,
SourceLocation LSquareLoc,
+ SourceLocation RSquareLoc,
QualType T, TypeSourceInfo *TInfo,
StorageClass S,
ArrayRef<BindingDecl *> Bindings);
@@ -4326,6 +4329,9 @@ class DecompositionDecl final
std::move(Bindings));
}
+ /// The closing bracket (before the initializer is expected).
+ SourceLocation getRSquareLoc() const { return RSquareLoc; }
+
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 91897d6261a5c..4c2db88958e5f 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -512,7 +512,6 @@ def err_expected_coloncolon_after_super : Error<
def ext_decomp_decl_empty : ExtWarn<
"ISO C++17 does not allow a structured binding group to be empty">,
InGroup<DiagGroup<"empty-decomposition">>;
-def err_decomp_decl_expected_init : Error<"structured binding declaration %0 requires an initializer; got '%1'">;
def err_function_parameter_limit_exceeded : Error<
"too many function parameters; subsequent parameters will be ignored">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 44fc74c7ff73f..4896b1437f67c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -617,7 +617,10 @@ def err_decomp_decl_template : Error<
"structured binding declaration cannot be a template">;
def err_decomp_decl_not_alone : Error<
"structured binding declaration must be the only declaration in its group">;
+def err_decomp_decl_requires_init : Error<
+ "structured binding declaration %0 requires an initializer; expected '=' or braced initializer list">;
def err_decomp_decl_wrong_number_bindings : Error<
+
"type %0 binds to %3 %plural{1:element|:elements}2, but "
"%select{%plural{0:no|:only %1}1|%1}4 "
"%plural{1:name was|:names were}1 provided">;
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 567d2d07298a3..ebca88ae51d23 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4838,7 +4838,8 @@ ExpectedDecl ASTNodeImporter::VisitVarDecl(VarDecl *D) {
DecompositionDecl *ToDecomp;
if (GetImportedOrCreateDecl(
ToDecomp, FromDecomp, Importer.getToContext(), DC, ToInnerLocStart,
- Loc, ToType, ToTypeSourceInfo, D->getStorageClass(), Bindings))
+ Loc, FromDecomp->getRSquareLoc(), ToType, ToTypeSourceInfo,
+ D->getStorageClass(), Bindings))
return ToDecomp;
ToVar = ToDecomp;
} else {
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index ce4ba971a4631..86b3182a28a87 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3730,12 +3730,14 @@ void DecompositionDecl::anchor() {}
DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation StartLoc,
SourceLocation LSquareLoc,
+ SourceLocation RSquareLoc,
QualType T, TypeSourceInfo *TInfo,
StorageClass SC,
ArrayRef<BindingDecl *> Bindings) {
size_t Extra = additionalSizeToAlloc<BindingDecl *>(Bindings.size());
return new (C, DC, Extra)
- DecompositionDecl(C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings);
+ DecompositionDecl(C, DC, StartLoc, LSquareLoc, RSquareLoc, T, TInfo, SC,
+ Bindings);
}
DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C,
@@ -3744,7 +3746,8 @@ DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C,
size_t Extra = additionalSizeToAlloc<BindingDecl *>(NumBindings);
auto *Result = new (C, ID, Extra)
DecompositionDecl(C, nullptr, SourceLocation(), SourceLocation(),
- QualType(), nullptr, StorageClass(), {});
+ SourceLocation(), QualType(), nullptr, StorageClass(),
+ {});
// Set up and clean out the bindings array.
Result->NumBindings = NumBindings;
auto *Trail = Result->getTrailingObjects();
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 62e8d58d492be..3f41e7c5c6f0d 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2740,9 +2740,6 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
case InitKind::Uninitialized: {
InitializerScopeRAII InitScope(*this, D, ThisDecl);
Actions.ActOnUninitializedDecl(ThisDecl);
- if (D.isDecompositionDeclarator())
- Diag(Tok, diag::err_decomp_decl_expected_init)
- << dyn_cast<VarDecl>(ThisDecl) << PP.getSpelling(Tok);
break;
}
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 48ba8a9137152..996d68a97fc0e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8036,8 +8036,8 @@ NamedDecl *Sema::ActOnVariableDeclarator(
AddToScope = false;
} else if (D.isDecompositionDeclarator()) {
NewVD = DecompositionDecl::Create(Context, DC, D.getBeginLoc(),
- D.getIdentifierLoc(), R, TInfo, SC,
- Bindings);
+ D.getIdentifierLoc(), D.getEndLoc(),
+ R, TInfo, SC, Bindings);
} else
NewVD = VarDecl::Create(Context, DC, D.getBeginLoc(),
D.getIdentifierLoc(), II, R, TInfo, SC);
@@ -14480,6 +14480,11 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
}
// C++1z [dcl.dcl]p1 grammar implies that an initializer is mandatory.
if (isa<DecompositionDecl>(RealDecl)) {
+ // point carat to the token immediately after the closing bracket
+ auto NextLoc = dyn_cast<DecompositionDecl>(RealDecl)->getRSquareLoc();
+ NextLoc = Lexer::findNextToken(NextLoc, PP.getSourceManager(),
+ PP.getLangOpts())->getLocation();
+ Diag(NextLoc, diag::err_decomp_decl_requires_init) << Var;
Var->setInvalidDecl();
return;
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 324d6bf3857c7..87a133b61c385 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1777,8 +1777,8 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D,
VarDecl *Var;
if (Bindings)
Var = DecompositionDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
- D->getLocation(), TSI->getType(), TSI,
- D->getStorageClass(), *Bindings);
+ D->getLocation(), D->getEndLoc(), TSI->getType(),
+ TSI, D->getStorageClass(), *Bindings);
else
Var = VarDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
D->getLocation(), D->getIdentifier(), TSI->getType(),
diff --git a/clang/test/PCH/cxx1z-decomposition.cpp b/clang/test/PCH/cxx1z-decomposition.cpp
index 980fc4700b710..635ba7c26e09b 100644
--- a/clang/test/PCH/cxx1z-decomposition.cpp
+++ b/clang/test/PCH/cxx1z-decomposition.cpp
@@ -22,7 +22,7 @@ constexpr int foo(Q &&q) {
return a * 10 + b;
}
-auto [noinit]; // expected-error{{structured binding declaration '[noinit]' requires an initializer; got ';'}}
+auto [noinit]; // expected-error{{structured binding declaration '[noinit]' requires an initializer; expected '=' or braced initializer list}}
#else
diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp
index 2244743773d25..76f1b97085814 100644
--- a/clang/test/Parser/cxx1z-decomposition.cpp
+++ b/clang/test/Parser/cxx1z-decomposition.cpp
@@ -111,10 +111,10 @@ namespace BadSpecifiers {
// FIXME: This error is not very good.
auto [d]() = s; // expected-error {{expected ';'}} expected-error {{expected expression}}
- auto [e][1] = s; // expected-error {{expected ';'}} expected-error {{structured binding declaration '[e]' requires an initializer; got '['}}
+ auto [e][1] = s; // expected-error {{expected ';'}} expected-error {{structured binding declaration '[e]' requires an initializer; expected '=' or braced initializer list}}
// FIXME: This should fire the 'misplaced array declarator' diagnostic.
- int [K] arr = {0}; // expected-error {{expected ';'}} expected-error {{cannot be declared with type 'int'}} expected-error {{structured binding declaration '[K]' requires an initializer; got 'arr'}}
+ int [K] arr = {0}; // expected-error {{expected ';'}} expected-error {{cannot be declared with type 'int'}} expected-error {{structured binding declaration '[K]' requires an initializer; expected '=' or braced initializer list}}
int [5] arr = {0}; // expected-error {{place the brackets after the name}}
auto *[f] = s; // expected-error {{cannot be declared with type 'auto *'}} expected-error {{incompatible initializer}}
@@ -154,7 +154,7 @@ namespace Init {
template<typename T> T f(T t) {
int arr[1];
struct S { int n; };
- auto &[bad1]; // expected-error {{structured binding declaration '[bad1]' requires an initializer; got ';'}}
+ auto &[bad1]; // expected-error {{structured binding declaration '[bad1]' requires an initializer; expected '=' or braced initializer list}}
const auto &[bad2](S{}, S{}); // expected-error {{initializer for variable '[bad2]' with type 'const auto &' contains multiple expressions}}
const auto &[bad3](); // expected-error {{expected expression}}
auto &[good1] = arr;
@@ -162,9 +162,9 @@ namespace Init {
const auto &[good3](S{});
S [goodish3] = { 4 }; // expected-error {{cannot be declared with type 'S'}}
S [goodish4] { 4 }; // expected-error {{cannot be declared with type 'S'}}
- auto [A, B] C = {1, 2}; // expected-error{{structured binding declaration '[A, B]' requires an initializer; got 'C'}} expected-error{{expected ';' at end of declaration}}
+ auto [A, B] C = {1, 2}; // expected-error{{structured binding declaration '[A, B]' requires an initializer; expected '=' or braced initializer list}} expected-error{{expected ';' at end of declaration}}
T t1 = t; // check that uninitialized structured binding declaration error works with templates and macros
- auto [t0, t2] MYC = {t, t1}; // expected-error{{structured binding declaration '[t0, t2]' requires an initializer; got 'C'}} expected-error{{expected ';' at end of declaration}}
+ auto [t0, t2] MYC = {t, t1}; // expected-error{{structured binding declaration '[t0, t2]' requires an initializer; expected '=' or braced initializer list}} expected-error{{expected ';' at end of declaration}}
}
}
More information about the cfe-commits
mailing list