Index: lib/AST/DeclPrinter.cpp =================================================================== --- lib/AST/DeclPrinter.cpp +++ lib/AST/DeclPrinter.cpp @@ -375,21 +375,26 @@ !isa(DC)) continue; - // The next bits of code handles stuff like "struct {int x;} a,b"; we're + // The next bits of code handle stuff like "struct {int x;} a,b"; we're // forced to merge the declarations because there's no other way to - // refer to the struct in question. This limited merging is safe without - // a bunch of other checks because it only merges declarations directly - // referring to the tag, not typedefs. + // refer to the struct in question. When that struct is named instead, we + // also need to merge to avoid splitting off a stand-alone struct + // declaration that produces the warning ext_no_declarators in some + // contexts. + // + // This limited merging is safe without a bunch of other checks because it + // only merges declarations directly referring to the tag, not typedefs. // // Check whether the current declaration should be grouped with a previous - // unnamed struct. + // non-free-standing tag declaration. QualType CurDeclType = getDeclType(*D); if (!Decls.empty() && !CurDeclType.isNull()) { QualType BaseType = GetBaseType(CurDeclType); if (!BaseType.isNull() && isa(BaseType)) BaseType = cast(BaseType)->getNamedType(); if (!BaseType.isNull() && isa(BaseType) && - cast(BaseType)->getDecl() == Decls[0]) { + cast(BaseType) == + cast(Decls[0])->getTypeForDecl()) { Decls.push_back(*D); continue; } @@ -399,9 +404,9 @@ if (!Decls.empty()) ProcessDeclGroup(Decls); - // If the current declaration is an unnamed tag type, save it + // If the current declaration is not a free standing declaration, save it // so we can merge it with the subsequent declaration(s) using it. - if (isa(*D) && !cast(*D)->getIdentifier()) { + if (isa(*D) && !cast(*D)->isFreeStanding()) { Decls.push_back(*D); continue; } Index: test/Misc/ast-print-enum-decl.c =================================================================== --- test/Misc/ast-print-enum-decl.c +++ test/Misc/ast-print-enum-decl.c @@ -83,3 +83,23 @@ // PRINT-NEXT: enum T *p4; enum T *p4; } + +// Check that tag decl groups stay together in decl contexts. + +// PRINT-LABEL: enum DeclGroupAtFileScope { +// PRINT-NEXT: DeclGroupAtFileScope0 +// PRINT-NEXT: } *DeclGroupAtFileScopePtr; +enum DeclGroupAtFileScope { DeclGroupAtFileScope0 } *DeclGroupAtFileScopePtr; + +// PRINT-LABEL: struct DeclGroupInMemberList +struct DeclGroupInMemberList { + // PRINT-NEXT: enum T1 { + // PRINT-NEXT: T10 + // PRINT-NEXT: } *p0; + enum T1 { T10 } *p0; + // PRINT-NEXT: enum T2 { + // PRINT-NEXT: T20 + // PRINT-NEXT: } *p1, *p2; + enum T2 { T20 } *p1, *p2; + // PRINT-NEXT: }; +}; Index: test/Misc/ast-print-record-decl.c =================================================================== --- test/Misc/ast-print-record-decl.c +++ test/Misc/ast-print-record-decl.c @@ -6,8 +6,8 @@ // RUN: | FileCheck --check-prefixes=CHECK,LLVM %s // // RUN: %clang_cc1 -verify -ast-print -DKW=struct -DBASES= %s > %t.c -// RUN: FileCheck --check-prefixes=CHECK,PRINT -DKW=struct -DBASES= %s \ -// RUN: --input-file %t.c +// RUN: FileCheck --check-prefixes=CHECK,PRINT,PRINT-C -DKW=struct -DBASES= \ +// RUN: %s --input-file %t.c // // Now check compiling and printing of the printed file. // @@ -18,7 +18,8 @@ // RUN: | FileCheck --check-prefixes=CHECK,LLVM %s // // RUN: %clang_cc1 -verify -ast-print %t.c \ -// RUN: | FileCheck --check-prefixes=CHECK,PRINT -DKW=struct -DBASES= %s +// RUN: | FileCheck --check-prefixes=CHECK,PRINT,PRINT-C -DKW=struct \ +// RUN: -DBASES= %s // Repeat for union: // @@ -28,8 +29,8 @@ // RUN: | FileCheck --check-prefixes=CHECK,LLVM %s // // RUN: %clang_cc1 -verify -ast-print -DKW=union -DBASES= %s > %t.c -// RUN: FileCheck --check-prefixes=CHECK,PRINT -DKW=union -DBASES= %s \ -// RUN: --input-file %t.c +// RUN: FileCheck --check-prefixes=CHECK,PRINT,PRINT-C -DKW=union -DBASES= \ +// RUN: %s --input-file %t.c // // Now check compiling and printing of the printed file. // @@ -40,7 +41,8 @@ // RUN: | FileCheck --check-prefixes=CHECK,LLVM %s // // RUN: %clang_cc1 -verify -ast-print %t.c \ -// RUN: | FileCheck --check-prefixes=CHECK,PRINT -DKW=union -DBASES= %s +// RUN: | FileCheck --check-prefixes=CHECK,PRINT,PRINT-C -DKW=union \ +// RUN: -DBASES= %s // Repeat for C++ (BASES helps ensure we're printing as C++ not as C): // @@ -52,7 +54,7 @@ // // RUN: %clang_cc1 -verify -ast-print -DKW=struct -DBASES=' : B' -xc++ %s \ // RUN: > %t.cpp -// RUN: FileCheck --check-prefixes=CHECK,PRINT,CXX -DKW=struct \ +// RUN: FileCheck --check-prefixes=CHECK,PRINT,PRINT-CXX -DKW=struct \ // RUN: -DBASES=' : B' %s --input-file %t.cpp // // Now check compiling and printing of the printed file. @@ -64,7 +66,7 @@ // RUN: | FileCheck --check-prefixes=CHECK,LLVM %s // // RUN: %clang_cc1 -verify -ast-print %t.cpp \ -// RUN: | FileCheck --check-prefixes=CHECK,PRINT,CXX -DKW=struct \ +// RUN: | FileCheck --check-prefixes=CHECK,PRINT,PRINT-CXX -DKW=struct \ // RUN: -DBASES=' : B' %s // END. @@ -153,25 +155,32 @@ // expected-note@+1 2 {{'T' has been explicitly marked deprecated here}} KW __attribute__((deprecated(""))) T *p0; - // PRINT-NEXT: [[KW]] __attribute__((aligned(16))) T[[BASES]] { - // PRINT-NEXT: int i; - // PRINT-NEXT: [[KW]] T *p2; - // PRINT-NEXT: } *p1; - KW __attribute__((aligned(16))) T BASES { // expected-warning {{'T' is deprecated}} + // PRINT-NEXT: [[KW]] __attribute__((aligned(64))) T[[BASES]] { + // PRINT-NEXT: int i; + // PRINT-NEXT: [[KW]] T *p2; + // PRINT-NEXT: [[KW]] __attribute__((may_alias)) T *p3, *p4; + // PRINT-NEXT: } *p1; + KW __attribute__((aligned(64))) T BASES { // expected-warning {{'T' is deprecated}} int i; KW T *p2; + // FIXME: For C++, T at p3 loses aligned and deprecated, perhaps because + // that RecordDecl isn't in the same redecl list. Perhaps the redecl lists + // are split here but not in C due to the different scoping rules in C++ + // classes. + KW __attribute__((may_alias)) T *p3; + KW T *p4; } *p1; - // LLVM: store i64 16 + // LLVM: store i64 64 long s0 = sizeof *p0; - // LLVM-NEXT: store i64 16 + // LLVM-NEXT: store i64 64 long s1 = sizeof *p1; - // LLVM-NEXT: store i64 16 + // LLVM-NEXT: store i64 64 long s2 = sizeof *p0->p2; - // LLVM-NEXT: store i64 16 - long s3 = sizeof *p1->p2; - // LLVM-NEXT: store i64 16 - long s4 = sizeof *p1->p2->p2; + // LLVM-NEXT: store i64 64 + long s3 = sizeof *p1->p3; + // LLVM-NEXT: store i64 64 + long s4 = sizeof *p1->p4->p2; } // CHECK-LABEL: declsOnly @@ -222,14 +231,41 @@ } #ifdef __cplusplus -// CXX-LABEL: inMemberPtr +// PRINT-CXX-LABEL: inMemberPtr void inMemberPtr() { - // CXX-NEXT: [[KW]] T1 { - // CXX-NEXT: int i; - // CXX-NEXT: }; + // PRINT-CXX-NEXT: [[KW]] T1 { + // PRINT-CXX-NEXT: int i; + // PRINT-CXX-NEXT: }; KW T1 { int i; }; - // CXX-NEXT: [[KW]] T2 { - // CXX-NEXT: } T1::*p; + // PRINT-CXX-NEXT: [[KW]] T2 { + // PRINT-CXX-NEXT: } T1::*p; KW T2 {} T1::*p; } #endif + +// Check that tag decl groups stay together in decl contexts. + +// PRINT-LABEL: DeclGroupAtFileScope { +// PRINT-NEXT: int i; +// PRINT-NEXT: } *DeclGroupAtFileScopePtr; +KW DeclGroupAtFileScope { int i; } *DeclGroupAtFileScopePtr; + +// PRINT-LABEL: DeclGroupInMemberList { +KW DeclGroupInMemberList { + // PRINT-NEXT: struct T1 { + // PRINT-NEXT: int i; + // PRINT-NEXT: } t1; + struct T1 { int i; } t1; + // PRINT-NEXT: union T2 { + // PRINT-NEXT: int i; + // PRINT-NEXT: } *t20, t21[2]; + union T2 { int i; } *t20, t21[2]; + // PRINT-NEXT: enum T3 { + // PRINT-NEXT: T30 + // PRINT-NEXT: } t30; + enum T3 { T30 } t30; + // PRINT-NEXT: }; +}; + +// A tag decl group in the tag decl's own member list is exercised in +// defSelfRef above. Index: test/Sema/ast-print.c =================================================================== --- test/Sema/ast-print.c +++ test/Sema/ast-print.c @@ -83,8 +83,7 @@ EnumWithAttributesFoo __attribute__((deprecated)), // CHECK-NEXT: EnumWithAttributesBar __attribute__((unavailable(""))) = 50 EnumWithAttributesBar __attribute__((unavailable)) = 50 - // CHECK-NEXT: }; - // CHECK-NEXT: enum EnumWithAttributes *EnumWithAttributesPtr; + // CHECK-NEXT: } *EnumWithAttributesPtr; } __attribute__((deprecated)) *EnumWithAttributesPtr; // expected-note {{'EnumWithAttributes' has been explicitly marked deprecated here}} // FIXME: If enum is forward-declared at file scope, attributes are lost. Index: test/SemaCXX/ast-print.cpp =================================================================== --- test/SemaCXX/ast-print.cpp +++ test/SemaCXX/ast-print.cpp @@ -214,10 +214,13 @@ struct [[gnu::visibility("hidden")]] S; } -// CHECK: struct CXXFunctionalCastExprPrint fce = CXXFunctionalCastExprPrint{}; +// CHECK: struct CXXFunctionalCastExprPrint { +// CHECK-NEXT: } fce = CXXFunctionalCastExprPrint{}; struct CXXFunctionalCastExprPrint {} fce = CXXFunctionalCastExprPrint{}; -// CHECK: struct CXXTemporaryObjectExprPrint toe = CXXTemporaryObjectExprPrint{}; +// CHECK: struct CXXTemporaryObjectExprPrint { +// CHECK-NEXT: CXXTemporaryObjectExprPrint(); +// CHECK-NEXT: } toe = CXXTemporaryObjectExprPrint{}; struct CXXTemporaryObjectExprPrint { CXXTemporaryObjectExprPrint(); } toe = CXXTemporaryObjectExprPrint{}; namespace PR24872 {