[clang] 41079da - Control spacing for attribute printing (#174197)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 10 10:43:16 PST 2026
Author: Kim Gräsman
Date: 2026-01-10T20:43:11+02:00
New Revision: 41079dac98ece04a3ee555049c5dd1d50523bb61
URL: https://github.com/llvm/llvm-project/commit/41079dac98ece04a3ee555049c5dd1d50523bb61
DIFF: https://github.com/llvm/llvm-project/commit/41079dac98ece04a3ee555049c5dd1d50523bb61.diff
LOG: Control spacing for attribute printing (#174197)
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, have it return an
`optional<string>` which is either the pretty-printed attributes for
Pos,
or `nullopt` if no attributes were found.
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.
Added:
Modified:
clang/lib/AST/DeclPrinter.cpp
clang/test/SemaCXX/cxx11-attr-print.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 47ae613b643b6..205b6a53f80e5 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,10 @@ namespace {
void printTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
const TemplateParameterList *Params);
enum class AttrPosAsWritten { Default = 0, Left, Right };
- bool
+ std::optional<std::string>
prettyPrintAttributes(const Decl *D,
AttrPosAsWritten Pos = AttrPosAsWritten::Default);
+
void prettyPrintPragmas(Decl *D);
void printDeclType(QualType T, StringRef DeclName, bool Pack = false);
};
@@ -252,41 +254,40 @@ 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;
+std::optional<std::string>
+DeclPrinter::prettyPrintAttributes(const Decl *D,
+ AttrPosAsWritten Pos /*=Default*/) {
+ if (!D->hasAttrs())
+ return std::nullopt;
- 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()) {
+ std::string AttrStr;
+ llvm::raw_string_ostream AOut(AttrStr);
+ llvm::ListSeparator LS(" ");
+ 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) {
+ AOut << LS;
+ A->printPretty(AOut, Policy);
}
+ break;
}
}
- return hasPrinted;
+ if (AttrStr.empty())
+ return std::nullopt;
+ return AttrStr;
}
void DeclPrinter::PrintOpenACCRoutineOnLambda(Decl *D) {
@@ -584,12 +585,15 @@ void DeclPrinter::VisitTypedefDecl(TypedefDecl *D) {
}
QualType Ty = D->getTypeSourceInfo()->getType();
Ty.print(Out, Policy, D->getName(), Indentation);
- prettyPrintAttributes(D);
+
+ if (std::optional<std::string> Attrs = prettyPrintAttributes(D))
+ Out << ' ' << *Attrs;
}
void DeclPrinter::VisitTypeAliasDecl(TypeAliasDecl *D) {
Out << "using " << *D;
- prettyPrintAttributes(D);
+ if (std::optional<std::string> Attrs = prettyPrintAttributes(D))
+ Out << ' ' << *Attrs;
Out << " = " << D->getTypeSourceInfo()->getType().getAsString(Policy);
}
@@ -604,7 +608,8 @@ void DeclPrinter::VisitEnumDecl(EnumDecl *D) {
Out << " struct";
}
- prettyPrintAttributes(D);
+ if (std::optional<std::string> Attrs = prettyPrintAttributes(D))
+ Out << ' ' << *Attrs;
if (D->getDeclName())
Out << ' ' << D->getDeclName();
@@ -624,7 +629,8 @@ void DeclPrinter::VisitRecordDecl(RecordDecl *D) {
Out << "__module_private__ ";
Out << D->getKindName();
- prettyPrintAttributes(D);
+ if (std::optional<std::string> Attrs = prettyPrintAttributes(D))
+ Out << ' ' << *Attrs;
if (D->getIdentifier())
Out << ' ' << *D;
@@ -638,7 +644,8 @@ void DeclPrinter::VisitRecordDecl(RecordDecl *D) {
void DeclPrinter::VisitEnumConstantDecl(EnumConstantDecl *D) {
Out << *D;
- prettyPrintAttributes(D);
+ if (std::optional<std::string> Attrs = prettyPrintAttributes(D))
+ Out << ' ' << *Attrs;
if (Expr *Init = D->getInitExpr()) {
Out << " = ";
Init->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context);
@@ -664,7 +671,9 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
if (!D->getDescribedFunctionTemplate() &&
!D->isFunctionTemplateSpecialization()) {
prettyPrintPragmas(D);
- prettyPrintAttributes(D, AttrPosAsWritten::Left);
+ if (std::optional<std::string> Attrs =
+ prettyPrintAttributes(D, AttrPosAsWritten::Left))
+ Out << *Attrs << ' ';
}
if (D->isFunctionTemplateSpecialization())
@@ -836,7 +845,9 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
Ty.print(Out, Policy, Proto);
}
- prettyPrintAttributes(D, AttrPosAsWritten::Right);
+ if (std::optional<std::string> Attrs =
+ prettyPrintAttributes(D, AttrPosAsWritten::Right))
+ Out << ' ' << *Attrs;
if (D->isPureVirtual())
Out << " = 0";
@@ -928,7 +939,8 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
Out << " = ";
Init->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context);
}
- prettyPrintAttributes(D);
+ if (std::optional<std::string> Attrs = prettyPrintAttributes(D))
+ Out << ' ' << *Attrs;
}
void DeclPrinter::VisitLabelDecl(LabelDecl *D) {
@@ -938,7 +950,9 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) {
void DeclPrinter::VisitVarDecl(VarDecl *D) {
prettyPrintPragmas(D);
- prettyPrintAttributes(D, AttrPosAsWritten::Left);
+ if (std::optional<std::string> Attrs =
+ prettyPrintAttributes(D, AttrPosAsWritten::Left))
+ Out << *Attrs << ' ';
if (const auto *Param = dyn_cast<ParmVarDecl>(D);
Param && Param->isExplicitObjectParameter())
@@ -981,7 +995,9 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
? D->getIdentifier()->deuglifiedName()
: D->getName());
- prettyPrintAttributes(D, AttrPosAsWritten::Right);
+ if (std::optional<std::string> Attrs =
+ prettyPrintAttributes(D, AttrPosAsWritten::Right))
+ Out << ' ' << *Attrs;
Expr *Init = D->getInit();
if (!Policy.SuppressInitializers && Init) {
@@ -1073,7 +1089,8 @@ void DeclPrinter::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) {
}
void DeclPrinter::VisitEmptyDecl(EmptyDecl *D) {
- prettyPrintAttributes(D);
+ if (std::optional<std::string> Attrs = prettyPrintAttributes(D))
+ Out << *Attrs;
}
void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
@@ -1085,8 +1102,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 (prettyPrintAttributes(D, AttrPosAsWritten::Left))
- Out << ' ';
+ if (std::optional<std::string> Attrs =
+ prettyPrintAttributes(D, AttrPosAsWritten::Left))
+ Out << *Attrs << ' ';
if (D->getIdentifier()) {
D->getQualifier().print(Out, Policy);
@@ -1104,7 +1122,9 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
}
}
- prettyPrintAttributes(D, AttrPosAsWritten::Right);
+ if (std::optional<std::string> Attrs =
+ prettyPrintAttributes(D, AttrPosAsWritten::Right))
+ Out << ' ' << *Attrs;
if (D->isCompleteDefinition()) {
Out << ' ';
@@ -1421,7 +1441,8 @@ void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) {
if (OMD->isVariadic())
Out << ", ...";
- prettyPrintAttributes(OMD);
+ if (std::optional<std::string> Attrs = prettyPrintAttributes(OMD))
+ Out << ' ' << *Attrs;
if (OMD->getBody() && !Policy.TerseOutput) {
Out << ' ';
@@ -1477,10 +1498,8 @@ void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) {
return;
}
bool eolnOut = false;
- if (OID->hasAttrs()) {
- prettyPrintAttributes(OID);
- Out << "\n";
- }
+ if (std::optional<std::string> Attrs = prettyPrintAttributes(OID))
+ Out << *Attrs << "\n";
Out << "@interface " << I;
@@ -1777,7 +1796,8 @@ void DeclPrinter::VisitHLSLBufferDecl(HLSLBufferDecl *D) {
Out << *D;
- prettyPrintAttributes(D);
+ if (std::optional<std::string> Attrs = prettyPrintAttributes(D))
+ Out << ' ' << *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 {
More information about the cfe-commits
mailing list