Index: clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp =================================================================== --- clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp +++ clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp @@ -9,6 +9,7 @@ #include "SymbolCollector.h" #include "../CodeCompletionStrings.h" +#include "Logger.h" #include "clang/AST/DeclCXX.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/SourceManager.h" @@ -97,8 +98,8 @@ // * symbols in namespaces or translation unit scopes (e.g. no class // members) // * enum constants in unscoped enum decl (e.g. "red" in "enum {red};") - auto InTopLevelScope = - hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl())); + auto InTopLevelScope = hasDeclContext( + anyOf(namespaceDecl(), translationUnitDecl(), linkageSpecDecl())); if (match(decl(allOf(Opts.IndexMainFiles ? decl() : decl(unless(isExpansionInMainFile())), @@ -180,7 +181,17 @@ return true; auto &SM = ND->getASTContext().getSourceManager(); - std::string QName = ND->getQualifiedNameAsString(); + + std::string QName; + llvm::raw_string_ostream OS(QName); + PrintingPolicy Policy(ASTCtx->getLangOpts()); + // Note that inline namespaces are treated as transparent scopes. This + // reflects the way they're most commonly used for lookup. Ideally we'd + // include them, but at query time it's hard to find all the inline + // namespaces to query: the preamble doesn't have a dedicated list. + Policy.SuppressUnwrittenScope = true; + ND->printQualifiedName(OS, Policy); + OS.flush(); Symbol S; S.ID = std::move(ID); Index: clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp @@ -198,8 +198,7 @@ runSymbolCollector(Header, /*Main=*/""); EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Red"), QName("Color"), QName("Green"), QName("Color2"), - QName("ns"), - QName("ns::Black"))); + QName("ns"), QName("ns::Black"))); } TEST_F(SymbolCollectorTest, IgnoreNamelessSymbols) { @@ -321,6 +320,53 @@ EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"))); } +TEST_F(SymbolCollectorTest, Scopes) { + const std::string Header = R"( + namespace na { + class Foo {}; + namespace nb { + class Bar {}; + } + } + )"; + runSymbolCollector(Header, /*Main=*/""); + EXPECT_THAT(Symbols, + UnorderedElementsAre(QName("na"), QName("na::nb"), + QName("na::Foo"), QName("na::nb::Bar"))); +} + +TEST_F(SymbolCollectorTest, ExternC) { + const std::string Header = R"( + extern "C" { class Foo {}; } + namespace na { + extern "C" { class Bar {}; } + } + )"; + runSymbolCollector(Header, /*Main=*/""); + EXPECT_THAT(Symbols, + UnorderedElementsAre(QName("na"), QName("Foo"), QName("Bar"))); +} + +TEST_F(SymbolCollectorTest, SkipInlineNamespace) { + const std::string Header = R"( + namespace na { + inline namespace nb { + class Foo {}; + } + } + namespace na { + // This is still inlined. + namespace nb { + class Bar {}; + } + } + )"; + runSymbolCollector(Header, /*Main=*/""); + EXPECT_THAT(Symbols, + UnorderedElementsAre(QName("na"), QName("na::nb"), + QName("na::Foo"), QName("na::Bar"))); +} + TEST_F(SymbolCollectorTest, SymbolWithDocumentation) { const std::string Header = R"( namespace nx {