diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 86528d806eab3..ca20eacd5eb82 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -2335,23 +2335,41 @@ getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels, return Results; } -std::optional> -superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index) { - if (!Index || !Item.data.parents) - return std::nullopt; +// Resolves Parents against the index and appends the results to Results. +// Parents that cannot be found in the index (e.g. because they are implicit +// template instantiations, which clangd never indexes) are skipped over: +// their own already-known parents are resolved instead, so a single +// unindexed link in the chain doesn't hide everything above it. +static void +resolveParents(llvm::ArrayRef Parents, + llvm::StringRef TUPath, const SymbolIndex &Index, + std::vector &Results) { LookupRequest Req; llvm::DenseMap IDToData; - for (const auto &Parent : *Item.data.parents) { + for (const auto &Parent : Parents) { Req.IDs.insert(Parent.symbolID); IDToData[Parent.symbolID] = &Parent; } - std::vector Results; - Index->lookup(Req, [&Item, &Results, &IDToData](const Symbol &S) { - if (auto THI = symbolToTypeHierarchyItem(S, Item.uri.file())) { + llvm::DenseSet Found; + Index.lookup(Req, [&](const Symbol &S) { + if (auto THI = symbolToTypeHierarchyItem(S, TUPath)) { THI->data = *IDToData.lookup(S.ID); Results.emplace_back(std::move(*THI)); + Found.insert(S.ID); } }); + for (const auto &Parent : Parents) { + if (!Found.contains(Parent.symbolID) && Parent.parents) + resolveParents(*Parent.parents, TUPath, Index, Results); + } +} + +std::optional> +superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index) { + if (!Index || !Item.data.parents) + return std::nullopt; + std::vector Results; + resolveParents(*Item.data.parents, Item.uri.file(), *Index, Results); return Results.empty() ? std::nullopt : std::make_optional(std::move(Results)); } diff --git a/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp index 754063ede2724..e40e0ee11a8ae 100644 --- a/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp +++ b/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp @@ -825,6 +825,34 @@ struct Chil^d : Parent {}; withSymbolTags(SymbolTag::Declaration, SymbolTag::Definition), withResolveParents(Optional(IsEmpty())))))); } + +// A base class that is an implicit template instantiation +// (like Mixin below) is never indexed, so +// looking it up while resolving supertypes fails. superTypes() should not +// let that hide everything above it; it should skip over the unresolvable +// link and surface its own already-known parents instead. +TEST(Standard, SuperTypesSkipsUnindexedImplicitInstantiation) { + Annotations Source(R"cpp( +struct RootBase {}; +struct OtherBase {}; +template +struct Mixin : T, OtherBase {}; +struct Deri^ved : Mixin {}; +)cpp"); + + TestTU TU = TestTU::withCode(Source.code()); + auto AST = TU.build(); + auto Index = TU.index(); + + auto Result = getTypeHierarchy(AST, Source.point(), /*ResolveLevels=*/1, + TypeHierarchyDirection::Children, Index.get(), + testPath(TU.Filename)); + ASSERT_THAT(Result, SizeIs(1)); + auto Parents = superTypes(Result.front(), Index.get()); + + EXPECT_THAT(Parents, Optional(UnorderedElementsAre(withName("RootBase"), + withName("OtherBase")))); +} } // namespace } // namespace clangd } // namespace clang