Skip to content

Commit bb1b82a

Browse files
authored
[clang][TypePrinter] Replace AppendScope with printNestedNameSpecifier (#168534)
In debug-info we soon have the need to print names using the full scope of the entity (see discussion in #159592). Particularly, when a structure is scoped inside a function, we'd like to emit the name as `func()::foo`. `CGDebugInfo` uses the `TypePrinter` to print type names into debug-info. However, `TypePrinter` stops (and ignores) `DeclContext`s that are functions. I.e., it would just print `foo`. Ideally it would behave the same way `printNestedNameSpecifier` does. The FIXME in https://github.com/llvm/llvm-project/blob/47c1aa4cef638c97b74f3afb7bed60e92bba1f90/clang/lib/AST/TypePrinter.cpp#L1520-L1521 motivated this patch. See #168533 for how this will be used by `CGDebugInfo`. The plan is to introduce a new `PrintingPolicy` that prints anonymous entities using their full scope (including function/anonymous scopes) and the mangling number.
1 parent f2cb5d7 commit bb1b82a

File tree

9 files changed

+67
-72
lines changed

9 files changed

+67
-72
lines changed

clang/lib/AST/Decl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,9 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS,
17421742
// Collect named contexts.
17431743
DeclarationName NameInScope = getDeclName();
17441744
for (; Ctx; Ctx = Ctx->getParent()) {
1745+
if (P.Callbacks && P.Callbacks->isScopeVisible(Ctx))
1746+
continue;
1747+
17451748
// Suppress anonymous namespace if requested.
17461749
if (P.SuppressUnwrittenScope && isa<NamespaceDecl>(Ctx) &&
17471750
cast<NamespaceDecl>(Ctx)->isAnonymousNamespace())

clang/lib/AST/TypePrinter.cpp

Lines changed: 6 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,6 @@ class TypePrinter {
131131

132132
void printBefore(QualType T, raw_ostream &OS);
133133
void printAfter(QualType T, raw_ostream &OS);
134-
void AppendScope(DeclContext *DC, raw_ostream &OS,
135-
DeclarationName NameInScope);
136134
void printTagType(const TagType *T, raw_ostream &OS);
137135
void printFunctionAfter(const FunctionType::ExtInfo &Info, raw_ostream &OS);
138136
#define ABSTRACT_TYPE(CLASS, PARENT)
@@ -1226,7 +1224,7 @@ void TypePrinter::printTypeSpec(NamedDecl *D, raw_ostream &OS) {
12261224
// In C, this will always be empty except when the type
12271225
// being printed is anonymous within other Record.
12281226
if (!Policy.SuppressScope)
1229-
AppendScope(D->getDeclContext(), OS, D->getDeclName());
1227+
D->printNestedNameSpecifier(OS, Policy);
12301228

12311229
IdentifierInfo *II = D->getIdentifier();
12321230
OS << II->getName();
@@ -1240,7 +1238,7 @@ void TypePrinter::printUnresolvedUsingBefore(const UnresolvedUsingType *T,
12401238
OS << ' ';
12411239
auto *D = T->getDecl();
12421240
if (Policy.FullyQualifiedName || T->isCanonicalUnqualified()) {
1243-
AppendScope(D->getDeclContext(), OS, D->getDeclName());
1241+
D->printNestedNameSpecifier(OS, Policy);
12441242
} else {
12451243
T->getQualifier().print(OS, Policy);
12461244
}
@@ -1257,7 +1255,7 @@ void TypePrinter::printUsingBefore(const UsingType *T, raw_ostream &OS) {
12571255
OS << ' ';
12581256
auto *D = T->getDecl();
12591257
if (Policy.FullyQualifiedName) {
1260-
AppendScope(D->getDeclContext(), OS, D->getDeclName());
1258+
D->printNestedNameSpecifier(OS, Policy);
12611259
} else {
12621260
T->getQualifier().print(OS, Policy);
12631261
}
@@ -1273,7 +1271,7 @@ void TypePrinter::printTypedefBefore(const TypedefType *T, raw_ostream &OS) {
12731271
OS << ' ';
12741272
auto *D = T->getDecl();
12751273
if (Policy.FullyQualifiedName) {
1276-
AppendScope(D->getDeclContext(), OS, D->getDeclName());
1274+
D->printNestedNameSpecifier(OS, Policy);
12771275
} else {
12781276
T->getQualifier().print(OS, Policy);
12791277
}
@@ -1511,59 +1509,6 @@ void TypePrinter::printPredefinedSugarBefore(const PredefinedSugarType *T,
15111509
void TypePrinter::printPredefinedSugarAfter(const PredefinedSugarType *T,
15121510
raw_ostream &OS) {}
15131511

1514-
/// Appends the given scope to the end of a string.
1515-
void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS,
1516-
DeclarationName NameInScope) {
1517-
if (DC->isTranslationUnit())
1518-
return;
1519-
1520-
// FIXME: Consider replacing this with NamedDecl::printNestedNameSpecifier,
1521-
// which can also print names for function and method scopes.
1522-
if (DC->isFunctionOrMethod())
1523-
return;
1524-
1525-
if (Policy.Callbacks && Policy.Callbacks->isScopeVisible(DC))
1526-
return;
1527-
1528-
if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) {
1529-
if (Policy.SuppressUnwrittenScope && NS->isAnonymousNamespace())
1530-
return AppendScope(DC->getParent(), OS, NameInScope);
1531-
1532-
// Only suppress an inline namespace if the name has the same lookup
1533-
// results in the enclosing namespace.
1534-
if (Policy.SuppressInlineNamespace !=
1535-
PrintingPolicy::SuppressInlineNamespaceMode::None &&
1536-
NS->isInline() && NameInScope &&
1537-
NS->isRedundantInlineQualifierFor(NameInScope))
1538-
return AppendScope(DC->getParent(), OS, NameInScope);
1539-
1540-
AppendScope(DC->getParent(), OS, NS->getDeclName());
1541-
if (NS->getIdentifier())
1542-
OS << NS->getName() << "::";
1543-
else
1544-
OS << "(anonymous namespace)::";
1545-
} else if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC)) {
1546-
AppendScope(DC->getParent(), OS, Spec->getDeclName());
1547-
IncludeStrongLifetimeRAII Strong(Policy);
1548-
OS << Spec->getIdentifier()->getName();
1549-
const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
1550-
printTemplateArgumentList(
1551-
OS, TemplateArgs.asArray(), Policy,
1552-
Spec->getSpecializedTemplate()->getTemplateParameters());
1553-
OS << "::";
1554-
} else if (const auto *Tag = dyn_cast<TagDecl>(DC)) {
1555-
AppendScope(DC->getParent(), OS, Tag->getDeclName());
1556-
if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl())
1557-
OS << Typedef->getIdentifier()->getName() << "::";
1558-
else if (Tag->getIdentifier())
1559-
OS << Tag->getIdentifier()->getName() << "::";
1560-
else
1561-
return;
1562-
} else {
1563-
AppendScope(DC->getParent(), OS, NameInScope);
1564-
}
1565-
}
1566-
15671512
void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) {
15681513
TagDecl *D = T->getDecl();
15691514

@@ -1593,7 +1538,7 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) {
15931538
// Compute the full nested-name-specifier for this type.
15941539
// In C, this will always be empty except when the type
15951540
// being printed is anonymous within other Record.
1596-
AppendScope(D->getDeclContext(), OS, D->getDeclName());
1541+
D->printNestedNameSpecifier(OS, Policy);
15971542
}
15981543

15991544
if (const IdentifierInfo *II = D->getIdentifier())
@@ -1809,7 +1754,7 @@ void TypePrinter::printTemplateId(const TemplateSpecializationType *T,
18091754
// FIXME: Null TD never exercised in test suite.
18101755
if (FullyQualify && TD) {
18111756
if (!Policy.SuppressScope)
1812-
AppendScope(TD->getDeclContext(), OS, TD->getDeclName());
1757+
TD->printNestedNameSpecifier(OS, Policy);
18131758

18141759
OS << TD->getName();
18151760
} else {

clang/test/ASTMerge/struct/test.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@
4444
// CHECK: struct2.c:72:7: note: field 'i' has type 'int' here
4545
// CHECK: struct2.c:76:5: warning: external variable 'x13' declared with incompatible types in different translation units ('S13' vs. 'S13')
4646
// CHECK: struct1.c:79:5: note: declared here with type 'S13'
47-
// CHECK: struct1.c:130:7: warning: type 'struct DeepUnnamedError::(unnamed at [[PATH_TO_INPUTS:.+]]struct1.c:130:7)' has incompatible definitions in different translation units
47+
// CHECK: struct1.c:130:7: warning: type 'struct DeepUnnamedError::(anonymous union)::(anonymous union)::(unnamed at [[PATH_TO_INPUTS:.+]]struct1.c:130:7)' has incompatible definitions in different translation units
4848
// CHECK: struct1.c:131:14: note: field 'i' has type 'long' here
4949
// CHECK: struct2.c:128:15: note: field 'i' has type 'float' here
50-
// CHECK: struct1.c:129:5: warning: type 'union DeepUnnamedError::(unnamed at [[PATH_TO_INPUTS]]struct1.c:129:5)' has incompatible definitions in different translation units
50+
// CHECK: struct1.c:129:5: warning: type 'union DeepUnnamedError::(anonymous union)::(unnamed at [[PATH_TO_INPUTS]]struct1.c:129:5)' has incompatible definitions in different translation units
5151
// CHECK: struct1.c:132:9: note: field 'S' has type 'struct (unnamed struct at [[PATH_TO_INPUTS]]struct1.c:130:7)' here
5252
// CHECK: struct2.c:129:9: note: field 'S' has type 'struct (unnamed struct at [[PATH_TO_INPUTS]]struct2.c:127:7)' here
5353
// CHECK: struct2.c:138:3: warning: external variable 'x16' declared with incompatible types in different translation units ('struct DeepUnnamedError' vs. 'struct DeepUnnamedError')

clang/test/CXX/drs/cwg6xx.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,7 @@ namespace cwg686 { // cwg686: 3.0
12411241
#endif
12421242
struct N {
12431243
operator struct O{}(){};
1244-
// expected-error@-1 {{'N::O' cannot be defined in a type specifier}}
1244+
// expected-error@-1 {{'cwg686::f()::N::O' cannot be defined in a type specifier}}
12451245
};
12461246
try {}
12471247
catch (struct P *) {}

clang/test/Index/print-type.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ _Atomic(unsigned long) aul;
7171
// CHECK: TypeRef=struct Struct:16:8 [type=struct Struct] [typekind=Record] [isPOD=1]
7272
// CHECK: StructDecl=struct (unnamed at {{.*}}):18:1 (Definition) [type=struct (unnamed at {{.*}}print-type.c:18:1)] [typekind=Record] [isPOD=1] [nbFields=2] [isAnon=1] [isAnonRecDecl=0]
7373
// CHECK: StructDecl=struct (unnamed at {{.*}}):23:1 (Definition) [type=struct (unnamed at {{.*}}print-type.c:23:1)] [typekind=Record] [isPOD=1] [nbFields=1] [isAnon=1] [isAnonRecDecl=0]
74-
// CHECK: StructDecl=struct (anonymous at {{.*}}):24:3 (Definition) [type=struct (anonymous at {{.*}}print-type.c:24:3)] [typekind=Record] [isPOD=1] [nbFields=2] [isAnon=1] [isAnonRecDecl=1]
74+
// CHECK: StructDecl=struct (anonymous at {{.*}}):24:3 (Definition) [type=struct (anonymous struct)::(anonymous at {{.*}}print-type.c:24:3)] [typekind=Record] [isPOD=1] [nbFields=2] [isAnon=1] [isAnonRecDecl=1]
7575
// CHECK: FieldDecl=x:25:17 (Definition) [type=_Atomic(int)] [typekind=Atomic] [valuetype=int] [valuetypekind=Int] [isPOD=0] [isAnonRecDecl=0]
7676
// CHECK: FieldDecl=y:26:9 (Definition) [type=int] [typekind=Int] [isPOD=1] [isAnonRecDecl=0]
7777
// CHECK: StructDecl=struct (unnamed at {{.*}}):30:10 (Definition) [type=struct (unnamed at {{.*}}print-type.c:30:10)] [typekind=Record] [isPOD=1] [nbFields=2] [isAnon=1] [isAnonRecDecl=0]

clang/test/Layout/ms-x86-alias-avoidance-padding.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ struct AT3 : AT2, AT1 {
4949
// CHECK-NEXT: 0 | struct AT2 (base)
5050
// CHECK-NEXT: 0 | struct AT0 t
5151
// CHECK-NEXT: 0 | union AT0::(unnamed at {{.*}} x
52-
// CHECK-NEXT: 0 | struct AT0::(unnamed at {{.*}} y
52+
// CHECK-NEXT: 0 | struct AT0::(anonymous union)::(unnamed at {{.*}} y
5353
// CHECK-NEXT: 0 | int a
5454
// CHECK-NEXT: 4 | struct AT t (empty)
5555
// CHECK: 0 | int b
@@ -66,7 +66,7 @@ struct AT3 : AT2, AT1 {
6666
// CHECK-X64-NEXT: 0 | struct AT2 (base)
6767
// CHECK-X64-NEXT: 0 | struct AT0 t
6868
// CHECK-X64-NEXT: 0 | union AT0::(unnamed at {{.*}} x
69-
// CHECK-X64-NEXT: 0 | struct AT0::(unnamed at {{.*}} y
69+
// CHECK-X64-NEXT: 0 | struct AT0::(anonymous union)::(unnamed at {{.*}} y
7070
// CHECK-X64-NEXT: 0 | int a
7171
// CHECK-X64-NEXT: 4 | struct AT t (empty)
7272
// CHECK-X64: 0 | int b

clang/test/Modules/compare-record.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ struct CompareAnonymousNestedStruct compareAnonymousNestedStruct;
496496
// [email protected]:* {{declaration of 'anonymousNestedStructField' does not match}}
497497
#elif defined(CASE3)
498498
struct CompareDeeplyNestedAnonymousUnionsAndStructs compareDeeplyNested;
499-
// [email protected]:* {{'CompareDeeplyNestedAnonymousUnionsAndStructs::(anonymous union)::(anonymous union)::(anonymous struct)::z' from module 'Second' is not present in definition of 'struct CompareDeeplyNestedAnonymousUnionsAndStructs::(anonymous at {{.*}})' in module 'First.Hidden'}}
499+
// [email protected]:* {{'CompareDeeplyNestedAnonymousUnionsAndStructs::(anonymous union)::(anonymous union)::(anonymous struct)::z' from module 'Second' is not present in definition of 'struct CompareDeeplyNestedAnonymousUnionsAndStructs::(anonymous union)::(anonymous union)::(anonymous at {{.*}})' in module 'First.Hidden'}}
500+
500501
// [email protected]:* {{declaration of 'z' does not match}}
501502
#endif

clang/test/SemaObjCXX/arc-0x.mm

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ void test() {
162162

163163
struct S1 {
164164
union {
165-
union { // expected-note-re {{copy constructor of 'S1' is implicitly deleted because field 'test_union::S1::(anonymous union at {{.*}})' has a deleted copy constructor}} expected-note-re {{copy assignment operator of 'S1' is implicitly deleted because field 'test_union::S1::(anonymous union at {{.*}})' has a deleted copy assignment operator}} expected-note-re 4 {{'S1' is implicitly deleted because field 'test_union::S1::(anonymous union at {{.*}})' has a deleted}}
165+
union { // expected-note-re {{copy constructor of 'S1' is implicitly deleted because field 'test_union::S1::(anonymous union)::(anonymous union at {{.*}})' has a deleted copy constructor}} expected-note-re {{copy assignment operator of 'S1' is implicitly deleted because field 'test_union::S1::(anonymous union)::(anonymous union at {{.*}})' has a deleted copy assignment operator}} expected-note-re 4 {{'S1' is implicitly deleted because field 'test_union::S1::(anonymous union)::(anonymous union at {{.*}})' has a deleted}}
166166
id f0; // expected-note-re 2 {{{{.*}} of '(anonymous union at {{.*}}' is implicitly deleted because variant field 'f0' is an ObjC pointer}}
167167
char f1;
168168
};
@@ -173,7 +173,7 @@ void test() {
173173
struct S2 {
174174
union {
175175
// FIXME: the note should say 'f0' is causing the special functions to be deleted.
176-
struct { // expected-note-re 6 {{'S2' is implicitly deleted because variant field 'test_union::S2::(anonymous struct at {{.*}})' has a non-trivial}}
176+
struct { // expected-note-re 6 {{'S2' is implicitly deleted because variant field 'test_union::S2::(anonymous union)::(anonymous struct at {{.*}})' has a non-trivial}}
177177
id f0;
178178
int f1;
179179
};
@@ -195,8 +195,8 @@ void test() {
195195
};
196196

197197
static union { // expected-error {{call to implicitly-deleted default constructor of}}
198-
union { // expected-note-re {{default constructor of '(unnamed union at {{.*}}' is implicitly deleted because field 'test_union::(anonymous union at {{.*}})' has a deleted default constructor}}
199-
union { // expected-note-re {{default constructor of '(anonymous union at {{.*}}' is implicitly deleted because field 'test_union::(anonymous union at {{.*}})' has a deleted default constructor}}
198+
union { // expected-note-re {{default constructor of '(unnamed union at {{.*}}' is implicitly deleted because field 'test_union::(anonymous union)::(anonymous union at {{.*}})' has a deleted default constructor}}
199+
union { // expected-note-re {{default constructor of '(anonymous union at {{.*}}' is implicitly deleted because field 'test_union::(anonymous union)::(anonymous union)::(anonymous union at {{.*}})' has a deleted default constructor}}
200200
__weak id g1; // expected-note-re {{default constructor of '(anonymous union at {{.*}}' is implicitly deleted because variant field 'g1' is an ObjC pointer}}
201201
int g2;
202202
};

clang/unittests/AST/TypePrinterTest.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,49 @@ TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
295295
Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
296296
}
297297
}
298+
299+
TEST(TypePrinter, NestedNameSpecifiers) {
300+
constexpr char Code[] = R"cpp(
301+
void level1() {
302+
struct Inner {
303+
Inner(int) {
304+
struct {
305+
union {} u;
306+
} imem;
307+
}
308+
};
309+
}
310+
)cpp";
311+
312+
// Types scoped immediately inside a function don't print the function name in
313+
// their scope.
314+
ASSERT_TRUE(PrintedTypeMatches(
315+
Code, {}, varDecl(hasName("imem"), hasType(qualType().bind("id"))),
316+
"struct (unnamed)", [](PrintingPolicy &Policy) {
317+
Policy.FullyQualifiedName = true;
318+
Policy.AnonymousTagLocations = false;
319+
}));
320+
321+
ASSERT_TRUE(PrintedTypeMatches(
322+
Code, {}, varDecl(hasName("imem"), hasType(qualType().bind("id"))),
323+
"struct (unnamed)", [](PrintingPolicy &Policy) {
324+
Policy.FullyQualifiedName = false;
325+
Policy.AnonymousTagLocations = false;
326+
}));
327+
328+
// Further levels of nesting print the entire scope.
329+
ASSERT_TRUE(PrintedTypeMatches(
330+
Code, {}, fieldDecl(hasName("u"), hasType(qualType().bind("id"))),
331+
"union level1()::Inner::Inner(int)::(anonymous struct)::(unnamed)",
332+
[](PrintingPolicy &Policy) {
333+
Policy.FullyQualifiedName = true;
334+
Policy.AnonymousTagLocations = false;
335+
}));
336+
337+
ASSERT_TRUE(PrintedTypeMatches(
338+
Code, {}, fieldDecl(hasName("u"), hasType(qualType().bind("id"))),
339+
"union (unnamed)", [](PrintingPolicy &Policy) {
340+
Policy.FullyQualifiedName = false;
341+
Policy.AnonymousTagLocations = false;
342+
}));
343+
}

0 commit comments

Comments
 (0)