#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/TemplateBase.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
return QName;
}
+static bool isAnonymous(const DeclarationName &N) {
+ return N.isIdentifier() && !N.getAsIdentifierInfo();
+}
+
+/// Returns a nested name specifier of \p ND if it was present in the source,
+/// e.g.
+/// void ns::something::foo() -> returns 'ns::something'
+/// void foo() -> returns null
+static NestedNameSpecifier *getQualifier(const NamedDecl &ND) {
+ if (auto *V = llvm::dyn_cast<DeclaratorDecl>(&ND))
+ return V->getQualifier();
+ if (auto *T = llvm::dyn_cast<TagDecl>(&ND))
+ return T->getQualifier();
+ return nullptr;
+}
+
std::string printName(const ASTContext &Ctx, const NamedDecl &ND) {
std::string Name;
llvm::raw_string_ostream Out(Name);
PrintingPolicy PP(Ctx.getLangOpts());
+
// Handle 'using namespace'. They all have the same name - <using-directive>.
if (auto *UD = llvm::dyn_cast<UsingDirectiveDecl>(&ND)) {
Out << "using namespace ";
UD->getNominatedNamespaceAsWritten()->printName(Out);
return Out.str();
}
- ND.getDeclName().print(Out, PP);
- if (!Out.str().empty()) {
- Out << printTemplateSpecializationArgs(ND);
- return Out.str();
+
+ if (isAnonymous(ND.getDeclName())) {
+ // Come up with a presentation for an anonymous entity.
+ if (isa<NamespaceDecl>(ND))
+ return "(anonymous namespace)";
+ if (auto *Cls = llvm::dyn_cast<RecordDecl>(&ND))
+ return ("(anonymous " + Cls->getKindName() + ")").str();
+ if (isa<EnumDecl>(ND))
+ return "(anonymous enum)";
+ return "(anonymous)";
}
- // The name was empty, so present an anonymous entity.
- if (isa<NamespaceDecl>(ND))
- return "(anonymous namespace)";
- if (auto *Cls = llvm::dyn_cast<RecordDecl>(&ND))
- return ("(anonymous " + Cls->getKindName() + ")").str();
- if (isa<EnumDecl>(ND))
- return "(anonymous enum)";
- return "(anonymous)";
+
+ // Print nested name qualifier if it was written in the source code.
+ if (auto *Qualifier = getQualifier(ND))
+ Qualifier->print(Out, PP);
+ // Print the name itself.
+ ND.getDeclName().print(Out, PP);
+ // Print template arguments.
+ Out << printTemplateSpecializationArgs(ND);
+
+ return Out.str();
}
std::string printTemplateSpecializationArgs(const NamedDecl &ND) {
namespace {
using ::testing::AllOf;
-using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::Field;
AllOf(WithName("KInt"), WithKind(SymbolKind::Variable), Children()),
AllOf(WithName("kStr"), WithKind(SymbolKind::Variable), Children()),
AllOf(WithName("f1"), WithKind(SymbolKind::Function), Children()),
- AllOf(WithName("foo"), WithKind(SymbolKind::Namespace),
- Children(
- AllOf(WithName("int32"), WithKind(SymbolKind::Class),
- Children()),
- AllOf(WithName("int32_t"), WithKind(SymbolKind::Class),
- Children()),
- AllOf(WithName("v1"), WithKind(SymbolKind::Variable),
- Children()),
- AllOf(WithName("bar"), WithKind(SymbolKind::Namespace),
- Children(AllOf(WithName("v2"),
- WithKind(SymbolKind::Variable),
- Children()))),
- AllOf(WithName("baz"), WithKind(SymbolKind::Namespace),
- Children()),
- AllOf(WithName("v2"), WithKind(SymbolKind::Namespace))))}));
+ AllOf(
+ WithName("foo"), WithKind(SymbolKind::Namespace),
+ Children(
+ AllOf(WithName("int32"), WithKind(SymbolKind::Class),
+ Children()),
+ AllOf(WithName("int32_t"), WithKind(SymbolKind::Class),
+ Children()),
+ AllOf(WithName("v1"), WithKind(SymbolKind::Variable),
+ Children()),
+ AllOf(WithName("bar"), WithKind(SymbolKind::Namespace),
+ Children(AllOf(WithName("v2"),
+ WithKind(SymbolKind::Variable),
+ Children()))),
+ AllOf(WithName("baz"), WithKind(SymbolKind::Namespace),
+ Children()),
+ AllOf(WithName("v2"), WithKind(SymbolKind::Namespace))))}));
}
TEST_F(DocumentSymbolsTest, DeclarationDefinition) {
)");
addFile(FilePath, Main.code());
- EXPECT_THAT(getSymbols(FilePath),
- ElementsAre(AllOf(WithName("Foo"), WithKind(SymbolKind::Class),
- Children(AllOf(
- WithName("f"), WithKind(SymbolKind::Method),
- SymNameRange(Main.range("decl"))))),
- AllOf(WithName("f"), WithKind(SymbolKind::Method),
- SymNameRange(Main.range("def")))));
+ EXPECT_THAT(
+ getSymbols(FilePath),
+ ElementsAre(
+ AllOf(WithName("Foo"), WithKind(SymbolKind::Class),
+ Children(AllOf(WithName("f"), WithKind(SymbolKind::Method),
+ SymNameRange(Main.range("decl"))))),
+ AllOf(WithName("Foo::f"), WithKind(SymbolKind::Method),
+ SymNameRange(Main.range("def")))));
}
TEST_F(DocumentSymbolsTest, ExternSymbol) {
AllOf(WithName("Foo<bool, int, 3>"), WithKind(SymbolKind::Class))));
}
+TEST_F(DocumentSymbolsTest, Qualifiers) {
+ addFile("foo.cpp", R"cpp(
+ namespace foo { namespace bar {
+ struct Cls;
+
+ int func1();
+ int func2();
+ int func3();
+ int func4();
+ }}
+
+ struct foo::bar::Cls { };
+
+ int foo::bar::func1() { return 10; }
+ int ::foo::bar::func2() { return 20; }
+
+ using namespace foo;
+ int bar::func3() { return 30; }
+
+ namespace alias = foo::bar;
+ int ::alias::func4() { return 40; }
+ )cpp");
+
+ // All the qualifiers should be preserved exactly as written.
+ EXPECT_THAT(getSymbols("foo.cpp"),
+ UnorderedElementsAre(
+ WithName("foo"), WithName("foo::bar::Cls"),
+ WithName("foo::bar::func1"), WithName("::foo::bar::func2"),
+ WithName("using namespace foo"), WithName("bar::func3"),
+ WithName("alias"), WithName("::alias::func4")));
+}
+
+TEST_F(DocumentSymbolsTest, QualifiersWithTemplateArgs) {
+ addFile("foo.cpp", R"cpp(
+ template <typename T, typename U = double> class Foo;
+
+ template <>
+ class Foo<int, double> {
+ int method1();
+ int method2();
+ int method3();
+ };
+
+ using int_type = int;
+
+ // Typedefs should be preserved!
+ int Foo<int_type, double>::method1() { return 10; }
+
+ // Default arguments should not be shown!
+ int Foo<int>::method2() { return 20; }
+
+ using Foo_type = Foo<int>;
+ // If the whole type is aliased, this should be preserved too!
+ int Foo_type::method3() { return 30; }
+ )cpp");
+ EXPECT_THAT(
+ getSymbols("foo.cpp"),
+ UnorderedElementsAre(WithName("Foo"), WithName("Foo<int, double>"),
+ WithName("int_type"),
+ WithName("Foo<int_type, double>::method1"),
+ WithName("Foo<int>::method2"), WithName("Foo_type"),
+ WithName("Foo_type::method3")));
+}
+
} // namespace clangd
} // namespace clang