nridge updated this revision to Diff 189275. nridge added a comment. Add tests involving templates and template specializations
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D58880/new/ https://reviews.llvm.org/D58880 Files: clang-tools-extra/clangd/ClangdServer.cpp clang-tools-extra/clangd/XRefs.cpp clang-tools-extra/clangd/XRefs.h clang-tools-extra/clangd/index/Background.cpp clang-tools-extra/clangd/index/FileIndex.cpp clang-tools-extra/clangd/index/FileIndex.h clang-tools-extra/clangd/index/Index.cpp clang-tools-extra/clangd/index/Index.h clang-tools-extra/clangd/index/MemIndex.cpp clang-tools-extra/clangd/index/MemIndex.h clang-tools-extra/clangd/index/Merge.cpp clang-tools-extra/clangd/index/Merge.h clang-tools-extra/clangd/index/Serialization.cpp clang-tools-extra/clangd/index/Serialization.h clang-tools-extra/clangd/index/SymbolCollector.cpp clang-tools-extra/clangd/index/SymbolCollector.h clang-tools-extra/clangd/index/YAMLSerialization.cpp clang-tools-extra/clangd/index/dex/Dex.cpp clang-tools-extra/clangd/index/dex/Dex.h clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp clang-tools-extra/unittests/clangd/DiagnosticsTests.cpp clang-tools-extra/unittests/clangd/FileIndexTests.cpp clang-tools-extra/unittests/clangd/IndexTests.cpp clang-tools-extra/unittests/clangd/TestTU.cpp clang-tools-extra/unittests/clangd/XRefsTests.cpp
Index: clang-tools-extra/unittests/clangd/XRefsTests.cpp =================================================================== --- clang-tools-extra/unittests/clangd/XRefsTests.cpp +++ clang-tools-extra/unittests/clangd/XRefsTests.cpp @@ -1773,6 +1773,175 @@ EXPECT_THAT(typeParents(Child3), ElementsAre()); } +SymbolID findSymbolIDByName(llvm::StringRef Name, SymbolIndex *Index) { + SymbolID Result; + FuzzyFindRequest Request; + Request.Query = Name; + Request.AnyScope = true; + Request.Limit = 1; + int ResultCount = 0; + Index->fuzzyFind(Request, [&](const Symbol &S) { + Result = S.ID; + ++ResultCount; + }); + EXPECT_EQ(1, ResultCount); + return Result; +} + +std::vector<SymbolID> collectSubtypes(SymbolID Type, SymbolIndex *Index) { + std::vector<SymbolID> Result; + Index->relations(Type, RelationKind::Subtype, + [&Result](const SymbolID &ID) { Result.push_back(ID); }); + return Result; +} + +TEST(Subtypes, SimpleInheritance) { + Annotations Source(R"cpp( +struct Parent { + int a; +}; + +struct Child1 : Parent { + int b; +}; + +struct Child2 : Child1 { + int c; +}; +)cpp"); + + TestTU TU = TestTU::withCode(Source.code()); + auto Index = TU.index(); + + SymbolID Parent = findSymbolIDByName("Parent", Index.get()); + SymbolID Child1 = findSymbolIDByName("Child1", Index.get()); + SymbolID Child2 = findSymbolIDByName("Child2", Index.get()); + + EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(Child1)); + EXPECT_THAT(collectSubtypes(Child1, Index.get()), ElementsAre(Child2)); +} + +TEST(Subtypes, MultipleInheritance) { + Annotations Source(R"cpp( +struct Parent1 { + int a; +}; + +struct Parent2 { + int b; +}; + +struct Parent3 : Parent2 { + int c; +}; + +struct Child : Parent1, Parent3 { + int d; +}; +)cpp"); + + TestTU TU = TestTU::withCode(Source.code()); + auto Index = TU.index(); + + SymbolID Parent1 = findSymbolIDByName("Parent1", Index.get()); + SymbolID Parent2 = findSymbolIDByName("Parent2", Index.get()); + SymbolID Parent3 = findSymbolIDByName("Parent3", Index.get()); + SymbolID Child = findSymbolIDByName("Child", Index.get()); + + EXPECT_THAT(collectSubtypes(Parent1, Index.get()), ElementsAre(Child)); + EXPECT_THAT(collectSubtypes(Parent2, Index.get()), ElementsAre(Parent3)); + EXPECT_THAT(collectSubtypes(Parent3, Index.get()), ElementsAre(Child)); +} + +TEST(Subtypes, ClassTemplate) { + Annotations Source(R"cpp( +struct Parent {}; + +template <typename T> +struct Child : Parent {}; +)cpp"); + + TestTU TU = TestTU::withCode(Source.code()); + auto Index = TU.index(); + + SymbolID Parent = findSymbolIDByName("Parent", Index.get()); + SymbolID Child = findSymbolIDByName("Child", Index.get()); + + EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(Child)); +} + +// This test fails because explicit class template specializations +// are not currently indexed. +TEST(DISABLED_Subtypes, TemplateSpec1) { + Annotations Source(R"cpp( +template <typename T> +struct Parent {}; + +template <> +struct Parent<int> {}; + +struct Child1 : Parent<float> {}; + +struct Child2 : Parent<int> {}; +)cpp"); + + TestTU TU = TestTU::withCode(Source.code()); + auto Index = TU.index(); + + SymbolID Parent = findSymbolIDByName("Parent", Index.get()); + // Even if we start storing explicit specializations in the index, + // we will likely need additional changes for + // findSymbolIDByName("Parent<int>") to find it. + SymbolID ParentSpec = findSymbolIDByName("Parent<int>", Index.get()); + SymbolID Child1 = findSymbolIDByName("Child1", Index.get()); + SymbolID Child2 = findSymbolIDByName("Child2", Index.get()); + + EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(Child1)); + EXPECT_THAT(collectSubtypes(ParentSpec, Index.get()), ElementsAre(Child2)); +} + +// This test fails because explicit class template specializations +// are not currently indexed. +TEST(DISABLED_Subtypes, TemplateSpec2) { + Annotations Source(R"cpp( +struct Parent {}; + +template <typename T> +struct Child {}; + +template <> +struct Child<int> : Parent {}; +)cpp"); + + TestTU TU = TestTU::withCode(Source.code()); + auto Index = TU.index(); + + SymbolID Parent = findSymbolIDByName("Parent", Index.get()); + SymbolID Child = findSymbolIDByName("Child", Index.get()); + SymbolID ChildSpec = findSymbolIDByName("Child<int>", Index.get()); + + EXPECT_THAT(collectSubtypes(Parent, Index.get()), + ElementsAre(Child, ChildSpec)); +} + +TEST(Subtypes, DependentBase) { + Annotations Source(R"cpp( +template <typename T> +struct Parent {}; + +template <typename T> +struct Child : Parent<T> {}; +)cpp"); + + TestTU TU = TestTU::withCode(Source.code()); + auto Index = TU.index(); + + SymbolID Parent = findSymbolIDByName("Parent", Index.get()); + SymbolID Child = findSymbolIDByName("Child", Index.get()); + + EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(Child)); +} + // Parts of getTypeHierarchy() are tested in more detail by the // FindRecordTypeAt.* and TypeParents.* tests above. This test exercises the // entire operation. Index: clang-tools-extra/unittests/clangd/TestTU.cpp =================================================================== --- clang-tools-extra/unittests/clangd/TestTU.cpp +++ clang-tools-extra/unittests/clangd/TestTU.cpp @@ -65,7 +65,9 @@ std::unique_ptr<SymbolIndex> TestTU::index() const { auto AST = build(); - auto Idx = llvm::make_unique<FileIndex>(/*UseDex=*/true); + // UseDex is temporarily set to false so we can test subtypes support + // before implementing it in Dex. + auto Idx = llvm::make_unique<FileIndex>(/*UseDex=*/false); Idx->updatePreamble(Filename, AST.getASTContext(), AST.getPreprocessorPtr(), AST.getCanonicalIncludes()); Idx->updateMain(Filename, AST); Index: clang-tools-extra/unittests/clangd/IndexTests.cpp =================================================================== --- clang-tools-extra/unittests/clangd/IndexTests.cpp +++ clang-tools-extra/unittests/clangd/IndexTests.cpp @@ -77,8 +77,9 @@ auto Token = std::make_shared<int>(); std::weak_ptr<int> WeakToken = Token; - SwapIndex S(llvm::make_unique<MemIndex>( - SymbolSlab(), RefSlab(), std::move(Token), /*BackingDataSize=*/0)); + SwapIndex S(llvm::make_unique<MemIndex>(SymbolSlab(), RefSlab(), + RelationSlab(), std::move(Token), + /*BackingDataSize=*/0)); EXPECT_FALSE(WeakToken.expired()); // Current MemIndex keeps it alive. S.reset(llvm::make_unique<MemIndex>()); // Now the MemIndex is destroyed. EXPECT_TRUE(WeakToken.expired()); // So the token is too. @@ -90,12 +91,13 @@ FuzzyFindRequest Req; Req.Query = "2"; Req.AnyScope = true; - MemIndex I(Symbols, RefSlab()); + MemIndex I(Symbols, RefSlab(), RelationSlab()); EXPECT_THAT(match(I, Req), ElementsAre("2")); } TEST(MemIndexTest, MemIndexLimitedNumMatches) { - auto I = MemIndex::build(generateNumSymbols(0, 100), RefSlab()); + auto I = + MemIndex::build(generateNumSymbols(0, 100), RefSlab(), RelationSlab()); FuzzyFindRequest Req; Req.Query = "5"; Req.AnyScope = true; @@ -110,7 +112,7 @@ TEST(MemIndexTest, FuzzyMatch) { auto I = MemIndex::build( generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}), - RefSlab()); + RefSlab(), RelationSlab()); FuzzyFindRequest Req; Req.Query = "lol"; Req.AnyScope = true; @@ -120,8 +122,8 @@ } TEST(MemIndexTest, MatchQualifiedNamesWithoutSpecificScope) { - auto I = - MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab()); + auto I = MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(), + RelationSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.AnyScope = true; @@ -129,8 +131,8 @@ } TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) { - auto I = - MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab()); + auto I = MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(), + RelationSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {""}; @@ -139,7 +141,8 @@ TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) { auto I = MemIndex::build( - generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), RefSlab()); + generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), RefSlab(), + RelationSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -148,7 +151,8 @@ TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) { auto I = MemIndex::build( - generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), RefSlab()); + generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), RefSlab(), + RelationSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::", "b::"}; @@ -156,7 +160,8 @@ } TEST(MemIndexTest, NoMatchNestedScopes) { - auto I = MemIndex::build(generateSymbols({"a::y1", "a::b::y2"}), RefSlab()); + auto I = MemIndex::build(generateSymbols({"a::y1", "a::b::y2"}), RefSlab(), + RelationSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -164,7 +169,8 @@ } TEST(MemIndexTest, IgnoreCases) { - auto I = MemIndex::build(generateSymbols({"ns::ABC", "ns::abc"}), RefSlab()); + auto I = MemIndex::build(generateSymbols({"ns::ABC", "ns::abc"}), RefSlab(), + RelationSlab()); FuzzyFindRequest Req; Req.Query = "AB"; Req.Scopes = {"ns::"}; @@ -172,7 +178,8 @@ } TEST(MemIndexTest, Lookup) { - auto I = MemIndex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab()); + auto I = MemIndex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab(), + RelationSlab()); EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); @@ -182,8 +189,10 @@ } TEST(MergeIndexTest, Lookup) { - auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), RefSlab()), - J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab()); + auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), RefSlab(), + RelationSlab()), + J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab(), + RelationSlab()); MergedIndex M(I.get(), J.get()); EXPECT_THAT(lookup(M, SymbolID("ns::A")), UnorderedElementsAre("ns::A")); EXPECT_THAT(lookup(M, SymbolID("ns::B")), UnorderedElementsAre("ns::B")); @@ -197,8 +206,10 @@ } TEST(MergeIndexTest, FuzzyFind) { - auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), RefSlab()), - J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab()); + auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), RefSlab(), + RelationSlab()), + J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab(), + RelationSlab()); FuzzyFindRequest Req; Req.Scopes = {"ns::"}; EXPECT_THAT(match(MergedIndex(I.get(), J.get()), Req), Index: clang-tools-extra/unittests/clangd/FileIndexTests.cpp =================================================================== --- clang-tools-extra/unittests/clangd/FileIndexTests.cpp +++ clang-tools-extra/unittests/clangd/FileIndexTests.cpp @@ -82,7 +82,7 @@ FileSymbols FS; EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty()); - FS.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc")); + FS.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc"), nullptr); EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), UnorderedElementsAre(QName("1"), QName("2"), QName("3"))); EXPECT_THAT(getRefs(*FS.buildIndex(IndexType::Light), SymbolID("1")), @@ -91,8 +91,8 @@ TEST(FileSymbolsTest, Overlap) { FileSymbols FS; - FS.update("f1", numSlab(1, 3), nullptr); - FS.update("f2", numSlab(3, 5), nullptr); + FS.update("f1", numSlab(1, 3), nullptr, nullptr); + FS.update("f2", numSlab(3, 5), nullptr, nullptr); for (auto Type : {IndexType::Light, IndexType::Heavy}) EXPECT_THAT(runFuzzyFind(*FS.buildIndex(Type), ""), UnorderedElementsAre(QName("1"), QName("2"), QName("3"), @@ -111,8 +111,8 @@ auto X2 = symbol("x"); X2.Definition.FileURI = "file:///x2"; - FS.update("f1", OneSymboSlab(X1), nullptr); - FS.update("f2", OneSymboSlab(X2), nullptr); + FS.update("f1", OneSymboSlab(X1), nullptr, nullptr); + FS.update("f2", OneSymboSlab(X2), nullptr, nullptr); for (auto Type : {IndexType::Light, IndexType::Heavy}) EXPECT_THAT( runFuzzyFind(*FS.buildIndex(Type, DuplicateHandling::Merge), "x"), @@ -124,14 +124,14 @@ FileSymbols FS; SymbolID ID("1"); - FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc")); + FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc"), nullptr); auto Symbols = FS.buildIndex(IndexType::Light); EXPECT_THAT(runFuzzyFind(*Symbols, ""), UnorderedElementsAre(QName("1"), QName("2"), QName("3"))); EXPECT_THAT(getRefs(*Symbols, ID), RefsAre({FileURI("f1.cc")})); - FS.update("f1", nullptr, nullptr); + FS.update("f1", nullptr, nullptr, nullptr); auto Empty = FS.buildIndex(IndexType::Light); EXPECT_THAT(runFuzzyFind(*Empty, ""), IsEmpty()); EXPECT_THAT(getRefs(*Empty, ID), ElementsAre()); Index: clang-tools-extra/unittests/clangd/DiagnosticsTests.cpp =================================================================== --- clang-tools-extra/unittests/clangd/DiagnosticsTests.cpp +++ clang-tools-extra/unittests/clangd/DiagnosticsTests.cpp @@ -71,7 +71,6 @@ return true; } - // Helper function to make tests shorter. Position pos(int line, int character) { Position Res; @@ -316,7 +315,7 @@ Sym.IncludeHeaders.emplace_back(S.IncludeHeader, 1); Slab.insert(Sym); } - return MemIndex::build(std::move(Slab).build(), RefSlab()); + return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab()); } TEST(IncludeFixerTest, IncompleteType) { @@ -372,7 +371,8 @@ SymbolSlab::Builder Slab; Slab.insert(Sym); - auto Index = MemIndex::build(std::move(Slab).build(), RefSlab()); + auto Index = + MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab()); TU.ExternalIndex = Index.get(); EXPECT_THAT(TU.build().getDiagnostics(), @@ -589,4 +589,3 @@ } // namespace } // namespace clangd } // namespace clang - Index: clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp =================================================================== --- clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp +++ clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp @@ -85,7 +85,7 @@ SymbolSlab::Builder Slab; for (const auto &Sym : Symbols) Slab.insert(Sym); - return MemIndex::build(std::move(Slab).build(), RefSlab()); + return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab()); } CodeCompleteResult completions(ClangdServer &Server, llvm::StringRef TestCode, @@ -999,6 +999,9 @@ void refs(const RefsRequest &, llvm::function_ref<void(const Ref &)>) const override {} + void relations(const SymbolID &, RelationKind, + llvm::function_ref<void(const SymbolID &)>) const override {} + // This is incorrect, but IndexRequestCollector is not an actual index and it // isn't used in production code. size_t estimateMemoryUsage() const override { return 0; } Index: clang-tools-extra/clangd/index/dex/Dex.h =================================================================== --- clang-tools-extra/clangd/index/dex/Dex.h +++ clang-tools-extra/clangd/index/dex/Dex.h @@ -72,6 +72,10 @@ void refs(const RefsRequest &Req, llvm::function_ref<void(const Ref &)> Callback) const override; + void + relations(const SymbolID &ID, RelationKind Kind, + llvm::function_ref<void(const SymbolID &)> Callback) const override; + size_t estimateMemoryUsage() const override; private: Index: clang-tools-extra/clangd/index/dex/Dex.cpp =================================================================== --- clang-tools-extra/clangd/index/dex/Dex.cpp +++ clang-tools-extra/clangd/index/dex/Dex.cpp @@ -259,6 +259,11 @@ } } +void Dex::relations(const SymbolID &ID, RelationKind Kind, + llvm::function_ref<void(const SymbolID &)> Callback) const { + // TODO: Implement. +} + size_t Dex::estimateMemoryUsage() const { size_t Bytes = Symbols.size() * sizeof(const Symbol *); Bytes += SymbolQuality.size() * sizeof(float); Index: clang-tools-extra/clangd/index/YAMLSerialization.cpp =================================================================== --- clang-tools-extra/clangd/index/YAMLSerialization.cpp +++ clang-tools-extra/clangd/index/YAMLSerialization.cpp @@ -29,14 +29,18 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Ref) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::SymbolID) namespace { using RefBundle = std::pair<clang::clangd::SymbolID, std::vector<clang::clangd::Ref>>; +using RelationBundle = + std::pair<clang::clangd::RelationKey, std::vector<clang::clangd::SymbolID>>; // This is a pale imitation of std::variant<Symbol, RefBundle> struct VariantEntry { llvm::Optional<clang::clangd::Symbol> Symbol; llvm::Optional<RefBundle> Refs; + llvm::Optional<RelationBundle> Relations; }; // A class helps YAML to serialize the 32-bit encoded position (Line&Column), // as YAMLIO can't directly map bitfields. @@ -51,6 +55,7 @@ using clang::clangd::Ref; using clang::clangd::RefKind; +using clang::clangd::RelationKind; using clang::clangd::Symbol; using clang::clangd::SymbolID; using clang::clangd::SymbolLocation; @@ -271,6 +276,34 @@ } }; +struct NormalizedRelationKind { + NormalizedRelationKind(IO &) {} + NormalizedRelationKind(IO &, RelationKind O) { + Kind = static_cast<uint8_t>(O); + } + + RelationKind denormalize(IO &) { return static_cast<RelationKind>(Kind); } + + uint8_t Kind = 0; +}; + +template <> struct MappingTraits<SymbolID> { + static void mapping(IO &IO, SymbolID &ID) { + MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, ID); + IO.mapRequired("ID", NSymbolID->HexString); + } +}; + +template <> struct MappingTraits<RelationBundle> { + static void mapping(IO &IO, RelationBundle &Relations) { + MappingNormalization<NormalizedRelationKind, RelationKind> NKind( + IO, Relations.first.Kind); + IO.mapRequired("Symbol", Relations.first.Symbol); + IO.mapRequired("Kind", NKind->Kind); + IO.mapRequired("Relations", Relations.second); + } +}; + template <> struct MappingTraits<VariantEntry> { static void mapping(IO &IO, VariantEntry &Variant) { if (IO.mapTag("!Symbol", Variant.Symbol.hasValue())) { @@ -281,6 +314,10 @@ if (!IO.outputting()) Variant.Refs.emplace(); MappingTraits<RefBundle>::mapping(IO, *Variant.Refs); + } else if (IO.mapTag("!Relations", Variant.Relations.hasValue())) { + if (!IO.outputting()) + Variant.Relations.emplace(); + MappingTraits<RelationBundle>::mapping(IO, *Variant.Relations); } } }; @@ -304,11 +341,18 @@ Entry.Refs = Sym; Yout << Entry; } + if (O.Relations) + for (auto &E : *O.Relations) { + VariantEntry Entry; + Entry.Relations = E; + Yout << Entry; + } } llvm::Expected<IndexFileIn> readYAML(llvm::StringRef Data) { SymbolSlab::Builder Symbols; RefSlab::Builder Refs; + RelationSlab::Builder Relations; llvm::BumpPtrAllocator Arena; // store the underlying data of Position::FileURI. llvm::UniqueStringSaver Strings(Arena); @@ -325,12 +369,16 @@ if (Variant.Refs) for (const auto &Ref : Variant.Refs->second) Refs.insert(Variant.Refs->first, Ref); + if (Variant.Relations) + for (const auto &Relation : Variant.Relations->second) + Relations.insert(Variant.Relations->first, Relation); Yin.nextDocument(); } IndexFileIn Result; Result.Symbols.emplace(std::move(Symbols).build()); Result.Refs.emplace(std::move(Refs).build()); + Result.Relations.emplace(std::move(Relations).build()); return std::move(Result); } Index: clang-tools-extra/clangd/index/SymbolCollector.h =================================================================== --- clang-tools-extra/clangd/index/SymbolCollector.h +++ clang-tools-extra/clangd/index/SymbolCollector.h @@ -108,11 +108,13 @@ SymbolSlab takeSymbols() { return std::move(Symbols).build(); } RefSlab takeRefs() { return std::move(Refs).build(); } + RelationSlab takeRelations() { return std::move(Relations).build(); } void finish() override; private: - const Symbol *addDeclaration(const NamedDecl &, SymbolID, bool IsMainFileSymbol); + const Symbol *addDeclaration(const NamedDecl &, SymbolID, + bool IsMainFileSymbol); void addDefinition(const NamedDecl &, const Symbol &DeclSymbol); // All Symbols collected from the AST. @@ -121,6 +123,8 @@ // Only symbols declared in preamble (from #include) and referenced from the // main file will be included. RefSlab::Builder Refs; + // All relations collected from the AST. + RelationSlab::Builder Relations; ASTContext *ASTCtx; std::shared_ptr<Preprocessor> PP; std::shared_ptr<GlobalCodeCompletionAllocator> CompletionAllocator; Index: clang-tools-extra/clangd/index/SymbolCollector.cpp =================================================================== --- clang-tools-extra/clangd/index/SymbolCollector.cpp +++ clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -14,6 +14,7 @@ #include "Logger.h" #include "SourceCode.h" #include "URI.h" +#include "XRefs.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" @@ -201,7 +202,7 @@ // the first seen declaration as canonical declaration is not a good enough // heuristic. bool isPreferredDeclaration(const NamedDecl &ND, index::SymbolRoleSet Roles) { - const auto& SM = ND.getASTContext().getSourceManager(); + const auto &SM = ND.getASTContext().getSourceManager(); return (Roles & static_cast<unsigned>(index::SymbolRole::Definition)) && isa<TagDecl>(&ND) && !SM.isWrittenInMainFile(SM.getExpansionLoc(ND.getLocation())); @@ -323,8 +324,8 @@ // ND is the canonical (i.e. first) declaration. If it's in the main file, // then no public declaration was visible, so assume it's main-file only. - bool IsMainFileOnly = SM.isWrittenInMainFile(SM.getExpansionLoc( - ND->getBeginLoc())); + bool IsMainFileOnly = + SM.isWrittenInMainFile(SM.getExpansionLoc(ND->getBeginLoc())); if (!shouldCollectSymbol(*ND, *ASTCtx, Opts, IsMainFileOnly)) return true; // Do not store references to main-file symbols. @@ -513,8 +514,7 @@ FilesToIndexCache.clear(); } -const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, - SymbolID ID, +const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, SymbolID ID, bool IsMainFileOnly) { auto &Ctx = ND.getASTContext(); auto &SM = Ctx.getSourceManager(); @@ -612,6 +612,26 @@ getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI)) S.Definition = *DefLoc; Symbols.insert(S); + + // Store subtype relations. + const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(&ND); + if (!RD) + return; + for (const Decl *Parent : typeParents(RD)) { + if (auto ID = getSymbolID(Parent)) { + // We are a subtype to each of our parents. + // TODO: There may be cases where the parent type is not indexed for some + // reason (e.g. currently we don't index explicit class template + // specializations). Those cases should probably be removed in due course, + // but for now there are two possible ways to handle it: + // (A) Avoid storing the relationship in such cases. + // (B) Store it anyways. Clients will likely lookup() the SymbolID + // in the index and find nothing, but that's a situation they + // probably need to handle for other reasons anyways. + // We currently do (B) because it's simpler. + Relations.insert({*ID, RelationKind::Subtype}, S.ID); + } + } } } // namespace clangd Index: clang-tools-extra/clangd/index/Serialization.h =================================================================== --- clang-tools-extra/clangd/index/Serialization.h +++ clang-tools-extra/clangd/index/Serialization.h @@ -39,6 +39,7 @@ struct IndexFileIn { llvm::Optional<SymbolSlab> Symbols; llvm::Optional<RefSlab> Refs; + llvm::Optional<RelationSlab> Relations; // Keys are URIs of the source files. llvm::Optional<IncludeGraph> Sources; }; @@ -49,6 +50,7 @@ struct IndexFileOut { const SymbolSlab *Symbols = nullptr; const RefSlab *Refs = nullptr; + const RelationSlab *Relations = nullptr; // Keys are URIs of the source files. const IncludeGraph *Sources = nullptr; // TODO: Support serializing Dex posting lists. @@ -57,7 +59,8 @@ IndexFileOut() = default; IndexFileOut(const IndexFileIn &I) : Symbols(I.Symbols ? I.Symbols.getPointer() : nullptr), - Refs(I.Refs ? I.Refs.getPointer() : nullptr) {} + Refs(I.Refs ? I.Refs.getPointer() : nullptr), + Relations(I.Relations ? I.Relations.getPointer() : nullptr) {} }; // Serializes an index file. llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O); Index: clang-tools-extra/clangd/index/Serialization.cpp =================================================================== --- clang-tools-extra/clangd/index/Serialization.cpp +++ clang-tools-extra/clangd/index/Serialization.cpp @@ -558,6 +558,7 @@ SymbolSlab Symbols; RefSlab Refs; + RelationSlab Relations; // TODO: Populate. { trace::Span Tracer("ParseIndex"); if (auto I = readIndexFile(Buffer->get()->getBuffer())) { @@ -565,6 +566,8 @@ Symbols = std::move(*I->Symbols); if (I->Refs) Refs = std::move(*I->Refs); + if (I->Relations) + Relations = std::move(*I->Relations); } else { llvm::errs() << "Bad Index: " << llvm::toString(I.takeError()) << "\n"; return nullptr; @@ -573,15 +576,18 @@ size_t NumSym = Symbols.size(); size_t NumRefs = Refs.numRefs(); + size_t NumRelations = Relations.numRelations(); trace::Span Tracer("BuildIndex"); auto Index = UseDex ? dex::Dex::build(std::move(Symbols), std::move(Refs)) - : MemIndex::build(std::move(Symbols), std::move(Refs)); + : MemIndex::build(std::move(Symbols), std::move(Refs), + std::move(Relations)); vlog("Loaded {0} from {1} with estimated memory usage {2} bytes\n" " - number of symbols: {3}\n" - " - number of refs: {4}\n", + " - number of refs: {4}\n" + " - numnber of relations: {5}", UseDex ? "Dex" : "MemIndex", SymbolFilename, - Index->estimateMemoryUsage(), NumSym, NumRefs); + Index->estimateMemoryUsage(), NumSym, NumRefs, NumRelations); return Index; } Index: clang-tools-extra/clangd/index/Merge.h =================================================================== --- clang-tools-extra/clangd/index/Merge.h +++ clang-tools-extra/clangd/index/Merge.h @@ -42,6 +42,8 @@ llvm::function_ref<void(const Symbol &)>) const override; void refs(const RefsRequest &, llvm::function_ref<void(const Ref &)>) const override; + void relations(const SymbolID &, RelationKind, + llvm::function_ref<void(const SymbolID &)>) const override; size_t estimateMemoryUsage() const override { return Dynamic->estimateMemoryUsage() + Static->estimateMemoryUsage(); } Index: clang-tools-extra/clangd/index/Merge.cpp =================================================================== --- clang-tools-extra/clangd/index/Merge.cpp +++ clang-tools-extra/clangd/index/Merge.cpp @@ -118,6 +118,14 @@ }); } +void MergedIndex::relations( + const SymbolID &ID, RelationKind Kind, + llvm::function_ref<void(const SymbolID &)> Callback) const { + // TODO: Implement properly. + Static->relations(ID, Kind, Callback); + Dynamic->relations(ID, Kind, Callback); +} + // Returns true if \p L is (strictly) preferred to \p R (e.g. by file paths). If // neither is preferred, this returns false. bool prefer(const SymbolLocation &L, const SymbolLocation &R) { Index: clang-tools-extra/clangd/index/MemIndex.h =================================================================== --- clang-tools-extra/clangd/index/MemIndex.h +++ clang-tools-extra/clangd/index/MemIndex.h @@ -20,26 +20,31 @@ public: MemIndex() = default; // All symbols and refs must outlive this index. - template <typename SymbolRange, typename RefRange> - MemIndex(SymbolRange &&Symbols, RefRange &&Refs) { + template <typename SymbolRange, typename RefRange, typename RelationRange> + MemIndex(SymbolRange &&Symbols, RefRange &&Refs, RelationRange &&Relations) { for (const Symbol &S : Symbols) Index[S.ID] = &S; for (const std::pair<SymbolID, llvm::ArrayRef<Ref>> &R : Refs) this->Refs.try_emplace(R.first, R.second.begin(), R.second.end()); + for (const std::pair<RelationKey, llvm::ArrayRef<SymbolID>> &R : Relations) + this->Relations.try_emplace(R.first, R.second.begin(), R.second.end()); } // Symbols are owned by BackingData, Index takes ownership. - template <typename SymbolRange, typename RefRange, typename Payload> - MemIndex(SymbolRange &&Symbols, RefRange &&Refs, Payload &&BackingData, - size_t BackingDataSize) + template <typename SymbolRange, typename RefRange, typename RelationRange, + typename Payload> + MemIndex(SymbolRange &&Symbols, RefRange &&Refs, RelationRange &&Relations, + Payload &&BackingData, size_t BackingDataSize) : MemIndex(std::forward<SymbolRange>(Symbols), - std::forward<RefRange>(Refs)) { + std::forward<RefRange>(Refs), + std::forward<RelationRange>(Relations)) { KeepAlive = std::shared_ptr<void>( std::make_shared<Payload>(std::move(BackingData)), nullptr); this->BackingDataSize = BackingDataSize; } /// Builds an index from slabs. The index takes ownership of the data. - static std::unique_ptr<SymbolIndex> build(SymbolSlab Symbols, RefSlab Refs); + static std::unique_ptr<SymbolIndex> build(SymbolSlab Symbols, RefSlab Refs, + RelationSlab Relations); bool fuzzyFind(const FuzzyFindRequest &Req, @@ -51,6 +56,10 @@ void refs(const RefsRequest &Req, llvm::function_ref<void(const Ref &)> Callback) const override; + void + relations(const SymbolID &ID, RelationKind Kind, + llvm::function_ref<void(const SymbolID &)> Callback) const override; + size_t estimateMemoryUsage() const override; private: @@ -58,6 +67,7 @@ llvm::DenseMap<SymbolID, const Symbol *> Index; // A map from symbol ID to symbol refs, support query by IDs. llvm::DenseMap<SymbolID, llvm::ArrayRef<Ref>> Refs; + llvm::DenseMap<RelationKey, llvm::ArrayRef<SymbolID>> Relations; std::shared_ptr<void> KeepAlive; // poor man's move-only std::any // Size of memory retained by KeepAlive. size_t BackingDataSize = 0; Index: clang-tools-extra/clangd/index/MemIndex.cpp =================================================================== --- clang-tools-extra/clangd/index/MemIndex.cpp +++ clang-tools-extra/clangd/index/MemIndex.cpp @@ -15,11 +15,14 @@ namespace clang { namespace clangd { -std::unique_ptr<SymbolIndex> MemIndex::build(SymbolSlab Slab, RefSlab Refs) { +std::unique_ptr<SymbolIndex> MemIndex::build(SymbolSlab Slab, RefSlab Refs, + RelationSlab Relations) { // Store Slab size before it is moved. const auto BackingDataSize = Slab.bytes() + Refs.bytes(); - auto Data = std::make_pair(std::move(Slab), std::move(Refs)); - return llvm::make_unique<MemIndex>(Data.first, Data.second, std::move(Data), + auto Data = + std::make_tuple(std::move(Slab), std::move(Refs), std::move(Relations)); + return llvm::make_unique<MemIndex>(std::get<0>(Data), std::get<1>(Data), + std::get<2>(Data), std::move(Data), BackingDataSize); } @@ -83,6 +86,17 @@ } } +void MemIndex::relations( + const SymbolID &ID, RelationKind Kind, + llvm::function_ref<void(const SymbolID &)> Callback) const { + auto Rels = Relations.find({ID, Kind}); + if (Rels == Relations.end()) + return; + for (const auto &S : Rels->second) { + Callback(S); + } +} + size_t MemIndex::estimateMemoryUsage() const { return Index.getMemorySize() + Refs.getMemorySize() + BackingDataSize; } Index: clang-tools-extra/clangd/index/Index.h =================================================================== --- clang-tools-extra/clangd/index/Index.h +++ clang-tools-extra/clangd/index/Index.h @@ -43,9 +43,7 @@ void setColumn(uint32_t Column); uint32_t column() const { return Column; } - bool hasOverflow() const { - return Line >= MaxLine || Column >= MaxColumn; - } + bool hasOverflow() const { return Line >= MaxLine || Column >= MaxColumn; } static constexpr uint32_t MaxLine = (1 << 20) - 1; static constexpr uint32_t MaxColumn = (1 << 12) - 1; @@ -247,11 +245,13 @@ SymbolFlag Flags = SymbolFlag::None; /// FIXME: also add deprecation message and fixit? }; -inline Symbol::SymbolFlag operator|(Symbol::SymbolFlag A, Symbol::SymbolFlag B) { +inline Symbol::SymbolFlag operator|(Symbol::SymbolFlag A, + Symbol::SymbolFlag B) { return static_cast<Symbol::SymbolFlag>(static_cast<uint8_t>(A) | static_cast<uint8_t>(B)); } -inline Symbol::SymbolFlag &operator|=(Symbol::SymbolFlag &A, Symbol::SymbolFlag B) { +inline Symbol::SymbolFlag &operator|=(Symbol::SymbolFlag &A, + Symbol::SymbolFlag B) { return A = A | B; } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S); @@ -432,6 +432,71 @@ size_t NumRefs = 0; }; +enum class RelationKind { Subtype }; + +struct RelationKey { + SymbolID Symbol; + RelationKind Kind; + + bool operator==(const RelationKey &Other) const { + return Symbol == Other.Symbol && Kind == Other.Kind; + } + +private: + friend llvm::hash_code hash_value(const RelationKey &Key) { + return llvm::hash_combine(Key.Symbol, static_cast<int>(Key.Kind)); + } +}; + +class RelationSlab { +public: + using value_type = std::pair<RelationKey, llvm::ArrayRef<SymbolID>>; + using const_iterator = std::vector<value_type>::const_iterator; + using iterator = const_iterator; + + RelationSlab() = default; + RelationSlab(RelationSlab &&Slab) = default; + RelationSlab &operator=(RelationSlab &&RHS) = default; + + const_iterator begin() const { return Relations.begin(); } + const_iterator end() const { return Relations.end(); } + size_t size() const { return Relations.size(); } + size_t numRelations() const { return NumRelations; } + bool empty() const { return Relations.empty(); } + + size_t bytes() const { + return sizeof(*this) + Arena.getTotalMemory() + + sizeof(value_type) * Relations.size(); + } + + // RelationSlab::Builder is a mutable container that can 'freeze' to + // RelationSlab. + class Builder { + public: + Builder() {} + // Adds a relation to the slab. Deep copy: Strings will be owned by the + // slab. + void insert(const RelationKey &Key, const SymbolID &S); + // Consumes the builder to finalize the slab. + RelationSlab build() &&; + + private: + llvm::BumpPtrAllocator Arena; + llvm::DenseMap<RelationKey, std::vector<SymbolID>> Relations; + }; + +private: + RelationSlab(std::vector<value_type> Relations, llvm::BumpPtrAllocator Arena, + size_t NumRelations) + : Arena(std::move(Arena)), Relations(std::move(Relations)), + NumRelations(NumRelations) {} + + llvm::BumpPtrAllocator Arena; + std::vector<value_type> Relations; + // Number of all relations. + size_t NumRelations = 0; +}; + struct FuzzyFindRequest { /// \brief A query string for the fuzzy find. This is matched against symbols' /// un-qualified identifiers and should not contain qualifiers like "::". @@ -512,6 +577,10 @@ virtual void refs(const RefsRequest &Req, llvm::function_ref<void(const Ref &)> Callback) const = 0; + virtual void + relations(const SymbolID &ID, RelationKind Kind, + llvm::function_ref<void(const SymbolID &)> Callback) const = 0; + /// Returns estimated size of index (in bytes). // FIXME(kbobyrev): Currently, this only returns the size of index itself // excluding the size of actual symbol slab index refers to. We should include @@ -535,6 +604,9 @@ llvm::function_ref<void(const Symbol &)>) const override; void refs(const RefsRequest &, llvm::function_ref<void(const Ref &)>) const override; + void relations(const SymbolID &, RelationKind, + llvm::function_ref<void(const SymbolID &)>) const override; + size_t estimateMemoryUsage() const override; private: @@ -546,4 +618,30 @@ } // namespace clangd } // namespace clang +namespace llvm { + +// Support RelationKeys as DenseMap keys. +template <> struct DenseMapInfo<clang::clangd::RelationKey> { + static inline clang::clangd::RelationKey getEmptyKey() { + return {DenseMapInfo<clang::clangd::SymbolID>::getEmptyKey(), + clang::clangd::RelationKind::Subtype}; + } + + static inline clang::clangd::RelationKey getTombstoneKey() { + return {DenseMapInfo<clang::clangd::SymbolID>::getTombstoneKey(), + clang::clangd::RelationKind::Subtype}; + } + + static unsigned getHashValue(const clang::clangd::RelationKey &Key) { + return hash_value(Key); + } + + static bool isEqual(const clang::clangd::RelationKey &LHS, + const clang::clangd::RelationKey &RHS) { + return LHS == RHS; + } +}; + +} // namespace llvm + #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_H Index: clang-tools-extra/clangd/index/Index.cpp =================================================================== --- clang-tools-extra/clangd/index/Index.cpp +++ clang-tools-extra/clangd/index/Index.cpp @@ -158,6 +158,28 @@ return RefSlab(std::move(Result), std::move(Arena), NumRefs); } +void RelationSlab::Builder::insert(const RelationKey &Key, const SymbolID &S) { + Relations[Key].push_back(S); +} + +RelationSlab RelationSlab::Builder::build() && { + // Reallocate relations on the arena to reduce waste and indirections when + // reading. + std::vector<std::pair<RelationKey, llvm::ArrayRef<SymbolID>>> Result; + Result.reserve(Relations.size()); + size_t NumRelations = 0; + for (auto &Entry : Relations) { + auto &Rels = Entry.second; + + NumRelations += Rels.size(); + auto *Array = Arena.Allocate<SymbolID>(Rels.size()); + std::uninitialized_copy(Rels.begin(), Rels.end(), Array); + Result.emplace_back(Entry.first, + llvm::ArrayRef<SymbolID>(Array, Rels.size())); + } + return RelationSlab(std::move(Result), std::move(Arena), NumRelations); +} + void SwapIndex::reset(std::unique_ptr<SymbolIndex> Index) { // Keep the old index alive, so we don't destroy it under lock (may be slow). std::shared_ptr<SymbolIndex> Pin; @@ -210,6 +232,11 @@ llvm::function_ref<void(const Ref &)> CB) const { return snapshot()->refs(R, CB); } +void SwapIndex::relations(const SymbolID &ID, RelationKind Kind, + llvm::function_ref<void(const SymbolID &)> CB) const { + return snapshot()->relations(ID, Kind, CB); +} + size_t SwapIndex::estimateMemoryUsage() const { return snapshot()->estimateMemoryUsage(); } Index: clang-tools-extra/clangd/index/FileIndex.h =================================================================== --- clang-tools-extra/clangd/index/FileIndex.h +++ clang-tools-extra/clangd/index/FileIndex.h @@ -60,7 +60,8 @@ /// Updates all symbols and refs in a file. /// If either is nullptr, corresponding data for \p Path will be removed. void update(PathRef Path, std::unique_ptr<SymbolSlab> Slab, - std::unique_ptr<RefSlab> Refs); + std::unique_ptr<RefSlab> Refs, + std::unique_ptr<RelationSlab> Relations); // The index keeps the symbols alive. std::unique_ptr<SymbolIndex> @@ -74,6 +75,8 @@ llvm::StringMap<std::shared_ptr<SymbolSlab>> FileToSymbols; /// Stores the latest ref snapshots for all active files. llvm::StringMap<std::shared_ptr<RefSlab>> FileToRefs; + /// Stores the latest relation snapshots for all active files. + llvm::StringMap<std::shared_ptr<RelationSlab>> FileToRelations; }; /// This manages symbols from files and an in-memory index on all symbols. @@ -119,10 +122,12 @@ SwapIndex MainFileIndex; }; +using SlabTuple = std::tuple<SymbolSlab, RefSlab, RelationSlab>; + /// Retrieves symbols and refs of local top level decls in \p AST (i.e. /// `AST.getLocalTopLevelDecls()`). /// Exposed to assist in unit tests. -std::pair<SymbolSlab, RefSlab> indexMainDecls(ParsedAST &AST); +SlabTuple indexMainDecls(ParsedAST &AST); /// Idex declarations from \p AST and macros from \p PP that are declared in /// included headers. Index: clang-tools-extra/clangd/index/FileIndex.cpp =================================================================== --- clang-tools-extra/clangd/index/FileIndex.cpp +++ clang-tools-extra/clangd/index/FileIndex.cpp @@ -27,10 +27,10 @@ namespace clang { namespace clangd { -static std::pair<SymbolSlab, RefSlab> -indexSymbols(ASTContext &AST, std::shared_ptr<Preprocessor> PP, - llvm::ArrayRef<Decl *> DeclsToIndex, - const CanonicalIncludes &Includes, bool IsIndexMainAST) { +static SlabTuple indexSymbols(ASTContext &AST, std::shared_ptr<Preprocessor> PP, + llvm::ArrayRef<Decl *> DeclsToIndex, + const CanonicalIncludes &Includes, + bool IsIndexMainAST) { SymbolCollector::Options CollectorOpts; CollectorOpts.CollectIncludePath = true; CollectorOpts.Includes = &Includes; @@ -64,15 +64,18 @@ auto Syms = Collector.takeSymbols(); auto Refs = Collector.takeRefs(); + auto Relations = Collector.takeRelations(); vlog("index AST for {0} (main={1}): \n" " symbol slab: {2} symbols, {3} bytes\n" - " ref slab: {4} symbols, {5} refs, {6} bytes", + " ref slab: {4} symbols, {5} refs, {6} bytes\n" + " relations slab: {7} symbols, {8} relations, {9} bytes", FileName, IsIndexMainAST, Syms.size(), Syms.bytes(), Refs.size(), - Refs.numRefs(), Refs.bytes()); - return {std::move(Syms), std::move(Refs)}; + Refs.numRefs(), Refs.bytes(), Relations.size(), Relations.numRelations(), + Relations.bytes()); + return {std::move(Syms), std::move(Refs), std::move(Relations)}; } -std::pair<SymbolSlab, RefSlab> indexMainDecls(ParsedAST &AST) { +SlabTuple indexMainDecls(ParsedAST &AST) { return indexSymbols(AST.getASTContext(), AST.getPreprocessorPtr(), AST.getLocalTopLevelDecls(), AST.getCanonicalIncludes(), /*IsIndexMainAST=*/true); @@ -83,13 +86,13 @@ std::vector<Decl *> DeclsToIndex( AST.getTranslationUnitDecl()->decls().begin(), AST.getTranslationUnitDecl()->decls().end()); - return indexSymbols(AST, std::move(PP), DeclsToIndex, Includes, - /*IsIndexMainAST=*/false) - .first; + return std::get<0>(indexSymbols(AST, std::move(PP), DeclsToIndex, Includes, + /*IsIndexMainAST=*/false)); } void FileSymbols::update(PathRef Path, std::unique_ptr<SymbolSlab> Symbols, - std::unique_ptr<RefSlab> Refs) { + std::unique_ptr<RefSlab> Refs, + std::unique_ptr<RelationSlab> Relations) { std::lock_guard<std::mutex> Lock(Mutex); if (!Symbols) FileToSymbols.erase(Path); @@ -99,18 +102,25 @@ FileToRefs.erase(Path); else FileToRefs[Path] = std::move(Refs); + if (!Relations) + FileToRelations.erase(Path); + else + FileToRelations[Path] = std::move(Relations); } std::unique_ptr<SymbolIndex> FileSymbols::buildIndex(IndexType Type, DuplicateHandling DuplicateHandle) { std::vector<std::shared_ptr<SymbolSlab>> SymbolSlabs; std::vector<std::shared_ptr<RefSlab>> RefSlabs; + std::vector<std::shared_ptr<RelationSlab>> RelationSlabs; { std::lock_guard<std::mutex> Lock(Mutex); for (const auto &FileAndSymbols : FileToSymbols) SymbolSlabs.push_back(FileAndSymbols.second); for (const auto &FileAndRefs : FileToRefs) RefSlabs.push_back(FileAndRefs.second); + for (const auto &FileAndRelations : FileToRelations) + RelationSlabs.push_back(FileAndRelations.second); } std::vector<const Symbol *> AllSymbols; std::vector<Symbol> SymsStorage; @@ -166,20 +176,51 @@ } } + std::vector<SymbolID> + RelationsStorage; // Continguous ranges for each RelationKey. + llvm::DenseMap<RelationKey, llvm::ArrayRef<SymbolID>> AllRelations; + { + llvm::DenseMap<RelationKey, llvm::SmallVector<SymbolID, 4>> MergedRelations; + size_t Count = 0; + for (const auto &RelationSlab : RelationSlabs) { + for (const auto &E : *RelationSlab) { + MergedRelations[E.first].append(E.second.begin(), E.second.end()); + Count += E.second.size(); + } + } + RelationsStorage.reserve(Count); + AllRelations.reserve(MergedRelations.size()); + for (auto &E : MergedRelations) { + auto &Relations = E.second; + // Sorting isn't required, but yields more stable results over rebuilds. + llvm::sort(Relations); + llvm::copy(Relations, back_inserter(RelationsStorage)); + AllRelations.try_emplace( + E.first, + llvm::ArrayRef<SymbolID>( + &RelationsStorage[RelationsStorage.size() - Relations.size()], + Relations.size())); + } + } + size_t StorageSize = RefsStorage.size() * sizeof(Ref) + SymsStorage.size() * sizeof(Symbol); for (const auto &Slab : SymbolSlabs) StorageSize += Slab->bytes(); for (const auto &RefSlab : RefSlabs) StorageSize += RefSlab->bytes(); + for (const auto &RelationSlab : RelationSlabs) + StorageSize += RelationSlab->bytes(); // Index must keep the slabs and contiguous ranges alive. switch (Type) { case IndexType::Light: return llvm::make_unique<MemIndex>( llvm::make_pointee_range(AllSymbols), std::move(AllRefs), + std::move(AllRelations), std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs), - std::move(RefsStorage), std::move(SymsStorage)), + std::move(RelationSlabs), std::move(RefsStorage), + std::move(SymsStorage), std::move(RelationsStorage)), StorageSize); case IndexType::Heavy: return llvm::make_unique<dex::Dex>( @@ -200,9 +241,9 @@ std::shared_ptr<Preprocessor> PP, const CanonicalIncludes &Includes) { auto Symbols = indexHeaderSymbols(AST, std::move(PP), Includes); - PreambleSymbols.update(Path, - llvm::make_unique<SymbolSlab>(std::move(Symbols)), - llvm::make_unique<RefSlab>()); + PreambleSymbols.update( + Path, llvm::make_unique<SymbolSlab>(std::move(Symbols)), + llvm::make_unique<RefSlab>(), llvm::make_unique<RelationSlab>()); PreambleIndex.reset( PreambleSymbols.buildIndex(UseDex ? IndexType::Heavy : IndexType::Light, DuplicateHandling::PickOne)); @@ -211,8 +252,9 @@ void FileIndex::updateMain(PathRef Path, ParsedAST &AST) { auto Contents = indexMainDecls(AST); MainFileSymbols.update( - Path, llvm::make_unique<SymbolSlab>(std::move(Contents.first)), - llvm::make_unique<RefSlab>(std::move(Contents.second))); + Path, llvm::make_unique<SymbolSlab>(std::move(std::get<0>(Contents))), + llvm::make_unique<RefSlab>(std::move(std::get<1>(Contents))), + llvm::make_unique<RelationSlab>(std::move(std::get<2>(Contents)))); MainFileIndex.reset( MainFileSymbols.buildIndex(IndexType::Light, DuplicateHandling::PickOne)); } Index: clang-tools-extra/clangd/index/Background.cpp =================================================================== --- clang-tools-extra/clangd/index/Background.cpp +++ clang-tools-extra/clangd/index/Background.cpp @@ -316,12 +316,14 @@ llvm::StringRef Path = FileIt.getKey(); SymbolSlab::Builder Syms; RefSlab::Builder Refs; + RelationSlab::Builder Relations; // TODO: Populate. for (const auto *S : FileIt.second.Symbols) Syms.insert(*S); for (const auto *R : FileIt.second.Refs) Refs.insert(RefToIDs[R], *R); auto SS = llvm::make_unique<SymbolSlab>(std::move(Syms).build()); auto RS = llvm::make_unique<RefSlab>(std::move(Refs).build()); + auto RelS = llvm::make_unique<RelationSlab>(std::move(Relations).build()); auto IG = llvm::make_unique<IncludeGraph>( getSubGraph(URI::create(Path), Index.Sources.getValue())); // We need to store shards before updating the index, since the latter @@ -330,6 +332,7 @@ IndexFileOut Shard; Shard.Symbols = SS.get(); Shard.Refs = RS.get(); + Shard.Relations = RelS.get(); Shard.Sources = IG.get(); if (auto Error = IndexStorage->storeShard(Path, Shard)) @@ -347,7 +350,8 @@ // This can override a newer version that is added in another thread, if // this thread sees the older version but finishes later. This should be // rare in practice. - IndexedSymbols.update(Path, std::move(SS), std::move(RS)); + IndexedSymbols.update(Path, std::move(SS), std::move(RS), + std::move(RelS)); } } } @@ -557,8 +561,13 @@ auto RS = SI.Shard->Refs ? llvm::make_unique<RefSlab>(std::move(*SI.Shard->Refs)) : nullptr; + auto RelS = + SI.Shard->Relations + ? llvm::make_unique<RelationSlab>(std::move(*SI.Shard->Relations)) + : nullptr; IndexedFileDigests[SI.AbsolutePath] = SI.Digest; - IndexedSymbols.update(SI.AbsolutePath, std::move(SS), std::move(RS)); + IndexedSymbols.update(SI.AbsolutePath, std::move(SS), std::move(RS), + std::move(RelS)); } } Index: clang-tools-extra/clangd/XRefs.h =================================================================== --- clang-tools-extra/clangd/XRefs.h +++ clang-tools-extra/clangd/XRefs.h @@ -67,7 +67,8 @@ /// Get type hierarchy information at \p Pos. llvm::Optional<TypeHierarchyItem> getTypeHierarchy(ParsedAST &AST, Position Pos, int Resolve, - TypeHierarchyDirection Direction); + TypeHierarchyDirection Direction, + const SymbolIndex *Index = nullptr); } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/XRefs.cpp =================================================================== --- clang-tools-extra/clangd/XRefs.cpp +++ clang-tools-extra/clangd/XRefs.cpp @@ -871,6 +871,65 @@ return THI; } +static Optional<TypeHierarchyItem> +symbolIDToTypeHierarchyItem(const SymbolID &ID, const SymbolIndex *Index) { + LookupRequest Request; + Request.IDs.insert(ID); + Optional<TypeHierarchyItem> Result; + Index->lookup(Request, [&Result](const Symbol &S) { + TypeHierarchyItem THI; + THI.name = S.Name; + THI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind); + THI.deprecated = (S.Flags & Symbol::Deprecated); + Position Start, End; + auto &CD = S.Definition ? S.Definition : S.CanonicalDeclaration; + Start.line = CD.Start.line(); + Start.character = CD.Start.column(); + End.line = CD.End.line(); + End.character = CD.End.column(); + // TODO: How to get entire range like in declToTypeHierarchyItem()? + THI.range = {Start, End}; + THI.selectionRange = {Start, End}; + // TODO: Reuse code between here and getWorkspaceSymbols(). + auto Uri = URI::parse(CD.FileURI); + if (!Uri) { + log("Type hierarchy: Could not parse URI '{0}' for symbol '{1}'.", + CD.FileURI, S.Name); + return; + } + // TODO: Pass in ClangdServer::WorkspaceRoot as a HintPath. + StringRef HintPath; + auto Path = URI::resolve(*Uri, HintPath); + if (!Path) { + log("Type hierarchy: Could not resolve path for URI '{0}' for symbol " + "'{1}'.", + Uri->toString(), S.Name); + return; + } + THI.uri = URIForFile::canonicalize(*Path, HintPath); + + Result = std::move(THI); + }); + return Result; +} + +static void fillSubTypes(const SymbolID &ID, + std::vector<TypeHierarchyItem> &SubTypes, + const SymbolIndex *Index, int Levels) { + Index->relations(ID, RelationKind::Subtype, + [Index, Levels, &SubTypes](const SymbolID &Sub) { + if (Optional<TypeHierarchyItem> ChildSym = + symbolIDToTypeHierarchyItem(Sub, Index)) { + if (Levels > 1) { + ChildSym->children.emplace(); + fillSubTypes(Sub, *ChildSym->children, Index, + Levels - 1); + } + SubTypes.emplace_back(std::move(*ChildSym)); + } + }); +} + static Optional<TypeHierarchyItem> getTypeAncestors(const CXXRecordDecl &CXXRD, ASTContext &ASTCtx) { Optional<TypeHierarchyItem> Result = declToTypeHierarchyItem(ASTCtx, CXXRD); @@ -885,7 +944,6 @@ Result->parents->emplace_back(std::move(*ParentSym)); } } - return Result; } @@ -950,15 +1008,26 @@ llvm::Optional<TypeHierarchyItem> getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels, - TypeHierarchyDirection Direction) { + TypeHierarchyDirection Direction, const SymbolIndex *Index) { const CXXRecordDecl *CXXRD = findRecordTypeAt(AST, Pos); if (!CXXRD) return llvm::None; Optional<TypeHierarchyItem> Result = getTypeAncestors(*CXXRD, AST.getASTContext()); - // TODO: Resolve type descendants if direction is Children or Both, - // and ResolveLevels > 0. + + if ((Direction == TypeHierarchyDirection::Children || + Direction == TypeHierarchyDirection::Both) && + ResolveLevels > 0) { + Result->children.emplace(); + + if (Index) { + if (Optional<SymbolID> ID = getSymbolID(CXXRD)) { + fillSubTypes(*ID, *Result->children, Index, ResolveLevels); + } + } + } + return Result; } Index: clang-tools-extra/clangd/ClangdServer.cpp =================================================================== --- clang-tools-extra/clangd/ClangdServer.cpp +++ clang-tools-extra/clangd/ClangdServer.cpp @@ -525,11 +525,11 @@ void ClangdServer::findTypeHierarchy(PathRef File, Position Pos, int Resolve, TypeHierarchyDirection Direction, Callback<Optional<TypeHierarchyItem>> CB) { - auto Action = [Pos, Resolve, Direction](decltype(CB) CB, - Expected<InputsAndAST> InpAST) { + auto Action = [Pos, Resolve, Direction, this](decltype(CB) CB, + Expected<InputsAndAST> InpAST) { if (!InpAST) return CB(InpAST.takeError()); - CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction)); + CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction, Index)); }; WorkScheduler.runWithAST("Type Hierarchy", File, Bind(Action, std::move(CB)));
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits