[clang] [clang] more useful error message for decomposition declaration missing initializer (PR #127924)

via cfe-commits cfe-commits at lists.llvm.org
Sat Mar 1 01:21:33 PST 2025


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/4] [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/4] 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/4] 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/4] 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}}
   }
 }
 



More information about the cfe-commits mailing list