[AST] Fix printing tag decl groups in decl contexts
authorJoel E. Denny <jdenny.ornl@gmail.com>
Tue, 15 May 2018 00:44:14 +0000 (00:44 +0000)
committerJoel E. Denny <jdenny.ornl@gmail.com>
Tue, 15 May 2018 00:44:14 +0000 (00:44 +0000)
For example, given:

  struct T1 {
    struct T2 *p0;
  };

-ast-print produced:

  struct T1 {
    struct T2;
    struct T2 *p0;
  };

Compiling that produces a warning that the first struct T2 declaration
does not declare anything.

Details:

A tag decl group is one or more decls that share a type specifier that
is a tag decl (that is, a struct/union/class/enum decl).  Within
functions, the parser builds such a tag decl group as part of a
DeclStmt.  However, in decl contexts, such as file scope or a member
list, the parser does not group together the members of a tag decl
group.  Previously, detection of tag decl groups during printing was
implemented but only if the tag decl was unnamed.  Otherwise, as in
the above example, the members of the group did not print together and
so sometimes introduced warnings.

This patch extends detection of tag decl groups in decl contexts to
any tag decl that is recorded in the AST as not free-standing.

Reviewed by: rsmith

Differential Revision: https://reviews.llvm.org/D45465

llvm-svn: 332314

clang/lib/AST/DeclPrinter.cpp
clang/test/Misc/ast-print-enum-decl.c
clang/test/Misc/ast-print-record-decl.c
clang/test/Sema/ast-print.c
clang/test/SemaCXX/ast-print.cpp

index 84c56e3b960ca3b1c0ddf23a8d15b4d9dd95cbcd..b60efe81043266b9dc74568582826021eb6862a1 100644 (file)
@@ -375,21 +375,23 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) {
           !isa<ClassTemplateSpecializationDecl>(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<ElaboratedType>(BaseType))
-        BaseType = cast<ElaboratedType>(BaseType)->getNamedType();
-      if (!BaseType.isNull() && isa<TagType>(BaseType) &&
-          cast<TagType>(BaseType)->getDecl() == Decls[0]) {
+      if (!BaseType.isNull() && isa<ElaboratedType>(BaseType) &&
+          cast<ElaboratedType>(BaseType)->getOwnedTagDecl() == Decls[0]) {
         Decls.push_back(*D);
         continue;
       }
@@ -399,9 +401,9 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) {
     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<TagDecl>(*D) && !cast<TagDecl>(*D)->getIdentifier()) {
+    if (isa<TagDecl>(*D) && !cast<TagDecl>(*D)->isFreeStanding()) {
       Decls.push_back(*D);
       continue;
     }
index 17289e8e38cfc95cd4d0e60fe5e085cacc788b6c..fba9313442850d9c9e0e332928422e500759f5c3 100644 (file)
@@ -83,3 +83,23 @@ void declsOnly() {
   // 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 e1068d8408ed57bbf963aecfca2428a7020434bf..c28c0aba791b5bd87b386d32aed8eb000a9b1efa 100644 (file)
@@ -7,8 +7,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.
 //
@@ -19,7 +19,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:
 //
@@ -30,8 +31,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.
 //
@@ -42,7 +43,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):
 //
@@ -54,7 +56,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.
@@ -66,7 +68,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.
@@ -155,25 +157,33 @@ void defSelfRef() {
   // 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;
+  // PRINT-NEXT:    [[KW]] T *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
@@ -224,14 +234,41 @@ void inInit() {
 }
 
 #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 86166dbd99c49f07a9471cb37d92e4da8529a507..92b317e00b090cc9e1b78a2427858751f0371dbb 100644 (file)
@@ -83,8 +83,7 @@ enum EnumWithAttributes { // expected-warning {{'EnumWithAttributes' is deprecat
   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 408af35c295195c4bd497ac6e906825564534461..fd1d3fe84fac3875419ffb36bd6520d22ee52db0 100644 (file)
@@ -214,10 +214,13 @@ namespace {
 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 {