Skip to content

Commit a30662f

Browse files
authored
Rework the printing of attributes (#87281)
Commit 46f3ade introduced a notion of printing the attributes on the left to improve the printing of attributes attached to variable declarations. The intent was to produce more GCC compatible code because clang tends to print the attributes on the right hand side which is not accepted by gcc. This approach has increased the complexity in tablegen and the attrubutes themselves as now the are supposed to know where they could appear. That lead to mishandling of the `override` keyword which is modelled as an attribute in clang. This patch takes an inspiration from the existing approach and tries to keep the position of the attributes as they were written. To do so we use simpler heuristic which checks if the source locations of the attribute precedes the declaration. If so, it is considered to be printed before the declaration. Fixes #87151
1 parent 04f33a3 commit a30662f

17 files changed

+97
-230
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -324,13 +324,10 @@ class Spelling<string name, string variety, int version = 1> {
324324
}
325325

326326
class GNU<string name> : Spelling<name, "GNU">;
327-
class Declspec<string name> : Spelling<name, "Declspec"> {
328-
bit PrintOnLeft = 1;
329-
}
327+
class Declspec<string name> : Spelling<name, "Declspec">;
330328
class Microsoft<string name> : Spelling<name, "Microsoft">;
331329
class CXX11<string namespace, string name, int version = 1>
332330
: Spelling<name, "CXX11", version> {
333-
bit CanPrintOnLeft = 0;
334331
string Namespace = namespace;
335332
}
336333
class C23<string namespace, string name, int version = 1>
@@ -596,12 +593,6 @@ class AttrSubjectMatcherAggregateRule<AttrSubject subject> {
596593
def SubjectMatcherForNamed : AttrSubjectMatcherAggregateRule<Named>;
597594

598595
class Attr {
599-
// Specifies that when printed, this attribute is meaningful on the
600-
// 'left side' of the declaration.
601-
bit CanPrintOnLeft = 1;
602-
// Specifies that when printed, this attribute is required to be printed on
603-
// the 'left side' of the declaration.
604-
bit PrintOnLeft = 0;
605596
// The various ways in which an attribute can be spelled in source
606597
list<Spelling> Spellings;
607598
// The things to which an attribute can appertain
@@ -937,7 +928,6 @@ def AVRSignal : InheritableAttr, TargetSpecificAttr<TargetAVR> {
937928
}
938929

939930
def AsmLabel : InheritableAttr {
940-
let CanPrintOnLeft = 0;
941931
let Spellings = [CustomKeyword<"asm">, CustomKeyword<"__asm__">];
942932
let Args = [
943933
// Label specifies the mangled name for the decl.
@@ -1534,7 +1524,6 @@ def AllocSize : InheritableAttr {
15341524
}
15351525

15361526
def EnableIf : InheritableAttr {
1537-
let CanPrintOnLeft = 0;
15381527
// Does not have a [[]] spelling because this attribute requires the ability
15391528
// to parse function arguments but the attribute is not written in the type
15401529
// position.
@@ -3171,7 +3160,6 @@ def Unavailable : InheritableAttr {
31713160
}
31723161

31733162
def DiagnoseIf : InheritableAttr {
3174-
let CanPrintOnLeft = 0;
31753163
// Does not have a [[]] spelling because this attribute requires the ability
31763164
// to parse function arguments but the attribute is not written in the type
31773165
// position.

clang/include/clang/Basic/CMakeLists.txt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,6 @@ clang_tablegen(AttrList.inc -gen-clang-attr-list
3131
SOURCE Attr.td
3232
TARGET ClangAttrList)
3333

34-
clang_tablegen(AttrLeftSideCanPrintList.inc -gen-clang-attr-can-print-left-list
35-
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
36-
SOURCE Attr.td
37-
TARGET ClangAttrCanPrintLeftList)
38-
39-
clang_tablegen(AttrLeftSideMustPrintList.inc -gen-clang-attr-must-print-left-list
40-
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
41-
SOURCE Attr.td
42-
TARGET ClangAttrMustPrintLeftList)
43-
4434
clang_tablegen(AttrSubMatchRulesList.inc -gen-clang-attr-subject-match-rule-list
4535
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
4636
SOURCE Attr.td

clang/lib/AST/DeclPrinter.cpp

Lines changed: 52 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "clang/AST/ExprCXX.h"
2222
#include "clang/AST/PrettyPrinter.h"
2323
#include "clang/Basic/Module.h"
24+
#include "clang/Basic/SourceManager.h"
2425
#include "llvm/Support/raw_ostream.h"
2526
using namespace clang;
2627

@@ -49,18 +50,6 @@ namespace {
4950

5051
void PrintObjCTypeParams(ObjCTypeParamList *Params);
5152

52-
enum class AttrPrintLoc {
53-
None = 0,
54-
Left = 1,
55-
Right = 2,
56-
Any = Left | Right,
57-
58-
LLVM_MARK_AS_BITMASK_ENUM(/*DefaultValue=*/Any)
59-
};
60-
61-
void prettyPrintAttributes(Decl *D, raw_ostream &out,
62-
AttrPrintLoc loc = AttrPrintLoc::Any);
63-
6453
public:
6554
DeclPrinter(raw_ostream &Out, const PrintingPolicy &Policy,
6655
const ASTContext &Context, unsigned Indentation = 0,
@@ -129,11 +118,10 @@ namespace {
129118
const TemplateParameterList *Params);
130119
void printTemplateArguments(llvm::ArrayRef<TemplateArgumentLoc> Args,
131120
const TemplateParameterList *Params);
132-
133-
inline void prettyPrintAttributes(Decl *D) {
134-
prettyPrintAttributes(D, Out);
135-
}
136-
121+
enum class AttrPosAsWritten { Unknown = 0, Default, Left, Right };
122+
void
123+
prettyPrintAttributes(const Decl *D,
124+
AttrPosAsWritten Pos = AttrPosAsWritten::Default);
137125
void prettyPrintPragmas(Decl *D);
138126
void printDeclType(QualType T, StringRef DeclName, bool Pack = false);
139127
};
@@ -250,87 +238,53 @@ raw_ostream& DeclPrinter::Indent(unsigned Indentation) {
250238
return Out;
251239
}
252240

253-
// For CLANG_ATTR_LIST_CanPrintOnLeft macro.
254-
#include "clang/Basic/AttrLeftSideCanPrintList.inc"
241+
static DeclPrinter::AttrPosAsWritten getPosAsWritten(const Attr *A,
242+
const Decl *D) {
243+
SourceLocation ALoc = A->getLoc();
244+
SourceLocation DLoc = D->getLocation();
245+
const ASTContext &C = D->getASTContext();
246+
if (ALoc.isInvalid() || DLoc.isInvalid())
247+
return DeclPrinter::AttrPosAsWritten::Unknown;
255248

256-
// For CLANG_ATTR_LIST_PrintOnLeft macro.
257-
#include "clang/Basic/AttrLeftSideMustPrintList.inc"
249+
if (C.getSourceManager().isBeforeInTranslationUnit(ALoc, DLoc))
250+
return DeclPrinter::AttrPosAsWritten::Left;
258251

259-
static bool canPrintOnLeftSide(attr::Kind kind) {
260-
#ifdef CLANG_ATTR_LIST_CanPrintOnLeft
261-
switch (kind) {
262-
CLANG_ATTR_LIST_CanPrintOnLeft
263-
return true;
264-
default:
265-
return false;
266-
}
267-
#else
268-
return false;
269-
#endif
270-
}
271-
272-
static bool canPrintOnLeftSide(const Attr *A) {
273-
if (A->isStandardAttributeSyntax())
274-
return false;
275-
276-
return canPrintOnLeftSide(A->getKind());
252+
return DeclPrinter::AttrPosAsWritten::Right;
277253
}
278254

279-
static bool mustPrintOnLeftSide(attr::Kind kind) {
280-
#ifdef CLANG_ATTR_LIST_PrintOnLeft
281-
switch (kind) {
282-
CLANG_ATTR_LIST_PrintOnLeft
283-
return true;
284-
default:
285-
return false;
286-
}
287-
#else
288-
return false;
289-
#endif
290-
}
291-
292-
static bool mustPrintOnLeftSide(const Attr *A) {
293-
if (A->isDeclspecAttribute())
294-
return true;
295-
296-
return mustPrintOnLeftSide(A->getKind());
297-
}
298-
299-
void DeclPrinter::prettyPrintAttributes(Decl *D, llvm::raw_ostream &Out,
300-
AttrPrintLoc Loc) {
255+
void DeclPrinter::prettyPrintAttributes(const Decl *D,
256+
AttrPosAsWritten Pos /*=Default*/) {
301257
if (Policy.PolishForDeclaration)
302258
return;
303259

304260
if (D->hasAttrs()) {
305-
AttrVec &Attrs = D->getAttrs();
261+
assert(Pos != AttrPosAsWritten::Unknown && "Use Default");
262+
const AttrVec &Attrs = D->getAttrs();
306263
for (auto *A : Attrs) {
307264
if (A->isInherited() || A->isImplicit())
308265
continue;
309-
310-
AttrPrintLoc AttrLoc = AttrPrintLoc::Right;
311-
if (mustPrintOnLeftSide(A)) {
312-
// If we must always print on left side (e.g. declspec), then mark as
313-
// so.
314-
AttrLoc = AttrPrintLoc::Left;
315-
} else if (canPrintOnLeftSide(A)) {
316-
// For functions with body defined we print the attributes on the left
317-
// side so that GCC accept our dumps as well.
318-
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
319-
FD && FD->isThisDeclarationADefinition())
320-
// In case Decl is a function with a body, then attrs should be print
321-
// on the left side.
322-
AttrLoc = AttrPrintLoc::Left;
323-
324-
// In case it is a variable declaration with a ctor, then allow
325-
// printing on the left side for readbility.
326-
else if (const VarDecl *VD = dyn_cast<VarDecl>(D);
327-
VD && VD->getInit() &&
328-
VD->getInitStyle() == VarDecl::CallInit)
329-
AttrLoc = AttrPrintLoc::Left;
266+
switch (A->getKind()) {
267+
#define ATTR(X)
268+
#define PRAGMA_SPELLING_ATTR(X) case attr::X:
269+
#include "clang/Basic/AttrList.inc"
270+
break;
271+
default:
272+
AttrPosAsWritten APos = getPosAsWritten(A, D);
273+
// Might trigger on programatically created attributes or declarations
274+
// with no source locations.
275+
assert(APos != AttrPosAsWritten::Unknown &&
276+
"Invalid source location for attribute or decl.");
277+
assert(APos != AttrPosAsWritten::Default &&
278+
"Default not a valid for an attribute location");
279+
if (Pos == AttrPosAsWritten::Default || Pos == APos) {
280+
if (Pos != AttrPosAsWritten::Left)
281+
Out << ' ';
282+
A->printPretty(Out, Policy);
283+
if (Pos == AttrPosAsWritten::Left)
284+
Out << ' ';
285+
}
286+
break;
330287
}
331-
// Only print the side matches the user requested.
332-
if ((Loc & AttrLoc) != AttrPrintLoc::None)
333-
A->printPretty(Out, Policy);
334288
}
335289
}
336290
}
@@ -691,8 +645,10 @@ static void MaybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy,
691645

692646
void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
693647
if (!D->getDescribedFunctionTemplate() &&
694-
!D->isFunctionTemplateSpecialization())
648+
!D->isFunctionTemplateSpecialization()) {
695649
prettyPrintPragmas(D);
650+
prettyPrintAttributes(D, AttrPosAsWritten::Left);
651+
}
696652

697653
if (D->isFunctionTemplateSpecialization())
698654
Out << "template<> ";
@@ -702,22 +658,6 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
702658
printTemplateParameters(D->getTemplateParameterList(I));
703659
}
704660

705-
std::string LeftsideAttrs;
706-
llvm::raw_string_ostream LSAS(LeftsideAttrs);
707-
708-
prettyPrintAttributes(D, LSAS, AttrPrintLoc::Left);
709-
710-
// prettyPrintAttributes print a space on left side of the attribute.
711-
if (LeftsideAttrs[0] == ' ') {
712-
// Skip the space prettyPrintAttributes generated.
713-
LeftsideAttrs.erase(0, LeftsideAttrs.find_first_not_of(' '));
714-
715-
// Add a single space between the attribute and the Decl name.
716-
LSAS << ' ';
717-
}
718-
719-
Out << LeftsideAttrs;
720-
721661
CXXConstructorDecl *CDecl = dyn_cast<CXXConstructorDecl>(D);
722662
CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D);
723663
CXXDeductionGuideDecl *GuideDecl = dyn_cast<CXXDeductionGuideDecl>(D);
@@ -883,7 +823,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
883823
Ty.print(Out, Policy, Proto);
884824
}
885825

886-
prettyPrintAttributes(D, Out, AttrPrintLoc::Right);
826+
prettyPrintAttributes(D, AttrPosAsWritten::Right);
887827

888828
if (D->isPureVirtual())
889829
Out << " = 0";
@@ -976,27 +916,12 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) {
976916
void DeclPrinter::VisitVarDecl(VarDecl *D) {
977917
prettyPrintPragmas(D);
978918

919+
prettyPrintAttributes(D, AttrPosAsWritten::Left);
920+
979921
if (const auto *Param = dyn_cast<ParmVarDecl>(D);
980922
Param && Param->isExplicitObjectParameter())
981923
Out << "this ";
982924

983-
std::string LeftSide;
984-
llvm::raw_string_ostream LeftSideStream(LeftSide);
985-
986-
// Print attributes that should be placed on the left, such as __declspec.
987-
prettyPrintAttributes(D, LeftSideStream, AttrPrintLoc::Left);
988-
989-
// prettyPrintAttributes print a space on left side of the attribute.
990-
if (LeftSide[0] == ' ') {
991-
// Skip the space prettyPrintAttributes generated.
992-
LeftSide.erase(0, LeftSide.find_first_not_of(' '));
993-
994-
// Add a single space between the attribute and the Decl name.
995-
LeftSideStream << ' ';
996-
}
997-
998-
Out << LeftSide;
999-
1000925
QualType T = D->getTypeSourceInfo()
1001926
? D->getTypeSourceInfo()->getType()
1002927
: D->getASTContext().getUnqualifiedObjCPointerType(D->getType());
@@ -1029,21 +954,16 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
1029954
}
1030955
}
1031956

1032-
StringRef Name;
1033-
1034-
Name = (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters &&
1035-
D->getIdentifier())
1036-
? D->getIdentifier()->deuglifiedName()
1037-
: D->getName();
1038-
1039957
if (!Policy.SuppressTagKeyword && Policy.SuppressScope &&
1040958
!Policy.SuppressUnwrittenScope)
1041959
MaybePrintTagKeywordIfSupressingScopes(Policy, T, Out);
1042-
printDeclType(T, Name);
1043960

1044-
// Print the attributes that should be placed right before the end of the
1045-
// decl.
1046-
prettyPrintAttributes(D, Out, AttrPrintLoc::Right);
961+
printDeclType(T, (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters &&
962+
D->getIdentifier())
963+
? D->getIdentifier()->deuglifiedName()
964+
: D->getName());
965+
966+
prettyPrintAttributes(D, AttrPosAsWritten::Right);
1047967

1048968
Expr *Init = D->getInit();
1049969
if (!Policy.SuppressInitializers && Init) {

clang/lib/AST/StmtPrinter.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,11 @@ void StmtPrinter::VisitLabelStmt(LabelStmt *Node) {
292292
}
293293

294294
void StmtPrinter::VisitAttributedStmt(AttributedStmt *Node) {
295-
for (const auto *Attr : Node->getAttrs()) {
295+
llvm::ArrayRef<const Attr *> Attrs = Node->getAttrs();
296+
for (const auto *Attr : Attrs) {
296297
Attr->printPretty(OS, Policy);
298+
if (Attr != Attrs.back())
299+
OS << ' ';
297300
}
298301

299302
PrintStmt(Node->getSubStmt(), 0);

clang/test/AST/ast-print-method-decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ struct DefMethodsWithoutBody {
9494
// CHECK-NEXT: DefMethodsWithoutBody() = default;
9595
~DefMethodsWithoutBody() = default;
9696

97-
// CHECK-NEXT: __attribute__((alias("X"))) void m1();
97+
// CHECK-NEXT: void m1() __attribute__((alias("X")));
9898
void m1() __attribute__((alias("X")));
9999

100100
// CHECK-NEXT: };

clang/test/AST/ast-print-no-sanitize.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ void should_not_crash_1() __attribute__((no_sanitize_memory));
44
[[clang::no_sanitize_memory]] void should_not_crash_2();
55

66
// CHECK: void should_not_crash_1() __attribute__((no_sanitize("memory")));
7-
// CHECK: void should_not_crash_2() {{\[\[}}clang::no_sanitize("memory"){{\]\]}};
7+
// CHECK: {{\[\[}}clang::no_sanitize("memory"){{\]\]}} void should_not_crash_2();

clang/test/AST/attr-print-emit.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,18 @@ class C {
7373
// CHECK: void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 2, 3)));
7474
void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 2, 3)));
7575
};
76+
77+
#define ANNOTATE_ATTR __attribute__((annotate("Annotated")))
78+
ANNOTATE_ATTR int annotated_attr ANNOTATE_ATTR = 0;
79+
// CHECK: __attribute__((annotate("Annotated"))) int annotated_attr __attribute__((annotate("Annotated"))) = 0;
80+
81+
// FIXME: We do not print the attribute as written after the type specifier.
82+
int ANNOTATE_ATTR annotated_attr_fixme = 0;
83+
// CHECK: __attribute__((annotate("Annotated"))) int annotated_attr_fixme = 0;
84+
85+
#define NONNULL_ATTR __attribute__((nonnull(1)))
86+
ANNOTATE_ATTR NONNULL_ATTR void fn_non_null_annotated_attr(int *) __attribute__((annotate("AnnotatedRHS")));
87+
// CHECK:__attribute__((annotate("Annotated"))) __attribute__((nonnull(1))) void fn_non_null_annotated_attr(int *) __attribute__((annotate("AnnotatedRHS")));
88+
89+
[[gnu::nonnull(1)]] [[gnu::always_inline]] void cxx11_attr(int*) ANNOTATE_ATTR;
90+
// CHECK: {{\[\[}}gnu::nonnull(1)]] {{\[\[}}gnu::always_inline]] void cxx11_attr(int *) __attribute__((annotate("Annotated")));

clang/test/Analysis/scopes-cfg-output.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1469,7 +1469,7 @@ void test_cleanup_functions2(int m) {
14691469
// CHECK: [B1]
14701470
// CHECK-NEXT: 1: CFGScopeBegin(f)
14711471
// CHECK-NEXT: 2: (CXXConstructExpr, [B1.3], F)
1472-
// CHECK-NEXT: 3: __attribute__((cleanup(cleanup_F))) F f;
1472+
// CHECK-NEXT: 3: F f __attribute__((cleanup(cleanup_F)));
14731473
// CHECK-NEXT: 4: CleanupFunction (cleanup_F)
14741474
// CHECK-NEXT: 5: [B1.3].~F() (Implicit destructor)
14751475
// CHECK-NEXT: 6: CFGScopeEnd(f)

0 commit comments

Comments
 (0)