Index: clang-tools-extra/clangd/index/SymbolCollector.h =================================================================== --- clang-tools-extra/clangd/index/SymbolCollector.h +++ clang-tools-extra/clangd/index/SymbolCollector.h @@ -28,13 +28,14 @@ /// It collects most declarations except: /// - Implicit declarations /// - Anonymous declarations (anonymous enum/class/struct, etc) -/// - Declarations in anonymous namespaces +/// - Declarations in anonymous namespaces in headers /// - Local declarations (in function bodies, blocks, etc) -/// - Declarations in main files /// - Template specializations /// - Library-specific private declarations (e.g. private declaration generated /// by protobuf compiler) /// +/// References to main-file symbols are not collected. +/// /// See also shouldCollectSymbol(...). /// /// Clients (e.g. clangd) can use SymbolCollector together with @@ -81,7 +82,7 @@ /// Returns true is \p ND should be collected. static bool shouldCollectSymbol(const NamedDecl &ND, const ASTContext &ASTCtx, - const Options &Opts); + const Options &Opts, bool IsMainFileSymbol); void initialize(ASTContext &Ctx) override; @@ -105,7 +106,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 @@ -241,22 +241,16 @@ bool SymbolCollector::shouldCollectSymbol(const NamedDecl &ND, const ASTContext &ASTCtx, - const Options &Opts) { + const Options &Opts, + bool IsMainFileSymbol) { if (ND.isImplicit()) return false; // Skip anonymous declarations, e.g (anonymous enum/class/struct). 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()) + // Skip symbols in anonymous namespaces in header files. + if (!IsMainFileSymbol && ND.isInAnonymousNamespace()) return false; // We want most things but not "local" symbols such as symbols inside @@ -286,10 +280,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; @@ -336,9 +326,12 @@ if (IsOnlyRef && !CollectRef) return true; - if (!shouldCollectSymbol(*ND, *ASTCtx, Opts)) + + bool IsMainFileSymbol = SM.isInMainFile(SM.getExpansionLoc(ND->getBeginLoc())); + if (!shouldCollectSymbol(*ND, *ASTCtx, Opts, IsMainFileSymbol)) return true; - if (CollectRef && !isa(ND) && + // Do not store references to main-file symbols. + if (CollectRef && !IsMainFileSymbol && !isa(ND) && (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) DeclRefs[ND].emplace_back(SpellingLoc, Roles); // Don't continue indexing if this is a mere reference. @@ -352,13 +345,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); @@ -507,7 +500,8 @@ } const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, - SymbolID ID) { + SymbolID ID, + bool IsMainFileSymbol) { auto &Ctx = ND.getASTContext(); auto &SM = Ctx.getSourceManager(); @@ -518,7 +512,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 collect main-file symbols, but do not use them for code completion. + 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 @@ -125,7 +125,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"); @@ -135,7 +135,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"); @@ -188,7 +188,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: @@ -143,10 +144,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 +188,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 @@ -116,7 +116,7 @@ assert(AST.hasValue()); return SymbolCollector::shouldCollectSymbol( Qualified ? findDecl(*AST, Name) : findUnqualifiedDecl(*AST, Name), - AST->getASTContext(), SymbolCollector::Options()); + AST->getASTContext(), SymbolCollector::Options(), true); } protected: @@ -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) { @@ -296,7 +300,7 @@ const char* kStr = "123"; namespace { - void ff() {} // ignore + void ff() {} } void f1() {} @@ -418,7 +422,7 @@ void $printdef[[print]]() {} // Declared/defined in main only. - int Y; + int $ydecl[[Y]]; )cpp"); runSymbolCollector(Header.code(), Main.code()); EXPECT_THAT(Symbols, @@ -430,7 +434,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"))))); } TEST_F(SymbolCollectorTest, Refs) { @@ -509,17 +514,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) { @@ -622,22 +635,28 @@ DeclURI(TestHeaderURI)))); } -TEST_F(SymbolCollectorTest, IgnoreSymbolsInMainFile) { - const std::string Header = R"( +TEST_F(SymbolCollectorTest, SymbolsInMainFile) { + const std::string Main = R"( class Foo {}; void f1(); inline void f2() {} - )"; - const std::string Main = R"( + namespace { - void ff() {} // ignore + void ff() {} } - void main_f() {} // ignore + namespace foo { + namespace { + class Bar {}; + } + } + void main_f() {} void f1() {} )"; - runSymbolCollector(Header, Main); + runSymbolCollector(/*Header=*/"", Main); EXPECT_THAT(Symbols, - UnorderedElementsAre(QName("Foo"), QName("f1"), QName("f2"))); + UnorderedElementsAre(QName("Foo"), QName("f1"), QName("f2"), + QName("ff"), QName("foo"), QName("foo::Bar"), + QName("main_f"))); } TEST_F(SymbolCollectorTest, ClassMembers) { @@ -974,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) { @@ -1001,7 +1021,7 @@ CollectorOpts.CollectMacro = true; runSymbolCollector(Header.code(), Main); EXPECT_THAT(Symbols, - UnorderedElementsAre(QName("p"), + UnorderedElementsAre(QName("p"), QName("t"), AllOf(QName("X"), DeclURI(TestHeaderURI), IncludeHeader(TestHeaderURI)), AllOf(Labeled("MAC(x)"), RefCount(0),