diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -56,6 +56,13 @@ : Out(Out), Policy(Policy), Context(Context), Indentation(Indentation), PrintInstantiation(PrintInstantiation) {} + enum class AttrLocation { + Unspecified, + BeforeDecl, + AfterDecl, + Ignore, + }; + void VisitDeclContext(DeclContext *DC, bool Indent = true); void VisitTranslationUnitDecl(TranslationUnitDecl *D); @@ -117,7 +124,9 @@ const TemplateParameterList *Params); void printTemplateArguments(llvm::ArrayRef Args, const TemplateParameterList *Params); - void prettyPrintAttributes(Decl *D); + AttrLocation getAttrLocation(Decl *D, Attr *A); + void prettyPrintAttributes(Decl *D, + AttrLocation AL = AttrLocation::Unspecified); void prettyPrintPragmas(Decl *D); void printDeclType(QualType T, StringRef DeclName, bool Pack = false); }; @@ -234,14 +243,45 @@ return Out; } -void DeclPrinter::prettyPrintAttributes(Decl *D) { +DeclPrinter::AttrLocation DeclPrinter::getAttrLocation(Decl *D, Attr *A) { + if (A->isInherited() || A->isImplicit()) + return AttrLocation::Ignore; + + if (auto *FD = dyn_cast(D)) { + switch (A->getKind()) { + case attr::AsmLabel: + case attr::Final: + case attr::Override: + return AttrLocation::AfterDecl; + + default: + break; + } + + auto V = A->getVariety(); + // C2x/C++11-style attributes must appear before the declarator. + if (V == Attr::Variety::CXX11 || V == Attr::Variety::C2x) + return AttrLocation::BeforeDecl; + + if (FD->doesThisDeclarationHaveABody()) { + return AttrLocation::BeforeDecl; + } else { + return AttrLocation::AfterDecl; + } + } else { + return AttrLocation::Unspecified; + } +} + +void DeclPrinter::prettyPrintAttributes(Decl *D, AttrLocation AL) { if (Policy.PolishForDeclaration) return; if (D->hasAttrs()) { AttrVec &Attrs = D->getAttrs(); + bool PrintAttr = false; for (auto *A : Attrs) { - if (A->isInherited() || A->isImplicit()) + if (getAttrLocation(D, A) != AL) continue; switch (A->getKind()) { #define ATTR(X) @@ -252,7 +292,11 @@ A->printPretty(Out, Policy); break; } + PrintAttr = true; } + + if (PrintAttr && AL == AttrLocation::BeforeDecl) + Out << " "; } } @@ -613,6 +657,9 @@ printTemplateParameters(D->getTemplateParameterList(I)); } + // [[...]] attributes must be printed before static, inline, virtual, etc. + prettyPrintAttributes(D, AttrLocation::BeforeDecl); + CXXConstructorDecl *CDecl = dyn_cast(D); CXXConversionDecl *ConversionDecl = dyn_cast(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(D); @@ -774,7 +821,7 @@ Ty.print(Out, Policy, Proto); } - prettyPrintAttributes(D); + prettyPrintAttributes(D, AttrLocation::AfterDecl); if (D->isPure()) Out << " = 0"; diff --git a/clang/test/AST/ast-print-no-sanitize.cpp b/clang/test/AST/ast-print-no-sanitize.cpp --- a/clang/test/AST/ast-print-no-sanitize.cpp +++ b/clang/test/AST/ast-print-no-sanitize.cpp @@ -4,4 +4,4 @@ [[clang::no_sanitize_memory]] void should_not_crash_2(); // CHECK: void should_not_crash_1() __attribute__((no_sanitize("memory"))); -// CHECK: void should_not_crash_2() {{\[\[}}clang::no_sanitize("memory"){{\]\]}}; +// CHECK: {{\[\[}}clang::no_sanitize("memory"){{\]\]}} void should_not_crash_2(); diff --git a/clang/test/OpenMP/assumes_codegen.cpp b/clang/test/OpenMP/assumes_codegen.cpp --- a/clang/test/OpenMP/assumes_codegen.cpp +++ b/clang/test/OpenMP/assumes_codegen.cpp @@ -67,41 +67,41 @@ } #pragma omp end assumes -// AST: void foo() __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST: __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) void foo() { // AST-NEXT: } // AST-NEXT: class BAR { // AST-NEXT: public: -// AST-NEXT: BAR() __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST-NEXT: __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) BAR() { // AST-NEXT: } -// AST-NEXT: void bar1() __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST-NEXT: __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) void bar1() { // AST-NEXT: } -// AST-NEXT: static void bar2() __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST-NEXT: __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) static void bar2() { // AST-NEXT: } // AST-NEXT: }; -// AST-NEXT: void bar() __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST-NEXT: __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) void bar() { // AST-NEXT: BAR b; // AST-NEXT: } // AST-NEXT: void baz() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))); // AST-NEXT: template class BAZ { // AST-NEXT: public: -// AST-NEXT: BAZ() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) BAZ() { // AST-NEXT: } -// AST-NEXT: void baz1() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) void baz1() { // AST-NEXT: } -// AST-NEXT: static void baz2() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) static void baz2() { // AST-NEXT: } // AST-NEXT: }; // AST-NEXT: template<> class BAZ { // AST-NEXT: public: -// AST-NEXT: BAZ() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) BAZ() { // AST-NEXT: } // AST-NEXT: void baz1() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))); // AST-NEXT: static void baz2() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))); // AST-NEXT: }; -// AST-NEXT: void baz() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) void baz() { // AST-NEXT: BAZ b; // AST-NEXT: } -// AST-NEXT: int lambda_outer() __attribute__((assume("ompx_lambda_assumption"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) { +// AST-NEXT: __attribute__((assume("ompx_lambda_assumption"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) int lambda_outer() { // AST-NEXT: auto lambda_inner = []() { // AST-NEXT: return 42; // AST-NEXT: }; diff --git a/clang/test/OpenMP/assumes_print.cpp b/clang/test/OpenMP/assumes_print.cpp --- a/clang/test/OpenMP/assumes_print.cpp +++ b/clang/test/OpenMP/assumes_print.cpp @@ -37,8 +37,8 @@ } #pragma omp end assumes -// CHECK: void foo() __attribute__((assume("omp_no_openmp_routines"))) __attribute__((assume("omp_no_openmp"))) -// CHECK: void bar() __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines"))) __attribute__((assume("omp_no_openmp"))) -// CHECK: void baz() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines"))) __attribute__((assume("omp_no_openmp"))) +// CHECK: __attribute__((assume("omp_no_openmp_routines"))) __attribute__((assume("omp_no_openmp"))) void foo() +// CHECK: __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines"))) __attribute__((assume("omp_no_openmp"))) void bar() +// CHECK: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines"))) __attribute__((assume("omp_no_openmp"))) void baz() #endif diff --git a/clang/test/OpenMP/assumes_template_print.cpp b/clang/test/OpenMP/assumes_template_print.cpp --- a/clang/test/OpenMP/assumes_template_print.cpp +++ b/clang/test/OpenMP/assumes_template_print.cpp @@ -17,7 +17,7 @@ struct S { int a; // CHECK: template struct S { -// CHECK: void foo() __attribute__((assume("ompx_global_assumption"))) { +// CHECK: __attribute__((assume("ompx_global_assumption"))) void foo() { void foo() { #pragma omp parallel {} @@ -25,15 +25,15 @@ }; // CHECK: template<> struct S { -// CHECK: void foo() __attribute__((assume("ompx_global_assumption"))) { +// CHECK: __attribute__((assume("ompx_global_assumption"))) void foo() { #pragma omp begin assumes no_openmp -// CHECK: void S_with_assumes_no_call() __attribute__((assume("omp_no_openmp"))) __attribute__((assume("ompx_global_assumption"))) { +// CHECK: __attribute__((assume("omp_no_openmp"))) __attribute__((assume("ompx_global_assumption"))) void S_with_assumes_no_call() { void S_with_assumes_no_call() { S s; s.a = 0; } -// CHECK: void S_with_assumes_call() __attribute__((assume("omp_no_openmp"))) __attribute__((assume("ompx_global_assumption"))) { +// CHECK: __attribute__((assume("omp_no_openmp"))) __attribute__((assume("ompx_global_assumption"))) void S_with_assumes_call() { void S_with_assumes_call() { S s; s.a = 0; @@ -42,7 +42,7 @@ } #pragma omp end assumes -// CHECK: void S_without_assumes() __attribute__((assume("ompx_global_assumption"))) { +// CHECK: __attribute__((assume("ompx_global_assumption"))) void S_without_assumes() { void S_without_assumes() { S s; s.foo(); @@ -54,7 +54,7 @@ template struct P { // CHECK: template struct P { -// CHECK: void foo() __attribute__((assume("ompx_global_assumption"))) { +// CHECK: __attribute__((assume("ompx_global_assumption"))) void foo() { int a; void foo() { #pragma omp parallel @@ -65,21 +65,21 @@ // TODO: Avoid the duplication here: // CHECK: template<> struct P { -// CHECK: void foo() __attribute__((assume("ompx_global_assumption"))) __attribute__((assume("ompx_global_assumption"))) { +// CHECK: __attribute__((assume("ompx_global_assumption"))) __attribute__((assume("ompx_global_assumption"))) void foo() { -// CHECK: void P_without_assumes() __attribute__((assume("ompx_global_assumption"))) { +// CHECK: __attribute__((assume("ompx_global_assumption"))) void P_without_assumes() { void P_without_assumes() { P p; p.foo(); } #pragma omp begin assumes no_openmp -// CHECK: void P_with_assumes_no_call() __attribute__((assume("omp_no_openmp"))) __attribute__((assume("ompx_global_assumption"))) { +// CHECK: __attribute__((assume("omp_no_openmp"))) __attribute__((assume("ompx_global_assumption"))) void P_with_assumes_no_call() { void P_with_assumes_no_call() { P p; p.a = 0; } -// CHECK: void P_with_assumes_call() __attribute__((assume("omp_no_openmp"))) __attribute__((assume("ompx_global_assumption"))) { +// CHECK: __attribute__((assume("omp_no_openmp"))) __attribute__((assume("ompx_global_assumption"))) void P_with_assumes_call() { void P_with_assumes_call() { P p; p.a = 0; diff --git a/clang/test/OpenMP/declare_simd_ast_print.cpp b/clang/test/OpenMP/declare_simd_ast_print.cpp --- a/clang/test/OpenMP/declare_simd_ast_print.cpp +++ b/clang/test/OpenMP/declare_simd_ast_print.cpp @@ -60,7 +60,7 @@ class VV { // CHECK: #pragma omp declare simd uniform(this, a) linear(val(b): a) - // CHECK-NEXT: int add(int a, int b) __attribute__((cold)) { + // CHECK-NEXT: __attribute__((cold)) int add(int a, int b) { // CHECK-NEXT: return a + b; // CHECK-NEXT: } #pragma omp declare simd uniform(this, a) linear(val(b): a) diff --git a/clang/test/SemaCXX/attr-no-sanitize.cpp b/clang/test/SemaCXX/attr-no-sanitize.cpp --- a/clang/test/SemaCXX/attr-no-sanitize.cpp +++ b/clang/test/SemaCXX/attr-no-sanitize.cpp @@ -16,12 +16,12 @@ // DUMP-LABEL: FunctionDecl {{.*}} f4 // DUMP: NoSanitizeAttr {{.*}} thread -// PRINT: int f4() {{\[\[}}clang::no_sanitize("thread")]] +// PRINT: {{\[\[}}clang::no_sanitize("thread")]] int f4() [[clang::no_sanitize("thread")]] int f4(); // DUMP-LABEL: FunctionDecl {{.*}} f4 // DUMP: NoSanitizeAttr {{.*}} hwaddress -// PRINT: int f4() {{\[\[}}clang::no_sanitize("hwaddress")]] +// PRINT: {{\[\[}}clang::no_sanitize("hwaddress")]] int f4() [[clang::no_sanitize("hwaddress")]] int f4(); // DUMP-LABEL: FunctionDecl {{.*}} f5 @@ -36,5 +36,5 @@ // DUMP-LABEL: FunctionDecl {{.*}} f7 // DUMP: NoSanitizeAttr {{.*}} memtag -// PRINT: int f7() {{\[\[}}clang::no_sanitize("memtag")]] +// PRINT: {{\[\[}}clang::no_sanitize("memtag")]] int f7() [[clang::no_sanitize("memtag")]] int f7(); diff --git a/clang/test/SemaCXX/cxx11-attr-print.cpp b/clang/test/SemaCXX/cxx11-attr-print.cpp --- a/clang/test/SemaCXX/cxx11-attr-print.cpp +++ b/clang/test/SemaCXX/cxx11-attr-print.cpp @@ -42,21 +42,21 @@ // CHECK: int f1() __attribute__((warn_unused_result(""))); int f1() __attribute__((warn_unused_result)); -// CHECK: {{\[}}[clang::warn_unused_result("")]]; +// CHECK: {{\[}}[clang::warn_unused_result("")]] int f2 [[clang::warn_unused_result]] (); -// CHECK: {{\[}}[gnu::warn_unused_result("")]]; +// CHECK: {{\[}}[gnu::warn_unused_result("")]] int f3 [[gnu::warn_unused_result]] (); // FIXME: ast-print need to print C++11 // attribute after function declare-id. -// CHECK: {{\[}}[noreturn]]; +// CHECK: {{\[}}[noreturn]] void f4 [[noreturn]] (); // CHECK: __attribute__((gnu_inline)); inline void f6() __attribute__((gnu_inline)); -// CHECK: {{\[}}[gnu::gnu_inline]]; +// CHECK: {{\[}}[gnu::gnu_inline]] inline void f7 [[gnu::gnu_inline]] (); // arguments printing @@ -66,8 +66,8 @@ // CHECK: int m __attribute__((aligned(4 // CHECK: int n alignas(4 // CHECK: int p alignas(int -// CHECK: static int f() __attribute__((pure)) -// CHECK: static int g() {{\[}}[gnu::pure]] +// CHECK: __attribute__((pure)) static int f() +// CHECK: {{\[}}[gnu::pure]] static int g() template struct S { __attribute__((aligned(4))) int m; alignas(4) int n; @@ -82,8 +82,8 @@ // CHECK: int m __attribute__((aligned(4 // CHECK: int n alignas(4 -// CHECK: static int f() __attribute__((pure)) -// CHECK: static int g() {{\[}}[gnu::pure]] +// CHECK: __attribute__((pure)) static int f() +// CHECK: {{\[}}[gnu::pure]] static int g() template struct S; // CHECK: using Small2 {{\[}}[gnu::mode(byte)]] = int;