Skip to content

Commit 9391ff8

Browse files
committed
Reland "Rework the printing of attributes (#87281)"
Original commit message: " 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 " The reason for the bot breakage is that attributes coming from ApiNotes are not marked implicit even though they do not have source locations. This caused an assert to trigger. This patch forces attributes with no source location information to be printed on the left. That change is consistent to the overall intent of the change to increase the chances for attributes to compile across toolchains and at the same time the produced code to be as close as possible to the one written by the user.
1 parent dbb9749 commit 9391ff8

19 files changed

+102
-240
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: 47 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 { Default = 0, 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,48 @@ 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::Left;
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+
const AttrVec &Attrs = D->getAttrs();
306262
for (auto *A : Attrs) {
307263
if (A->isInherited() || A->isImplicit())
308264
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;
265+
switch (A->getKind()) {
266+
#define ATTR(X)
267+
#define PRAGMA_SPELLING_ATTR(X) case attr::X:
268+
#include "clang/Basic/AttrList.inc"
269+
break;
270+
default:
271+
AttrPosAsWritten APos = getPosAsWritten(A, D);
272+
assert(APos != AttrPosAsWritten::Default &&
273+
"Default not a valid for an attribute location");
274+
if (Pos == AttrPosAsWritten::Default || Pos == APos) {
275+
if (Pos != AttrPosAsWritten::Left)
276+
Out << ' ';
277+
A->printPretty(Out, Policy);
278+
if (Pos == AttrPosAsWritten::Left)
279+
Out << ' ';
280+
}
281+
break;
330282
}
331-
// Only print the side matches the user requested.
332-
if ((Loc & AttrLoc) != AttrPrintLoc::None)
333-
A->printPretty(Out, Policy);
334283
}
335284
}
336285
}
@@ -691,8 +640,10 @@ static void MaybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy,
691640

692641
void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
693642
if (!D->getDescribedFunctionTemplate() &&
694-
!D->isFunctionTemplateSpecialization())
643+
!D->isFunctionTemplateSpecialization()) {
695644
prettyPrintPragmas(D);
645+
prettyPrintAttributes(D, AttrPosAsWritten::Left);
646+
}
696647

697648
if (D->isFunctionTemplateSpecialization())
698649
Out << "template<> ";
@@ -702,22 +653,6 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
702653
printTemplateParameters(D->getTemplateParameterList(I));
703654
}
704655

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-
721656
CXXConstructorDecl *CDecl = dyn_cast<CXXConstructorDecl>(D);
722657
CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D);
723658
CXXDeductionGuideDecl *GuideDecl = dyn_cast<CXXDeductionGuideDecl>(D);
@@ -883,7 +818,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
883818
Ty.print(Out, Policy, Proto);
884819
}
885820

886-
prettyPrintAttributes(D, Out, AttrPrintLoc::Right);
821+
prettyPrintAttributes(D, AttrPosAsWritten::Right);
887822

888823
if (D->isPureVirtual())
889824
Out << " = 0";
@@ -976,27 +911,12 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) {
976911
void DeclPrinter::VisitVarDecl(VarDecl *D) {
977912
prettyPrintPragmas(D);
978913

914+
prettyPrintAttributes(D, AttrPosAsWritten::Left);
915+
979916
if (const auto *Param = dyn_cast<ParmVarDecl>(D);
980917
Param && Param->isExplicitObjectParameter())
981918
Out << "this ";
982919

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-
1000920
QualType T = D->getTypeSourceInfo()
1001921
? D->getTypeSourceInfo()->getType()
1002922
: D->getASTContext().getUnqualifiedObjCPointerType(D->getType());
@@ -1029,21 +949,16 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
1029949
}
1030950
}
1031951

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

1044-
// Print the attributes that should be placed right before the end of the
1045-
// decl.
1046-
prettyPrintAttributes(D, Out, AttrPrintLoc::Right);
956+
printDeclType(T, (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters &&
957+
D->getIdentifier())
958+
? D->getIdentifier()->deuglifiedName()
959+
: D->getName());
960+
961+
prettyPrintAttributes(D, AttrPosAsWritten::Right);
1047962

1048963
Expr *Init = D->getInit();
1049964
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/APINotes/retain-count-convention.m

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55

66
#import <SimpleKit/SimpleKit.h>
77

8-
// CHECK: void *getCFOwnedToUnowned(void) __attribute__((cf_returns_not_retained));
9-
// CHECK: void *getCFUnownedToOwned(void) __attribute__((cf_returns_retained));
10-
// CHECK: void *getCFOwnedToNone(void) __attribute__((cf_unknown_transfer));
11-
// CHECK: id getObjCOwnedToUnowned(void) __attribute__((ns_returns_not_retained));
12-
// CHECK: id getObjCUnownedToOwned(void) __attribute__((ns_returns_retained));
13-
// CHECK: int indirectGetCFOwnedToUnowned(void * _Nullable *out __attribute__((cf_returns_not_retained)));
14-
// CHECK: int indirectGetCFUnownedToOwned(void * _Nullable *out __attribute__((cf_returns_retained)));
8+
// CHECK: __attribute__((cf_returns_not_retained)) void *getCFOwnedToUnowned(void);
9+
// CHECK: __attribute__((cf_returns_retained)) void *getCFUnownedToOwned(void);
10+
// CHECK: __attribute__((cf_unknown_transfer)) void *getCFOwnedToNone(void);
11+
// CHECK: __attribute__((ns_returns_not_retained)) id getObjCOwnedToUnowned(void);
12+
// CHECK: __attribute__((ns_returns_retained)) id getObjCUnownedToOwned(void);
13+
// CHECK: int indirectGetCFOwnedToUnowned(__attribute__((cf_returns_not_retained)) void * _Nullable *out);
14+
// CHECK: int indirectGetCFUnownedToOwned(__attribute__((cf_returns_retained)) void * _Nullable *out);
1515
// CHECK: int indirectGetCFOwnedToNone(void * _Nullable *out);
16-
// CHECK: int indirectGetCFNoneToOwned(void **out __attribute__((cf_returns_not_retained)));
16+
// CHECK: int indirectGetCFNoneToOwned(__attribute__((cf_returns_not_retained)) void **out);
1717

1818
// CHECK-LABEL: @interface MethodTest
1919
// CHECK: - (id)getOwnedToUnowned __attribute__((ns_returns_not_retained));

clang/test/APINotes/versioned.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#import <VersionedKit/VersionedKit.h>
1414

1515
// CHECK-UNVERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)")));
16-
// CHECK-VERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(a:b:)")));
16+
// CHECK-VERSIONED:__attribute__((swift_name("moveTo(a:b:)"))) void moveToPointDUMP(double x, double y);
1717

1818
// CHECK-DUMP-LABEL: Dumping moveToPointDUMP
1919
// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}}
@@ -65,7 +65,7 @@
6565

6666
// CHECK-DUMP-NOT: Dumping
6767

68-
// CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape)));
68+
// CHECK-UNVERSIONED: void acceptClosure(__attribute__((noescape)) void (^block)(void));
6969
// CHECK-VERSIONED: void acceptClosure(void (^block)(void));
7070

7171
// CHECK-UNVERSIONED: void privateFunc(void) __attribute__((swift_private));

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: };

0 commit comments

Comments
 (0)