Skip to content

Commit 5f12f4f

Browse files
committed
Suppress printing of inline namespace names in diagnostics by default,
except where they are necessary to disambiguate the target. This substantially improves diagnostics from the standard library, which are otherwise full of `::__1::` noise.
1 parent e7f3e21 commit 5f12f4f

File tree

7 files changed

+123
-33
lines changed

7 files changed

+123
-33
lines changed

clang/include/clang/AST/PrettyPrinter.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ struct PrintingPolicy {
5252
: Indentation(2), SuppressSpecifiers(false),
5353
SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false),
5454
SuppressScope(false), SuppressUnwrittenScope(false),
55-
SuppressInitializers(false), ConstantArraySizeAsWritten(false),
56-
AnonymousTagLocations(true), SuppressStrongLifetime(false),
57-
SuppressLifetimeQualifiers(false),
55+
SuppressInlineNamespace(true), SuppressInitializers(false),
56+
ConstantArraySizeAsWritten(false), AnonymousTagLocations(true),
57+
SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false),
5858
SuppressTemplateArgsInCXXConstructors(false),
5959
SuppressDefaultTemplateArgs(true), Bool(LO.Bool),
6060
Nullptr(LO.CPlusPlus11), Restrict(LO.C99), Alignof(LO.CPlusPlus11),
@@ -118,10 +118,15 @@ struct PrintingPolicy {
118118
/// Suppresses printing of scope specifiers.
119119
unsigned SuppressScope : 1;
120120

121-
/// Suppress printing parts of scope specifiers that don't need
122-
/// to be written, e.g., for inline or anonymous namespaces.
121+
/// Suppress printing parts of scope specifiers that are never
122+
/// written, e.g., for anonymous namespaces.
123123
unsigned SuppressUnwrittenScope : 1;
124124

125+
/// Suppress printing parts of scope specifiers that correspond
126+
/// to inline namespaces, where the name is unambiguous with the specifier
127+
/// removed.
128+
unsigned SuppressInlineNamespace : 1;
129+
125130
/// Suppress printing of variable initializers.
126131
///
127132
/// This flag is used when printing the loop variable in a for-range

clang/lib/AST/Decl.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,21 +1600,35 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS,
16001600
ContextsTy Contexts;
16011601

16021602
// Collect named contexts.
1603-
while (Ctx) {
1604-
if (isa<NamedDecl>(Ctx))
1605-
Contexts.push_back(Ctx);
1606-
Ctx = Ctx->getParent();
1603+
DeclarationName NameInScope = getDeclName();
1604+
for (; Ctx; Ctx = Ctx->getParent()) {
1605+
// Suppress anonymous namespace if requested.
1606+
if (P.SuppressUnwrittenScope && isa<NamespaceDecl>(Ctx) &&
1607+
cast<NamespaceDecl>(Ctx)->isAnonymousNamespace())
1608+
continue;
1609+
1610+
// Suppress inline namespace if it doesn't make the result ambiguous.
1611+
if (P.SuppressInlineNamespace && Ctx->isInlineNamespace() && NameInScope &&
1612+
Ctx->lookup(NameInScope).size() ==
1613+
Ctx->getParent()->lookup(NameInScope).size())
1614+
continue;
1615+
1616+
// Skip non-named contexts such as linkage specifications and ExportDecls.
1617+
const NamedDecl *ND = dyn_cast<NamedDecl>(Ctx);
1618+
if (!ND)
1619+
continue;
1620+
1621+
Contexts.push_back(Ctx);
1622+
NameInScope = ND->getDeclName();
16071623
}
16081624

1609-
for (const DeclContext *DC : llvm::reverse(Contexts)) {
1625+
for (unsigned I = Contexts.size(); I != 0; --I) {
1626+
const DeclContext *DC = Contexts[I - 1];
16101627
if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC)) {
16111628
OS << Spec->getName();
16121629
const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
16131630
printTemplateArgumentList(OS, TemplateArgs.asArray(), P);
16141631
} else if (const auto *ND = dyn_cast<NamespaceDecl>(DC)) {
1615-
if (P.SuppressUnwrittenScope &&
1616-
(ND->isAnonymousNamespace() || ND->isInline()))
1617-
continue;
16181632
if (ND->isAnonymousNamespace()) {
16191633
OS << (P.MSVCFormatting ? "`anonymous namespace\'"
16201634
: "(anonymous namespace)");

clang/lib/AST/TypePrinter.cpp

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ namespace {
118118

119119
void printBefore(QualType T, raw_ostream &OS);
120120
void printAfter(QualType T, raw_ostream &OS);
121-
void AppendScope(DeclContext *DC, raw_ostream &OS);
121+
void AppendScope(DeclContext *DC, raw_ostream &OS,
122+
DeclarationName NameInScope);
122123
void printTag(TagDecl *T, raw_ostream &OS);
123124
void printFunctionAfter(const FunctionType::ExtInfo &Info, raw_ostream &OS);
124125
#define ABSTRACT_TYPE(CLASS, PARENT)
@@ -1021,7 +1022,7 @@ void TypePrinter::printTypeSpec(NamedDecl *D, raw_ostream &OS) {
10211022
// In C, this will always be empty except when the type
10221023
// being printed is anonymous within other Record.
10231024
if (!Policy.SuppressScope)
1024-
AppendScope(D->getDeclContext(), OS);
1025+
AppendScope(D->getDeclContext(), OS, D->getDeclName());
10251026

10261027
IdentifierInfo *II = D->getIdentifier();
10271028
OS << II->getName();
@@ -1211,20 +1212,34 @@ void TypePrinter::printDependentExtIntAfter(const DependentExtIntType *T,
12111212
raw_ostream &OS) {}
12121213

12131214
/// Appends the given scope to the end of a string.
1214-
void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS) {
1215-
if (DC->isTranslationUnit()) return;
1216-
if (DC->isFunctionOrMethod()) return;
1217-
AppendScope(DC->getParent(), OS);
1215+
void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS,
1216+
DeclarationName NameInScope) {
1217+
if (DC->isTranslationUnit())
1218+
return;
1219+
1220+
// FIXME: Consider replacing this with NamedDecl::printNestedNameSpecifier,
1221+
// which can also print names for function and method scopes.
1222+
if (DC->isFunctionOrMethod())
1223+
return;
12181224

12191225
if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) {
1220-
if (Policy.SuppressUnwrittenScope &&
1221-
(NS->isAnonymousNamespace() || NS->isInline()))
1222-
return;
1226+
if (Policy.SuppressUnwrittenScope && NS->isAnonymousNamespace())
1227+
return AppendScope(DC->getParent(), OS, NameInScope);
1228+
1229+
// Only suppress an inline namespace if the name has the same lookup
1230+
// results in the enclosing namespace.
1231+
if (Policy.SuppressInlineNamespace && NS->isInline() && NameInScope &&
1232+
DC->getParent()->lookup(NameInScope).size() ==
1233+
DC->lookup(NameInScope).size())
1234+
return AppendScope(DC->getParent(), OS, NameInScope);
1235+
1236+
AppendScope(DC->getParent(), OS, NS->getDeclName());
12231237
if (NS->getIdentifier())
12241238
OS << NS->getName() << "::";
12251239
else
12261240
OS << "(anonymous namespace)::";
12271241
} else if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC)) {
1242+
AppendScope(DC->getParent(), OS, Spec->getDeclName());
12281243
IncludeStrongLifetimeRAII Strong(Policy);
12291244
OS << Spec->getIdentifier()->getName();
12301245
const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
@@ -1233,12 +1248,15 @@ void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS) {
12331248
Spec->getSpecializedTemplate()->getTemplateParameters());
12341249
OS << "::";
12351250
} else if (const auto *Tag = dyn_cast<TagDecl>(DC)) {
1251+
AppendScope(DC->getParent(), OS, Tag->getDeclName());
12361252
if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl())
12371253
OS << Typedef->getIdentifier()->getName() << "::";
12381254
else if (Tag->getIdentifier())
12391255
OS << Tag->getIdentifier()->getName() << "::";
12401256
else
12411257
return;
1258+
} else {
1259+
AppendScope(DC->getParent(), OS, NameInScope);
12421260
}
12431261
}
12441262

@@ -1265,7 +1283,7 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) {
12651283
// In C, this will always be empty except when the type
12661284
// being printed is anonymous within other Record.
12671285
if (!Policy.SuppressScope)
1268-
AppendScope(D->getDeclContext(), OS);
1286+
AppendScope(D->getDeclContext(), OS, D->getDeclName());
12691287

12701288
if (const IdentifierInfo *II = D->getIdentifier())
12711289
OS << II->getName();

clang/lib/ASTMatchers/ASTMatchersInternal.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -630,13 +630,10 @@ bool HasNameMatcher::matchesNodeFullSlow(const NamedDecl &Node) const {
630630
llvm::SmallString<128> NodeName = StringRef("::");
631631
llvm::raw_svector_ostream OS(NodeName);
632632

633-
if (SkipUnwritten) {
634-
PrintingPolicy Policy = Node.getASTContext().getPrintingPolicy();
635-
Policy.SuppressUnwrittenScope = true;
636-
Node.printQualifiedName(OS, Policy);
637-
} else {
638-
Node.printQualifiedName(OS);
639-
}
633+
PrintingPolicy Policy = Node.getASTContext().getPrintingPolicy();
634+
Policy.SuppressUnwrittenScope = SkipUnwritten;
635+
Policy.SuppressInlineNamespace = SkipUnwritten;
636+
Node.printQualifiedName(OS, Policy);
640637

641638
const StringRef FullName = OS.str();
642639

clang/lib/CodeGen/CodeGenTypes.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,26 @@ void CodeGenTypes::addRecordTypeName(const RecordDecl *RD,
5252
llvm::raw_svector_ostream OS(TypeName);
5353
OS << RD->getKindName() << '.';
5454

55+
// FIXME: We probably want to make more tweaks to the printing policy. For
56+
// example, we should probably enable PrintCanonicalTypes and
57+
// FullyQualifiedNames.
58+
PrintingPolicy Policy = RD->getASTContext().getPrintingPolicy();
59+
Policy.SuppressInlineNamespace = false;
60+
5561
// Name the codegen type after the typedef name
5662
// if there is no tag type name available
5763
if (RD->getIdentifier()) {
5864
// FIXME: We should not have to check for a null decl context here.
5965
// Right now we do it because the implicit Obj-C decls don't have one.
6066
if (RD->getDeclContext())
61-
RD->printQualifiedName(OS);
67+
RD->printQualifiedName(OS, Policy);
6268
else
6369
RD->printName(OS);
6470
} else if (const TypedefNameDecl *TDD = RD->getTypedefNameForAnonDecl()) {
6571
// FIXME: We should not have to check for a null decl context here.
6672
// Right now we do it because the implicit Obj-C decls don't have one.
6773
if (TDD->getDeclContext())
68-
TDD->printQualifiedName(OS);
74+
TDD->printQualifiedName(OS, Policy);
6975
else
7076
TDD->printName(OS);
7177
} else

clang/test/CXX/dcl.dcl/basic.namespace/namespace.def/p8.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ void foo1() {
2121
f1();
2222
::f1();
2323
X::f1();
24-
Y::f1(); // expected-error {{no member named 'f1' in namespace 'X::Y'; did you mean simply 'f1'?}}
24+
Y::f1(); // expected-error {{no member named 'f1' in namespace 'Y'; did you mean simply 'f1'?}}
2525

2626
f2();
2727
::f2();
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %clang_cc1 -verify %s -std=c++20
2+
3+
// We avoid printing the name of an inline namespace unless it's necessary to
4+
// uniquely identify the target.
5+
namespace N {
6+
inline namespace A {
7+
inline namespace B {
8+
inline namespace C {
9+
int f, g, h, i, j;
10+
struct f; struct g; struct h; struct i; struct j;
11+
}
12+
struct g;
13+
struct j;
14+
}
15+
struct h;
16+
}
17+
struct i;
18+
struct j;
19+
20+
template<int*> struct Q; // expected-note 5{{here}}
21+
Q<&A::B::C::f> q1; // expected-error {{implicit instantiation of undefined template 'N::Q<&N::f>'}}
22+
Q<&A::B::C::g> q2; // expected-error {{implicit instantiation of undefined template 'N::Q<&N::C::g>'}}
23+
Q<&A::B::C::h> q3; // expected-error {{implicit instantiation of undefined template 'N::Q<&N::B::h>'}}
24+
Q<&A::B::C::i> q4; // expected-error {{implicit instantiation of undefined template 'N::Q<&N::A::i>'}}
25+
Q<&A::B::C::j> q5; // expected-error {{implicit instantiation of undefined template 'N::Q<&N::C::j>'}}
26+
27+
template<typename> struct R; // expected-note 5{{here}}
28+
R<struct A::B::C::f> r1; // expected-error {{implicit instantiation of undefined template 'N::R<N::f>'}}
29+
R<struct A::B::C::g> r2; // expected-error {{implicit instantiation of undefined template 'N::R<N::C::g>'}}
30+
R<struct A::B::C::h> r3; // expected-error {{implicit instantiation of undefined template 'N::R<N::B::h>'}}
31+
R<struct A::B::C::i> r4; // expected-error {{implicit instantiation of undefined template 'N::R<N::A::i>'}}
32+
R<struct A::B::C::j> r5; // expected-error {{implicit instantiation of undefined template 'N::R<N::C::j>'}}
33+
34+
// Make the name N::C ambiguous.
35+
inline namespace A { int C; }
36+
37+
template<int*> struct S; // expected-note 5{{here}}
38+
S<&A::B::C::f> s1; // expected-error {{implicit instantiation of undefined template 'N::S<&N::f>'}}
39+
S<&A::B::C::g> s2; // expected-error {{implicit instantiation of undefined template 'N::S<&N::B::C::g>'}}
40+
S<&A::B::C::h> s3; // expected-error {{implicit instantiation of undefined template 'N::S<&N::B::h>'}}
41+
S<&A::B::C::i> s4; // expected-error {{implicit instantiation of undefined template 'N::S<&N::A::i>'}}
42+
S<&A::B::C::j> s5; // expected-error {{implicit instantiation of undefined template 'N::S<&N::B::C::j>'}}
43+
44+
template<typename> struct T; // expected-note 5{{here}}
45+
T<struct A::B::C::f> t1; // expected-error {{implicit instantiation of undefined template 'N::T<N::f>'}}
46+
T<struct A::B::C::g> t2; // expected-error {{implicit instantiation of undefined template 'N::T<N::B::C::g>'}}
47+
T<struct A::B::C::h> t3; // expected-error {{implicit instantiation of undefined template 'N::T<N::B::h>'}}
48+
T<struct A::B::C::i> t4; // expected-error {{implicit instantiation of undefined template 'N::T<N::A::i>'}}
49+
T<struct A::B::C::j> t5; // expected-error {{implicit instantiation of undefined template 'N::T<N::B::C::j>'}}
50+
}

0 commit comments

Comments
 (0)