Index: clang-tools-extra/clangd/index/SymbolCollector.h =================================================================== --- clang-tools-extra/clangd/index/SymbolCollector.h +++ clang-tools-extra/clangd/index/SymbolCollector.h @@ -105,7 +105,7 @@ void finish() override; private: - const Symbol *addDeclaration(const NamedDecl &, SymbolID); + const Symbol *addDeclaration(const NamedDecl &, SymbolID, bool IsMainFileSymbol); void addDefinition(const NamedDecl &, const Symbol &DeclSymbol); // All Symbols collected from the AST. Index: clang-tools-extra/clangd/index/SymbolCollector.cpp =================================================================== --- clang-tools-extra/clangd/index/SymbolCollector.cpp +++ clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -268,17 +268,6 @@ if (ND.getDeclName().isEmpty()) return false; - // FIXME: figure out a way to handle internal linkage symbols (e.g. static - // variables, function) defined in the .cc files. Also we skip the symbols - // in anonymous namespace as the qualifier names of these symbols are like - // `foo::::bar`, which need a special handling. - // In real world projects, we have a relatively large set of header files - // that define static variables (like "static const int A = 1;"), we still - // want to collect these symbols, although they cause potential ODR - // violations. - if (ND.isInAnonymousNamespace()) - return false; - // We want most things but not "local" symbols such as symbols inside // FunctionDecl, BlockDecl, ObjCMethodDecl and OMPDeclareReductionDecl. // FIXME: Need a matcher for ExportDecl in order to include symbols declared @@ -306,10 +295,6 @@ explicitTemplateSpecialization(ND)) return false; - const auto &SM = ASTCtx.getSourceManager(); - // Skip decls in the main file. - if (SM.isInMainFile(SM.getExpansionLoc(ND.getBeginLoc()))) - return false; // Avoid indexing internal symbols in protobuf generated headers. if (isPrivateProtoDecl(ND)) return false; @@ -354,8 +339,14 @@ !(Roles & (static_cast(index::SymbolRole::Declaration) | static_cast(index::SymbolRole::Definition))); + bool IsMainFileSymbol = SM.isInMainFile(SM.getExpansionLoc(ND->getBeginLoc())); + // Do not store references to main-file symbols. + if (IsMainFileSymbol) + CollectRef = false; + if (IsOnlyRef && !CollectRef) return true; + if (!shouldCollectSymbol(*ND, *ASTCtx, Opts)) return true; if (CollectRef && !isa(ND) && @@ -372,13 +363,13 @@ const NamedDecl &OriginalDecl = *cast(ASTNode.OrigD); const Symbol *BasicSymbol = Symbols.find(*ID); if (!BasicSymbol) // Regardless of role, ND is the canonical declaration. - BasicSymbol = addDeclaration(*ND, std::move(*ID)); + BasicSymbol = addDeclaration(*ND, std::move(*ID), IsMainFileSymbol); else if (isPreferredDeclaration(OriginalDecl, Roles)) // If OriginalDecl is preferred, replace the existing canonical // declaration (e.g. a class forward declaration). There should be at most // one duplicate as we expect to see only one preferred declaration per // TU, because in practice they are definitions. - BasicSymbol = addDeclaration(OriginalDecl, std::move(*ID)); + BasicSymbol = addDeclaration(OriginalDecl, std::move(*ID), IsMainFileSymbol); if (Roles & static_cast(index::SymbolRole::Definition)) addDefinition(OriginalDecl, *BasicSymbol); @@ -395,13 +386,19 @@ const auto &SM = PP->getSourceManager(); auto DefLoc = MI->getDefinitionLoc(); - if (SM.isInMainFile(SM.getExpansionLoc(DefLoc))) - return true; + bool IsMainFileSymbol = SM.isInMainFile(SM.getExpansionLoc(DefLoc)); + // Header guards are not interesting in index. Builtin macros don't have // useful locations and are not needed for code completions. if (MI->isUsedForHeaderGuard() || MI->isBuiltinMacro()) return true; + // Also avoid storing predefined macros like __DBL_MIN__. + // These are not caught by MI->isBuiltinMacro(), but we can + // detect them by their definition not being in any file. + if (IsMainFileSymbol && SM.getFilename(DefLoc).empty()) + return true; + // Mark the macro as referenced if this is a reference coming from the main // file. The macro may not be an interesting symbol, but it's cheaper to check // at the end. @@ -426,7 +423,8 @@ Symbol S; S.ID = std::move(*ID); S.Name = Name->getName(); - S.Flags |= Symbol::IndexedForCodeCompletion; + if (!IsMainFileSymbol) + S.Flags |= Symbol::IndexedForCodeCompletion; S.SymInfo = index::getSymbolInfoForMacro(*MI); std::string FileURI; // FIXME: use the result to filter out symbols. @@ -531,7 +529,8 @@ } const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, - SymbolID ID) { + SymbolID ID, + bool IsMainFileSymbol) { auto &Ctx = ND.getASTContext(); auto &SM = Ctx.getSourceManager(); @@ -542,7 +541,8 @@ // FIXME: this returns foo:bar: for objective-C methods, we prefer only foo: // for consistency with CodeCompletionString and a clean name/signature split. - if (isIndexedForCodeCompletion(ND, Ctx)) + // We only collect main-file symbols for "textDocument/workspaceSymbols". + if (!IsMainFileSymbol && isIndexedForCodeCompletion(ND, Ctx)) S.Flags |= Symbol::IndexedForCodeCompletion; if (isImplementationDetail(&ND)) S.Flags |= Symbol::ImplementationDetail; Index: clang-tools-extra/unittests/clangd/BackgroundIndexTests.cpp =================================================================== --- clang-tools-extra/unittests/clangd/BackgroundIndexTests.cpp +++ clang-tools-extra/unittests/clangd/BackgroundIndexTests.cpp @@ -103,7 +103,7 @@ ASSERT_TRUE(Idx.blockUntilIdleForTest()); EXPECT_THAT( runFuzzyFind(Idx, ""), - UnorderedElementsAre(Named("common"), Named("A_CC"), + UnorderedElementsAre(Named("common"), Named("A_CC"), Named("g"), AllOf(Named("f_b"), Declared(), Not(Defined())))); Cmd.Filename = testPath("root/B.cc"); @@ -113,7 +113,7 @@ ASSERT_TRUE(Idx.blockUntilIdleForTest()); // B_CC is dropped as we don't collect symbols from A.h in this compilation. EXPECT_THAT(runFuzzyFind(Idx, ""), - UnorderedElementsAre(Named("common"), Named("A_CC"), + UnorderedElementsAre(Named("common"), Named("A_CC"), Named("g"), AllOf(Named("f_b"), Declared(), Defined()))); auto Syms = runFuzzyFind(Idx, "common"); @@ -166,7 +166,7 @@ auto ShardSource = MSS.loadShard(testPath("root/A.cc")); EXPECT_NE(ShardSource, nullptr); - EXPECT_THAT(*ShardSource->Symbols, UnorderedElementsAre()); + EXPECT_THAT(*ShardSource->Symbols, UnorderedElementsAre(Named("g"))); EXPECT_THAT(*ShardSource->Refs, RefsAre({FileURI("unittest:///root/A.cc")})); } Index: clang-tools-extra/unittests/clangd/FindSymbolsTests.cpp =================================================================== --- clang-tools-extra/unittests/clangd/FindSymbolsTests.cpp +++ clang-tools-extra/unittests/clangd/FindSymbolsTests.cpp @@ -64,6 +64,7 @@ : Server(CDB, FSProvider, DiagConsumer, optsForTests()) { // Make sure the test root directory is created. FSProvider.Files[testPath("unused")] = ""; + CDB.ExtraClangFlags = {"-xc++"}; } protected: @@ -89,13 +90,16 @@ } // namespace -TEST_F(WorkspaceSymbolsTest, NoMacro) { +TEST_F(WorkspaceSymbolsTest, Macros) { addFile("foo.cpp", R"cpp( #define MACRO X )cpp"); - // Macros are not in the index. - EXPECT_THAT(getSymbols("macro"), IsEmpty()); + // LSP's SymbolKind doesn't have a "Macro" kind, and + // indexSymbolKindToSymbolKind() currently maps macros + // to SymbolKind::String. + EXPECT_THAT(getSymbols("macro"), + ElementsAre(AllOf(QName("MACRO"), WithKind(SymbolKind::String)))); } TEST_F(WorkspaceSymbolsTest, NoLocals) { @@ -143,10 +147,11 @@ TEST_F(WorkspaceSymbolsTest, InMainFile) { addFile("foo.cpp", R"cpp( - int test() { - } + int test() {} + static test2() {} )cpp"); - EXPECT_THAT(getSymbols("test"), IsEmpty()); + EXPECT_THAT(getSymbols("test"), + ElementsAre(QName("test"), QName("test2"))); } TEST_F(WorkspaceSymbolsTest, Namespaces) { @@ -186,7 +191,7 @@ addFile("foo.cpp", R"cpp( #include "foo.h" )cpp"); - EXPECT_THAT(getSymbols("test"), IsEmpty()); + EXPECT_THAT(getSymbols("test"), ElementsAre(QName("test"))); } TEST_F(WorkspaceSymbolsTest, MultiFile) { Index: clang-tools-extra/unittests/clangd/SymbolCollectorTests.cpp =================================================================== --- clang-tools-extra/unittests/clangd/SymbolCollectorTests.cpp +++ clang-tools-extra/unittests/clangd/SymbolCollectorTests.cpp @@ -132,18 +132,22 @@ class X{}; auto f() { int Local; } // auto ensures function body is parsed. struct { int x } var; - namespace { class InAnonymous {}; } } )", - "class InMain {};"); + R"( + class InMain {}; + namespace { class InAnonymous {}; } + static void g(); + )"); auto AST = File.build(); EXPECT_TRUE(shouldCollect("nx")); EXPECT_TRUE(shouldCollect("nx::X")); EXPECT_TRUE(shouldCollect("nx::f")); + EXPECT_TRUE(shouldCollect("InMain")); + EXPECT_TRUE(shouldCollect("InAnonymous", /*Qualified=*/false)); + EXPECT_TRUE(shouldCollect("g")); - EXPECT_FALSE(shouldCollect("InMain")); EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false)); - EXPECT_FALSE(shouldCollect("InAnonymous", /*Qualified=*/false)); } TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) { @@ -297,7 +301,7 @@ const char* kStr = "123"; namespace { - void ff() {} // ignore + void ff() {} } void f1() {} @@ -338,6 +342,7 @@ AllOf(QName("Friend"), ForCodeCompletion(true)), AllOf(QName("f1"), ForCodeCompletion(true)), AllOf(QName("f2"), ForCodeCompletion(true)), + AllOf(QName("ff"), ForCodeCompletion(true)), AllOf(QName("KInt"), ForCodeCompletion(true)), AllOf(QName("kStr"), ForCodeCompletion(true)), AllOf(QName("foo"), ForCodeCompletion(true)), @@ -419,7 +424,7 @@ void $printdef[[print]]() {} // Declared/defined in main only. - int Y; + int $ydecl[[Y]]; )cpp"); runSymbolCollector(Header.code(), Main.code()); EXPECT_THAT( @@ -432,7 +437,8 @@ AllOf(QName("print"), DeclRange(Header.range("printdecl")), DefRange(Main.range("printdef"))), AllOf(QName("Z"), DeclRange(Header.range("zdecl"))), - AllOf(QName("foo"), DeclRange(Header.range("foodecl"))) + AllOf(QName("foo"), DeclRange(Header.range("foodecl"))), + AllOf(QName("Y"), DeclRange(Main.range("ydecl"))) )); } @@ -512,17 +518,25 @@ W* w2 = nullptr; // only one usage counts X x(); class V; - V* v = nullptr; // Used, but not eligible for indexing. class Y{}; // definition doesn't count as a reference + V* v = nullptr; GLOBAL_Z(z); // Not a reference to Z, we don't spell the type. )"; CollectorOpts.CountReferences = true; runSymbolCollector(Header, Main); EXPECT_THAT(Symbols, - UnorderedElementsAre(AllOf(QName("W"), RefCount(1)), - AllOf(QName("X"), RefCount(1)), - AllOf(QName("Y"), RefCount(0)), - AllOf(QName("Z"), RefCount(0)), QName("y"))); + UnorderedElementsAreArray( + {AllOf(QName("W"), RefCount(1)), + AllOf(QName("X"), RefCount(1)), + AllOf(QName("Y"), RefCount(0)), + AllOf(QName("Z"), RefCount(0)), + AllOf(QName("y"), RefCount(0)), + AllOf(QName("z"), RefCount(0)), + AllOf(QName("x"), RefCount(0)), + AllOf(QName("w"), RefCount(0)), + AllOf(QName("w2"), RefCount(0)), + AllOf(QName("V"), RefCount(1)), + AllOf(QName("v"), RefCount(0))})); } TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) { @@ -626,7 +640,7 @@ DeclURI(TestHeaderURI)))); } -TEST_F(SymbolCollectorTest, IgnoreSymbolsInMainFile) { +TEST_F(SymbolCollectorTest, SymbolsInMainFile) { const std::string Header = R"( class Foo {}; void f1(); @@ -634,14 +648,15 @@ )"; const std::string Main = R"( namespace { - void ff() {} // ignore + void ff() {} } - void main_f() {} // ignore + void main_f() {} void f1() {} )"; runSymbolCollector(Header, Main); EXPECT_THAT(Symbols, - UnorderedElementsAre(QName("Foo"), QName("f1"), QName("f2"))); + UnorderedElementsAre(QName("Foo"), QName("f1"), QName("f2"), + QName("ff"), QName("main_f"))); } TEST_F(SymbolCollectorTest, ClassMembers) { @@ -978,7 +993,8 @@ CollectorOpts.CountReferences = true; runSymbolCollector(Header, Main); EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), RefCount(1)), - AllOf(QName("Y"), RefCount(1)))); + AllOf(QName("Y"), RefCount(1)), + AllOf(QName("C"), RefCount(0)))); } TEST_F(SymbolCollectorTest, Origin) { @@ -997,21 +1013,23 @@ MAC(p); )"); - const std::string Main = R"( - #define MAIN 1 // not indexed + Annotations Main(R"( + #define $main[[MAIN]] 1 USED(t); - )"; + )"); CollectorOpts.CountReferences = true; CollectorOpts.CollectMacro = true; - runSymbolCollector(Header.code(), Main); + runSymbolCollector(Header.code(), Main.code()); EXPECT_THAT(Symbols, - UnorderedElementsAre(QName("p"), + UnorderedElementsAre(QName("p"), QName("t"), AllOf(QName("X"), DeclURI(TestHeaderURI), IncludeHeader(TestHeaderURI)), AllOf(Labeled("MAC(x)"), RefCount(0), DeclRange(Header.range("mac"))), AllOf(Labeled("USED(y)"), RefCount(1), - DeclRange(Header.range("used"))))); + DeclRange(Header.range("used"))), + AllOf(Labeled("MAIN"), RefCount(0), + DeclRange(Main.range("main"))))); } TEST_F(SymbolCollectorTest, DeprecatedSymbols) {