https://github.com/kimgr updated https://github.com/llvm/llvm-project/pull/174197
From 7c2dcb2b2f4fd1511dfcbd82c0f66fd433d51a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Gr=C3=A4sman?= <[email protected]> Date: Wed, 31 Dec 2025 12:04:56 +0100 Subject: [PATCH 1/2] Control spacing for attribute printing This was motivated by the decl printing for the alignas() keyword attribute: class alignas(1) Foo; would be printed as: class alignas(1) Foo; with two spaces before class name. Rather than trying to help prettyPrintAttributes guess what the caller wants in terms of leading and trailing spaces, split it into two: * getPrintableAttributes: returns attrs that should be printed at Pos * prettyPrintAttributes: writes pre-filtered attrs to Out That way callers can compose and only print desired prefix/suffix if they know there are attributes to print. Add simple test cases for alignas. --- clang/lib/AST/DeclPrinter.cpp | 153 ++++++++++++++++-------- clang/test/SemaCXX/cxx11-attr-print.cpp | 5 + 2 files changed, 108 insertions(+), 50 deletions(-) diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 47ae613b643b6..87a6dc96a0154 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -22,6 +22,7 @@ #include "clang/AST/PrettyPrinter.h" #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -124,9 +125,11 @@ namespace { void printTemplateArguments(ArrayRef<TemplateArgumentLoc> Args, const TemplateParameterList *Params); enum class AttrPosAsWritten { Default = 0, Left, Right }; - bool - prettyPrintAttributes(const Decl *D, - AttrPosAsWritten Pos = AttrPosAsWritten::Default); + AttrVec + getPrintableAttributes(const Decl *D, + AttrPosAsWritten Pos = AttrPosAsWritten::Default); + void prettyPrintAttributes(const AttrVec &Attrs); + void prettyPrintPragmas(Decl *D); void printDeclType(QualType T, StringRef DeclName, bool Pack = false); }; @@ -252,41 +255,41 @@ static DeclPrinter::AttrPosAsWritten getPosAsWritten(const Attr *A, return DeclPrinter::AttrPosAsWritten::Right; } -// returns true if an attribute was printed. -bool DeclPrinter::prettyPrintAttributes(const Decl *D, - AttrPosAsWritten Pos /*=Default*/) { - bool hasPrinted = false; - - if (D->hasAttrs()) { - const AttrVec &Attrs = D->getAttrs(); - for (auto *A : Attrs) { - if (A->isInherited() || A->isImplicit()) - continue; - // Print out the keyword attributes, they aren't regular attributes. - if (Policy.PolishForDeclaration && !A->isKeywordAttribute()) - continue; - switch (A->getKind()) { +AttrVec DeclPrinter::getPrintableAttributes(const Decl *D, + AttrPosAsWritten Pos /*=Default*/) { + AttrVec Printable; + if (!D->hasAttrs()) + return Printable; + for (auto *A : D->getAttrs()) { + if (A->isInherited() || A->isImplicit()) + continue; + // Print out the keyword attributes, they aren't regular attributes. + if (Policy.PolishForDeclaration && !A->isKeywordAttribute()) + continue; + switch (A->getKind()) { #define ATTR(X) #define PRAGMA_SPELLING_ATTR(X) case attr::X: #include "clang/Basic/AttrList.inc" - break; - default: - AttrPosAsWritten APos = getPosAsWritten(A, D); - assert(APos != AttrPosAsWritten::Default && - "Default not a valid for an attribute location"); - if (Pos == AttrPosAsWritten::Default || Pos == APos) { - if (Pos != AttrPosAsWritten::Left) - Out << ' '; - A->printPretty(Out, Policy); - hasPrinted = true; - if (Pos == AttrPosAsWritten::Left) - Out << ' '; - } - break; + break; + default: + AttrPosAsWritten APos = getPosAsWritten(A, D); + assert(APos != AttrPosAsWritten::Default && + "Default not a valid for an attribute location"); + if (Pos == AttrPosAsWritten::Default || Pos == APos) { + Printable.push_back(A); } + break; } } - return hasPrinted; + return Printable; +} + +void DeclPrinter::prettyPrintAttributes(const AttrVec &Attrs) { + llvm::ListSeparator LS(" "); + for (const auto *A : Attrs) { + Out << LS; + A->printPretty(Out, Policy); + } } void DeclPrinter::PrintOpenACCRoutineOnLambda(Decl *D) { @@ -584,12 +587,19 @@ void DeclPrinter::VisitTypedefDecl(TypedefDecl *D) { } QualType Ty = D->getTypeSourceInfo()->getType(); Ty.print(Out, Policy, D->getName(), Indentation); - prettyPrintAttributes(D); + + if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } } void DeclPrinter::VisitTypeAliasDecl(TypeAliasDecl *D) { Out << "using " << *D; - prettyPrintAttributes(D); + if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } Out << " = " << D->getTypeSourceInfo()->getType().getAsString(Policy); } @@ -604,7 +614,10 @@ void DeclPrinter::VisitEnumDecl(EnumDecl *D) { Out << " struct"; } - prettyPrintAttributes(D); + if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } if (D->getDeclName()) Out << ' ' << D->getDeclName(); @@ -624,7 +637,10 @@ void DeclPrinter::VisitRecordDecl(RecordDecl *D) { Out << "__module_private__ "; Out << D->getKindName(); - prettyPrintAttributes(D); + if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } if (D->getIdentifier()) Out << ' ' << *D; @@ -638,7 +654,10 @@ void DeclPrinter::VisitRecordDecl(RecordDecl *D) { void DeclPrinter::VisitEnumConstantDecl(EnumConstantDecl *D) { Out << *D; - prettyPrintAttributes(D); + if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } if (Expr *Init = D->getInitExpr()) { Out << " = "; Init->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context); @@ -664,7 +683,11 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { if (!D->getDescribedFunctionTemplate() && !D->isFunctionTemplateSpecialization()) { prettyPrintPragmas(D); - prettyPrintAttributes(D, AttrPosAsWritten::Left); + if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Left); + !Attrs.empty()) { + prettyPrintAttributes(Attrs); + Out << " "; + } } if (D->isFunctionTemplateSpecialization()) @@ -836,7 +859,11 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { Ty.print(Out, Policy, Proto); } - prettyPrintAttributes(D, AttrPosAsWritten::Right); + if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Right); + !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } if (D->isPureVirtual()) Out << " = 0"; @@ -928,7 +955,10 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) { Out << " = "; Init->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context); } - prettyPrintAttributes(D); + if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } } void DeclPrinter::VisitLabelDecl(LabelDecl *D) { @@ -938,7 +968,11 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) { void DeclPrinter::VisitVarDecl(VarDecl *D) { prettyPrintPragmas(D); - prettyPrintAttributes(D, AttrPosAsWritten::Left); + if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Left); + !Attrs.empty()) { + prettyPrintAttributes(Attrs); + Out << " "; + } if (const auto *Param = dyn_cast<ParmVarDecl>(D); Param && Param->isExplicitObjectParameter()) @@ -981,7 +1015,11 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { ? D->getIdentifier()->deuglifiedName() : D->getName()); - prettyPrintAttributes(D, AttrPosAsWritten::Right); + if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Right); + !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } Expr *Init = D->getInit(); if (!Policy.SuppressInitializers && Init) { @@ -1073,7 +1111,9 @@ void DeclPrinter::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { } void DeclPrinter::VisitEmptyDecl(EmptyDecl *D) { - prettyPrintAttributes(D); + if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { + prettyPrintAttributes(Attrs); + } } void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { @@ -1085,8 +1125,11 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { // FIXME: Move before printing the decl kind to match the behavior of the // attribute printing for variables and function where they are printed first. - if (prettyPrintAttributes(D, AttrPosAsWritten::Left)) - Out << ' '; + if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Left); + !Attrs.empty()) { + prettyPrintAttributes(Attrs); + Out << " "; + } if (D->getIdentifier()) { D->getQualifier().print(Out, Policy); @@ -1104,7 +1147,11 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { } } - prettyPrintAttributes(D, AttrPosAsWritten::Right); + if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Right); + !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } if (D->isCompleteDefinition()) { Out << ' '; @@ -1421,7 +1468,10 @@ void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) { if (OMD->isVariadic()) Out << ", ..."; - prettyPrintAttributes(OMD); + if (AttrVec Attrs = getPrintableAttributes(OMD); !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } if (OMD->getBody() && !Policy.TerseOutput) { Out << ' '; @@ -1477,8 +1527,8 @@ void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) { return; } bool eolnOut = false; - if (OID->hasAttrs()) { - prettyPrintAttributes(OID); + if (AttrVec Attrs = getPrintableAttributes(OID); !Attrs.empty()) { + prettyPrintAttributes(Attrs); Out << "\n"; } @@ -1777,7 +1827,10 @@ void DeclPrinter::VisitHLSLBufferDecl(HLSLBufferDecl *D) { Out << *D; - prettyPrintAttributes(D); + if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { + Out << " "; + prettyPrintAttributes(Attrs); + } Out << " {\n"; VisitDeclContext(D); diff --git a/clang/test/SemaCXX/cxx11-attr-print.cpp b/clang/test/SemaCXX/cxx11-attr-print.cpp index 2b084018bc066..70274bd5aa72a 100644 --- a/clang/test/SemaCXX/cxx11-attr-print.cpp +++ b/clang/test/SemaCXX/cxx11-attr-print.cpp @@ -92,3 +92,8 @@ class FinalNonTemplate final {}; // CHECK: class FinalNonTemplate final { template <typename T> class FinalTemplate final {}; // CHECK: template <typename T> class FinalTemplate final { + +class alignas(8) AlignedClass {}; +// CHECK: class alignas(8) AlignedClass { +template<typename T> class alignas(4) AlignedClassTemplate {}; +// CHECK: class alignas(4) AlignedClassTemplate { From 92be0ad8dee8f848dbabf8a999579e6e6673001c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Gr=C3=A4sman?= <[email protected]> Date: Thu, 8 Jan 2026 21:48:18 +0100 Subject: [PATCH 2/2] Pretty-print attributes to string This simplifies the interface and makes it impossible to send unfiltered attrs to prettyPrintAttributes, e.g. with: prettyPrintAttributes(D->getAttrs()); --- clang/lib/AST/DeclPrinter.cpp | 111 ++++++++++++++-------------------- 1 file changed, 46 insertions(+), 65 deletions(-) diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 87a6dc96a0154..02b8d16b423e3 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -125,10 +125,9 @@ namespace { void printTemplateArguments(ArrayRef<TemplateArgumentLoc> Args, const TemplateParameterList *Params); enum class AttrPosAsWritten { Default = 0, Left, Right }; - AttrVec - getPrintableAttributes(const Decl *D, - AttrPosAsWritten Pos = AttrPosAsWritten::Default); - void prettyPrintAttributes(const AttrVec &Attrs); + std::string + prettyPrintAttributes(const Decl *D, + AttrPosAsWritten Pos = AttrPosAsWritten::Default); void prettyPrintPragmas(Decl *D); void printDeclType(QualType T, StringRef DeclName, bool Pack = false); @@ -255,11 +254,15 @@ static DeclPrinter::AttrPosAsWritten getPosAsWritten(const Attr *A, return DeclPrinter::AttrPosAsWritten::Right; } -AttrVec DeclPrinter::getPrintableAttributes(const Decl *D, - AttrPosAsWritten Pos /*=Default*/) { - AttrVec Printable; +std::string +DeclPrinter::prettyPrintAttributes(const Decl *D, + AttrPosAsWritten Pos /*=Default*/) { if (!D->hasAttrs()) - return Printable; + return {}; + + std::string AttrStr; + llvm::raw_string_ostream AOut(AttrStr); + llvm::ListSeparator LS(" "); for (auto *A : D->getAttrs()) { if (A->isInherited() || A->isImplicit()) continue; @@ -276,20 +279,13 @@ AttrVec DeclPrinter::getPrintableAttributes(const Decl *D, assert(APos != AttrPosAsWritten::Default && "Default not a valid for an attribute location"); if (Pos == AttrPosAsWritten::Default || Pos == APos) { - Printable.push_back(A); + AOut << LS; + A->printPretty(AOut, Policy); } break; } } - return Printable; -} - -void DeclPrinter::prettyPrintAttributes(const AttrVec &Attrs) { - llvm::ListSeparator LS(" "); - for (const auto *A : Attrs) { - Out << LS; - A->printPretty(Out, Policy); - } + return AttrStr; } void DeclPrinter::PrintOpenACCRoutineOnLambda(Decl *D) { @@ -588,17 +584,15 @@ void DeclPrinter::VisitTypedefDecl(TypedefDecl *D) { QualType Ty = D->getTypeSourceInfo()->getType(); Ty.print(Out, Policy, D->getName(), Indentation); - if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + if (std::string Attrs = prettyPrintAttributes(D); !Attrs.empty()) { + Out << " " << Attrs; } } void DeclPrinter::VisitTypeAliasDecl(TypeAliasDecl *D) { Out << "using " << *D; - if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + if (std::string Attrs = prettyPrintAttributes(D); !Attrs.empty()) { + Out << " " << Attrs; } Out << " = " << D->getTypeSourceInfo()->getType().getAsString(Policy); } @@ -614,9 +608,8 @@ void DeclPrinter::VisitEnumDecl(EnumDecl *D) { Out << " struct"; } - if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + if (std::string Attrs = prettyPrintAttributes(D); !Attrs.empty()) { + Out << " " << Attrs; } if (D->getDeclName()) @@ -637,9 +630,8 @@ void DeclPrinter::VisitRecordDecl(RecordDecl *D) { Out << "__module_private__ "; Out << D->getKindName(); - if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + if (std::string Attrs = prettyPrintAttributes(D); !Attrs.empty()) { + Out << " " << Attrs; } if (D->getIdentifier()) @@ -654,9 +646,8 @@ void DeclPrinter::VisitRecordDecl(RecordDecl *D) { void DeclPrinter::VisitEnumConstantDecl(EnumConstantDecl *D) { Out << *D; - if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + if (std::string Attrs = prettyPrintAttributes(D); !Attrs.empty()) { + Out << " " << Attrs; } if (Expr *Init = D->getInitExpr()) { Out << " = "; @@ -683,10 +674,9 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { if (!D->getDescribedFunctionTemplate() && !D->isFunctionTemplateSpecialization()) { prettyPrintPragmas(D); - if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Left); + if (std::string Attrs = prettyPrintAttributes(D, AttrPosAsWritten::Left); !Attrs.empty()) { - prettyPrintAttributes(Attrs); - Out << " "; + Out << Attrs << " "; } } @@ -859,10 +849,9 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { Ty.print(Out, Policy, Proto); } - if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Right); + if (std::string Attrs = prettyPrintAttributes(D, AttrPosAsWritten::Right); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + Out << " " << Attrs; } if (D->isPureVirtual()) @@ -955,9 +944,8 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) { Out << " = "; Init->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context); } - if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + if (std::string Attrs = prettyPrintAttributes(D); !Attrs.empty()) { + Out << " " << Attrs; } } @@ -968,10 +956,9 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) { void DeclPrinter::VisitVarDecl(VarDecl *D) { prettyPrintPragmas(D); - if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Left); + if (std::string Attrs = prettyPrintAttributes(D, AttrPosAsWritten::Left); !Attrs.empty()) { - prettyPrintAttributes(Attrs); - Out << " "; + Out << Attrs << " "; } if (const auto *Param = dyn_cast<ParmVarDecl>(D); @@ -1015,10 +1002,9 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { ? D->getIdentifier()->deuglifiedName() : D->getName()); - if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Right); + if (std::string Attrs = prettyPrintAttributes(D, AttrPosAsWritten::Right); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + Out << " " << Attrs; } Expr *Init = D->getInit(); @@ -1111,8 +1097,8 @@ void DeclPrinter::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { } void DeclPrinter::VisitEmptyDecl(EmptyDecl *D) { - if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { - prettyPrintAttributes(Attrs); + if (std::string Attrs = prettyPrintAttributes(D); !Attrs.empty()) { + Out << Attrs; } } @@ -1125,10 +1111,9 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { // FIXME: Move before printing the decl kind to match the behavior of the // attribute printing for variables and function where they are printed first. - if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Left); + if (std::string Attrs = prettyPrintAttributes(D, AttrPosAsWritten::Left); !Attrs.empty()) { - prettyPrintAttributes(Attrs); - Out << " "; + Out << Attrs << " "; } if (D->getIdentifier()) { @@ -1147,10 +1132,9 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { } } - if (AttrVec Attrs = getPrintableAttributes(D, AttrPosAsWritten::Right); + if (std::string Attrs = prettyPrintAttributes(D, AttrPosAsWritten::Right); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + Out << " " << Attrs; } if (D->isCompleteDefinition()) { @@ -1468,9 +1452,8 @@ void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) { if (OMD->isVariadic()) Out << ", ..."; - if (AttrVec Attrs = getPrintableAttributes(OMD); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + if (std::string Attrs = prettyPrintAttributes(OMD); !Attrs.empty()) { + Out << " " << Attrs; } if (OMD->getBody() && !Policy.TerseOutput) { @@ -1527,9 +1510,8 @@ void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) { return; } bool eolnOut = false; - if (AttrVec Attrs = getPrintableAttributes(OID); !Attrs.empty()) { - prettyPrintAttributes(Attrs); - Out << "\n"; + if (std::string Attrs = prettyPrintAttributes(OID); !Attrs.empty()) { + Out << Attrs << "\n"; } Out << "@interface " << I; @@ -1827,9 +1809,8 @@ void DeclPrinter::VisitHLSLBufferDecl(HLSLBufferDecl *D) { Out << *D; - if (AttrVec Attrs = getPrintableAttributes(D); !Attrs.empty()) { - Out << " "; - prettyPrintAttributes(Attrs); + if (std::string Attrs = prettyPrintAttributes(D); !Attrs.empty()) { + Out << " " << Attrs; } Out << " {\n"; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
