Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/include/clang/AST/TypeBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -7374,6 +7374,10 @@ bool isSubstitutedDefaultArgument(ASTContext &Ctx, TemplateArgument Arg,
ArrayRef<TemplateArgument> Args,
unsigned Depth);

void printAnonymousTagDecl(llvm::raw_ostream &OS, const TagDecl *D,
const PrintingPolicy &Policy,
bool PrintKindDecoration, bool PrintTagLocations);

/// Represents a qualified type name for which the type name is
/// dependent.
///
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "clang/AST/Stmt.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeBase.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/IdentifierTable.h"
Expand Down Expand Up @@ -1793,7 +1794,9 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS,
if (TypedefNameDecl *TD = RD->getTypedefNameForAnonDecl())
OS << *TD;
else if (!RD->getIdentifier())
OS << "(anonymous " << RD->getKindName() << ')';
printAnonymousTagDecl(OS, llvm::cast<TagDecl>(RD), P,
/*PrintKindDecoration=*/true,
/*AllowSourceLocations=*/false);
else
OS << *RD;
} else if (const auto *FD = dyn_cast<FunctionDecl>(DC)) {
Expand Down
106 changes: 59 additions & 47 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeBase.h"
#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/AttrKinds.h"
#include "clang/Basic/ExceptionSpecificationType.h"
Expand Down Expand Up @@ -1518,11 +1519,11 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) {
return;
}

bool HasKindDecoration = false;
bool PrintKindDecoration = Policy.AnonymousTagLocations;

if (T->isCanonicalUnqualified()) {
if (!Policy.SuppressTagKeyword && !D->getTypedefNameForAnonDecl()) {
HasKindDecoration = true;
PrintKindDecoration = false;
OS << D->getKindName();
OS << ' ';
}
Expand All @@ -1546,51 +1547,10 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) {
else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) {
assert(Typedef->getIdentifier() && "Typedef without identifier?");
OS << Typedef->getIdentifier()->getName();
} else {
// Make an unambiguous representation for anonymous types, e.g.
// (anonymous enum at /usr/include/string.h:120:9)
OS << (Policy.MSVCFormatting ? '`' : '(');

if (isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda()) {
OS << "lambda";
HasKindDecoration = true;
} else if ((isa<RecordDecl>(D) && cast<RecordDecl>(D)->isAnonymousStructOrUnion())) {
OS << "anonymous";
} else {
OS << "unnamed";
}

if (Policy.AnonymousTagLocations) {
// Suppress the redundant tag keyword if we just printed one.
// We don't have to worry about ElaboratedTypes here because you can't
// refer to an anonymous type with one.
if (!HasKindDecoration)
OS << " " << D->getKindName();

PresumedLoc PLoc = D->getASTContext().getSourceManager().getPresumedLoc(
D->getLocation());
if (PLoc.isValid()) {
OS << " at ";
StringRef File = PLoc.getFilename();
llvm::SmallString<1024> WrittenFile(File);
if (auto *Callbacks = Policy.Callbacks)
WrittenFile = Callbacks->remapPath(File);
// Fix inconsistent path separator created by
// clang::DirectoryLookup::LookupFile when the file path is relative
// path.
llvm::sys::path::Style Style =
llvm::sys::path::is_absolute(WrittenFile)
? llvm::sys::path::Style::native
: (Policy.MSVCFormatting
? llvm::sys::path::Style::windows_backslash
: llvm::sys::path::Style::posix);
llvm::sys::path::native(WrittenFile, Style);
OS << WrittenFile << ':' << PLoc.getLine() << ':' << PLoc.getColumn();
}
}

OS << (Policy.MSVCFormatting ? '\'' : ')');
}
} else
printAnonymousTagDecl(OS, D, Policy,
/*PrintKindDecoration=*/PrintKindDecoration,
/*PrintTagLocations=*/Policy.AnonymousTagLocations);

// If this is a class template specialization, print the template
// arguments.
Expand Down Expand Up @@ -2469,6 +2429,58 @@ static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg,
return false;
}

void clang::printAnonymousTagDecl(llvm::raw_ostream &OS, const TagDecl *D,
const PrintingPolicy &Policy,
bool PrintKindDecoration,
bool PrintTagLocations) {
assert(D);

// Make an unambiguous representation for anonymous types, e.g.
// (anonymous enum at /usr/include/string.h:120:9)
OS << (Policy.MSVCFormatting ? '`' : '(');

if (isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda()) {
PrintKindDecoration = false;
OS << "lambda";
} else if ((isa<RecordDecl>(D) &&
cast<RecordDecl>(D)->isAnonymousStructOrUnion())) {
OS << "anonymous";
} else {
OS << "unnamed";
}

// Suppress the redundant tag keyword if we just printed one.
// We don't have to worry about ElaboratedTypes here because you can't
// refer to an anonymous type with one.
if (PrintKindDecoration)
OS << " " << D->getKindName();

if (PrintTagLocations) {
PresumedLoc PLoc =
D->getASTContext().getSourceManager().getPresumedLoc(D->getLocation());
if (PLoc.isValid()) {
OS << " at ";
StringRef File = PLoc.getFilename();
llvm::SmallString<1024> WrittenFile(File);
if (auto *Callbacks = Policy.Callbacks)
WrittenFile = Callbacks->remapPath(File);
// Fix inconsistent path separator created by
// clang::DirectoryLookup::LookupFile when the file path is relative
// path.
llvm::sys::path::Style Style =
llvm::sys::path::is_absolute(WrittenFile)
? llvm::sys::path::Style::native
: (Policy.MSVCFormatting
? llvm::sys::path::Style::windows_backslash
: llvm::sys::path::Style::posix);
llvm::sys::path::native(WrittenFile, Style);
OS << WrittenFile << ':' << PLoc.getLine() << ':' << PLoc.getColumn();
}
}

OS << (Policy.MSVCFormatting ? '\'' : ')');
}

bool clang::isSubstitutedDefaultArgument(ASTContext &Ctx, TemplateArgument Arg,
const NamedDecl *Param,
ArrayRef<TemplateArgument> Args,
Expand Down
10 changes: 9 additions & 1 deletion clang/lib/ASTMatchers/ASTMatchersInternal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ParentMapContext.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/TypeBase.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/LLVM.h"
#include "clang/Lex/Lexer.h"
Expand Down Expand Up @@ -514,7 +515,14 @@ static StringRef getNodeName(const RecordDecl &Node,
return Node.getName();
}
Scratch.clear();
return ("(anonymous " + Node.getKindName() + ")").toStringRef(Scratch);

llvm::raw_svector_ostream OS(Scratch);

printAnonymousTagDecl(
OS, llvm::cast<TagDecl>(&Node), Node.getASTContext().getPrintingPolicy(),
/*PrintKindDecoration=*/true, /*PrintTagLocations=*/false);

return OS.str();
}

static StringRef getNodeName(const NamespaceDecl &Node,
Expand Down
29 changes: 29 additions & 0 deletions clang/unittests/AST/NamedDeclPrinterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,32 @@ TEST(NamedDeclPrinter, NestedNameSpecifierTemplateArgs) {
ASSERT_TRUE(
PrintedNestedNameSpecifierMatches(Code, "method", "vector<int>::"));
}

TEST(NamedDeclPrinter, NestedNameSpecifierLambda) {
const char *Code =
R"(
auto l = [] {
struct Foo {
void method();
};
};
)";
ASSERT_TRUE(PrintedNestedNameSpecifierMatches(
Code, "method", "(lambda)::operator()()::Foo::"));
}

TEST(NamedDeclPrinter, NestedNameSpecifierAnonymousTags) {
const char *Code =
R"(
struct Foo {
class {
public:
struct {
void method();
} i;
};
};
)";
ASSERT_TRUE(PrintedNestedNameSpecifierMatches(
Code, "method", "Foo::(anonymous class)::(unnamed struct)::"));
}
2 changes: 1 addition & 1 deletion clang/unittests/AST/TypePrinterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ TEST(TypePrinter, NestedNameSpecifiers) {
// Further levels of nesting print the entire scope.
ASSERT_TRUE(PrintedTypeMatches(
Code, {}, fieldDecl(hasName("u"), hasType(qualType().bind("id"))),
"union level1()::Inner::Inner(int)::(anonymous struct)::(unnamed)",
"union level1()::Inner::Inner(int)::(unnamed struct)::(unnamed)",
[](PrintingPolicy &Policy) {
Policy.FullyQualifiedName = true;
Policy.AnonymousTagLocations = false;
Expand Down
33 changes: 28 additions & 5 deletions clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2709,25 +2709,48 @@ TEST_P(ASTMatchersTest, HasName_MatchesAnonymousNamespaces) {
EXPECT_TRUE(matches(code, recordDecl(hasName("::a::C"))));
}

TEST_P(ASTMatchersTest, HasName_MatchesAnonymousOuterClasses) {
TEST_P(ASTMatchersTest, HasName_MatchesUnnamedOuterClasses) {
if (!GetParam().isCXX()) {
return;
}

EXPECT_TRUE(matches("class A { class { class C; } x; };",
recordDecl(hasName("A::(anonymous class)::C"))));
recordDecl(hasName("A::(unnamed class)::C"))));
EXPECT_TRUE(matches("class A { class { class C; } x; };",
recordDecl(hasName("::A::(anonymous class)::C"))));
recordDecl(hasName("::A::(unnamed class)::C"))));
EXPECT_FALSE(matches("class A { class { class C; } x; };",
recordDecl(hasName("::A::C"))));
EXPECT_TRUE(matches("class A { struct { class C; } x; };",
recordDecl(hasName("A::(anonymous struct)::C"))));
recordDecl(hasName("A::(unnamed struct)::C"))));
EXPECT_TRUE(matches("class A { struct { class C; } x; };",
recordDecl(hasName("::A::(anonymous struct)::C"))));
recordDecl(hasName("::A::(unnamed struct)::C"))));
EXPECT_FALSE(matches("class A { struct { class C; } x; };",
recordDecl(hasName("::A::C"))));
}

TEST_P(ASTMatchersTest, HasName_MatchesAnonymousOuterClasses) {
if (!GetParam().isCXX()) {
return;
}

EXPECT_TRUE(matches(
"class A { struct { struct { class C; } x; }; };",
recordDecl(hasName("A::(anonymous struct)::(unnamed struct)::C"))));
EXPECT_TRUE(matches(
"class A { struct { struct { class C; } x; }; };",
recordDecl(hasName("::A::(anonymous struct)::(unnamed struct)::C"))));
EXPECT_FALSE(matches("class A { struct { struct { class C; } x; }; };",
recordDecl(hasName("A::(unnamed struct)::C"))));
EXPECT_TRUE(matches(
"class A { class { public: struct { class C; } x; }; };",
recordDecl(hasName("A::(anonymous class)::(unnamed struct)::C"))));
EXPECT_TRUE(matches(
"class A { class { public: struct { class C; } x; }; };",
recordDecl(hasName("::A::(anonymous class)::(unnamed struct)::C"))));
EXPECT_FALSE(matches("class A { class { public: struct { class C; } x; }; };",
recordDecl(hasName("A::(unnamed struct)::C"))));
}

TEST_P(ASTMatchersTest, HasName_MatchesFunctionScope) {
if (!GetParam().isCXX()) {
return;
Expand Down
Loading