https://github.com/regevran updated https://github.com/llvm/llvm-project/pull/221711
>From 7fb058545a2c275cc5bee88b3b41b35eb56c4fb6 Mon Sep 17 00:00:00 2001 From: Ran Regev <[email protected]> Date: Mon, 7 Sep 2026 12:57:15 +0300 Subject: [PATCH 1/2] [clang] Support DecompositionDecl in -ast-print DeclPrinter had no VisitDecompositionDecl: auto [a, b] = get(); printed as: auto = get(); silently dropping the whole binding list. At namespace scope this was worse: struct Pair { int a, b; }; Pair get(); auto [gx, gy] = get(); printed as: struct Pair { int a; int b; }; Pair get(); ; ; auto = get(); Added VisitDecompositionDecl, sharing VisitVarDecl's specifier and initializer logic via two extracted helpers: printVarDeclSpecifiers, printVarInitializer, to keep the two in sync Skip BindingDecl in VisitDeclContext. Structured binding packs (`auto [...rest] = arr;`) are a separate, pre-existing gap this doesn't address: a pack binding still prints its name, just without the leading `...`, e.g. auto [first, ...rest, last] = arr; currently prints as auto [first, rest, last] = arr; --- clang/lib/AST/DeclPrinter.cpp | 41 +++++++++++++- clang/test/AST/ast-print-decomposition.cpp | 66 ++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 clang/test/AST/ast-print-decomposition.cpp diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index cdd6bb0a90a2b..6fa7c341ef9d5 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -52,6 +52,9 @@ namespace { void PrintObjCTypeParams(ObjCTypeParamList *Params); void PrintOpenACCRoutineOnLambda(Decl *D); + QualType printVarDeclSpecifiers(VarDecl *D); + void printVarInitializer(VarDecl *D); + public: DeclPrinter(raw_ostream &Out, const PrintingPolicy &Policy, const ASTContext &Context, unsigned Indentation = 0, @@ -73,6 +76,7 @@ namespace { void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitFieldDecl(FieldDecl *D); void VisitVarDecl(VarDecl *D); + void VisitDecompositionDecl(DecompositionDecl *D); void VisitLabelDecl(LabelDecl *D); void VisitParmVarDecl(ParmVarDecl *D); void VisitFileScopeAsmDecl(FileScopeAsmDecl *D); @@ -461,6 +465,11 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) { if (isa<ObjCIvarDecl>(*D)) continue; + // Don't print BindingDecls, as they are printed when visiting the + // containing DecompositionDecl. + if (isa<BindingDecl>(*D)) + continue; + // Skip over implicit declarations in pretty-printing mode. if (D->isImplicit()) continue; @@ -963,7 +972,7 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) { Out << *D << ":"; } -void DeclPrinter::VisitVarDecl(VarDecl *D) { +QualType DeclPrinter::printVarDeclSpecifiers(VarDecl *D) { prettyPrintPragmas(D); if (std::optional<std::string> Attrs = @@ -1006,6 +1015,12 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { } } + return T; +} + +void DeclPrinter::VisitVarDecl(VarDecl *D) { + QualType T = printVarDeclSpecifiers(D); + printDeclType(T, (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters && D->getIdentifier()) ? D->getIdentifier()->deuglifiedName() @@ -1015,6 +1030,10 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { prettyPrintAttributes(D, AttrPosAsWritten::Right)) Out << ' ' << *Attrs; + printVarInitializer(D); +} + +void DeclPrinter::printVarInitializer(VarDecl *D) { Expr *Init = D->getInit(); if (!Policy.SuppressInitializers && Init) { bool ImplicitInit = false; @@ -1044,6 +1063,26 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { } } +void DeclPrinter::VisitDecompositionDecl(DecompositionDecl *D) { + QualType T = printVarDeclSpecifiers(D); + + // DecompositionDecl has no name of its own. + printDeclType(T, ""); + + Out << " ["; + bool First = true; + for (BindingDecl *B : D->bindings()) { + if (!First) + Out << ", "; + First = false; + // FIXME: this drops the leading "..." for a pack binding. + Out << B->getName(); + } + Out << "]"; + + printVarInitializer(D); +} + void DeclPrinter::VisitParmVarDecl(ParmVarDecl *D) { VisitVarDecl(D); } diff --git a/clang/test/AST/ast-print-decomposition.cpp b/clang/test/AST/ast-print-decomposition.cpp new file mode 100644 index 0000000000000..aabfcad111bc5 --- /dev/null +++ b/clang/test/AST/ast-print-decomposition.cpp @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -std=c++20 -ast-print %s | FileCheck %s + +// The `[a, b]` binding list must survive -ast-print, not just the type. + +namespace std { +using size_t = decltype(sizeof(0)); +template <typename> struct tuple_size; +template <size_t, typename> struct tuple_element; +} // namespace std + +namespace Aggregate { +struct Pair { int a, b; }; +Pair get(); +Pair &getref(); + +// CHECK-LABEL: void local() { +void local() { + // CHECK-NEXT: auto [x, y] = get(); + auto [x, y] = get(); + // CHECK-NEXT: auto & [rx, ry] = getref(); + auto &[rx, ry] = getref(); + // CHECK-NEXT: const auto [cx, cy] = get(); + const auto [cx, cy] = get(); + // CHECK-NEXT: static auto [sx, sy] = get(); + static auto [sx, sy] = get(); +} +} // namespace Aggregate + +namespace Array { +// CHECK-LABEL: void local() { +void local() { + // CHECK-NEXT: int arr[3] = {1, 2, 3}; + int arr[3] = {1, 2, 3}; + // CHECK-NEXT: auto [a, b, c] + auto [a, b, c] = arr; +} +} // namespace Array + +namespace TupleLike { +struct Two {}; +Two getTwo(); +} // namespace TupleLike + +template <> struct std::tuple_size<TupleLike::Two> { enum { value = 2 }; }; +template <> struct std::tuple_element<0, TupleLike::Two> { using type = int; }; +template <> struct std::tuple_element<1, TupleLike::Two> { using type = int; }; + +namespace TupleLike { +// get() must be found by ADL, so it needs to live here, not at global scope. +template <std::size_t N> int get(Two); + +// CHECK-LABEL: void local() { +void local() { + // CHECK-NEXT: auto [p, q] = getTwo(); + auto [p, q] = getTwo(); +} +} // namespace TupleLike + +namespace NamespaceScope { +using Aggregate::Pair; +using Aggregate::get; + +// CHECK: auto [gx, gy] = get(); +auto [gx, gy] = get(); +// CHECK-NOT: {{^;$}} +} // namespace NamespaceScope >From a6772d4da6fc0a404c3af1cc4da80ba5f97ec100 Mon Sep 17 00:00:00 2001 From: Ran Regev <[email protected]> Date: Tue, 8 Sep 2026 12:02:32 +0300 Subject: [PATCH 2/2] [clang] Fold printDeclType into printVarDeclSpecifiers Both VisitVarDecl and VisitDecompositionDecl called printDeclType(T, ...) immediately after printVarDeclSpecifiers(D) returned T, and the name argument in both cases is computable from D alone: DecompositionDecl has no identifier (Id is null), and NamedDecl::getName() already returns "" -- exactly the declarator VisitDecompositionDecl wants. So the existing ternary, unmodified, already produces the right name for all three cases (VarDecl, ParmVarDecl, DecompositionDecl) without a separate parameter. Move the printDeclType call into printVarDeclSpecifiers so it no longer needs to return QualType across the function boundary; both callers now just call printVarDeclSpecifiers(D) with no local T needed. Assisted-by: AI --- clang/lib/AST/DeclPrinter.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 6fa7c341ef9d5..3d79b5eb241fe 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -52,7 +52,7 @@ namespace { void PrintObjCTypeParams(ObjCTypeParamList *Params); void PrintOpenACCRoutineOnLambda(Decl *D); - QualType printVarDeclSpecifiers(VarDecl *D); + void printVarDeclSpecifiers(VarDecl *D); void printVarInitializer(VarDecl *D); public: @@ -972,7 +972,7 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) { Out << *D << ":"; } -QualType DeclPrinter::printVarDeclSpecifiers(VarDecl *D) { +void DeclPrinter::printVarDeclSpecifiers(VarDecl *D) { prettyPrintPragmas(D); if (std::optional<std::string> Attrs = @@ -1015,16 +1015,16 @@ QualType DeclPrinter::printVarDeclSpecifiers(VarDecl *D) { } } - return T; -} - -void DeclPrinter::VisitVarDecl(VarDecl *D) { - QualType T = printVarDeclSpecifiers(D); - + // D->getName() is "" for a DecompositionDecl (it has no name of its own), + // which is exactly the declarator we want for one: just the type. printDeclType(T, (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters && D->getIdentifier()) ? D->getIdentifier()->deuglifiedName() : D->getName()); +} + +void DeclPrinter::VisitVarDecl(VarDecl *D) { + printVarDeclSpecifiers(D); if (std::optional<std::string> Attrs = prettyPrintAttributes(D, AttrPosAsWritten::Right)) @@ -1064,10 +1064,7 @@ void DeclPrinter::printVarInitializer(VarDecl *D) { } void DeclPrinter::VisitDecompositionDecl(DecompositionDecl *D) { - QualType T = printVarDeclSpecifiers(D); - - // DecompositionDecl has no name of its own. - printDeclType(T, ""); + printVarDeclSpecifiers(D); Out << " ["; bool First = true; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
