diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -82,6 +82,11 @@ std::vector References; bool HasMore = false; }; + +/// Returns implementations of the virtual function at a specified \p Pos. +std::vector findImplementations(ParsedAST &AST, Position Pos, + const SymbolIndex *Index); + /// Returns references of the symbol at a specified \p Pos. /// \p Limit limits the number of results returned (0 means no limit). ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit, diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1124,6 +1124,52 @@ return Result; } +std::vector findImplementations(ParsedAST &AST, Position Pos, + const SymbolIndex *Index) { + // We rely on index to find the implementations in subclasses. + // FIXME: Index can be stale, so we may loose some latest results from the + // main file. + if (!Index) + return {}; + const SourceManager &SM = AST.getSourceManager(); + auto MainFilePath = + getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM); + if (!MainFilePath) { + elog("Failed to get a path for the main file, so no implementations."); + return {}; + } + auto CurLoc = sourceLocationInMainFile(SM, Pos); + if (!CurLoc) { + elog("Failed to convert position to source location: {0}", + CurLoc.takeError()); + return {}; + } + std::vector Results; + DeclRelationSet Relations = + DeclRelation::TemplatePattern | DeclRelation::Alias; + RelationsRequest Req; + Req.Predicate = RelationKind::OverriddenBy; + for (const NamedDecl *ND : getDeclAtPosition(AST, *CurLoc, Relations)) + if (const CXXMethodDecl *CXXMD = llvm::dyn_cast(ND)) + if (CXXMD->isVirtual()) + Req.Subjects.insert(getSymbolID(ND)); + + if (Req.Subjects.empty()) + return Results; + Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) { + if (auto DeclLoc = + indexToLSPLocation(Object.CanonicalDeclaration, *MainFilePath)) { + LocatedSymbol Loc; + Loc.Name = Object.Name.str(); + Loc.PreferredDeclaration = *DeclLoc; + if (auto DefLoc = indexToLSPLocation(Object.Definition, *MainFilePath)) + Loc.Definition = *DefLoc; + Results.push_back(Loc); + } + }); + return Results; +} + ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit, const SymbolIndex *Index) { if (!Limit) diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -43,6 +43,7 @@ using ::testing::Matcher; using ::testing::UnorderedElementsAre; using ::testing::UnorderedElementsAreArray; +using ::testing::UnorderedPointwise; MATCHER_P2(FileRange, File, Range, "") { return Location{URIForFile::canonicalize(File, testRoot()), Range} == arg; @@ -1161,12 +1162,12 @@ )cpp", }; - for (const auto* Case : Tests) { + for (const auto *Case : Tests) { SCOPED_TRACE(Case); auto T = Annotations(Case); auto AST = TestTU::withCode(T.code()).build(); EXPECT_THAT(locateSymbolAt(AST, T.point()), - ::testing::UnorderedPointwise(DeclRange(), T.ranges())); + UnorderedPointwise(DeclRange(), T.ranges())); } } @@ -1464,6 +1465,67 @@ } } +TEST(FindImplementations, Inheritance) { + llvm::StringRef Test = R"cpp( + struct Base { + virtual void F$1^oo(); + void C$4^oncrete(); + }; + struct Child1 : Base { + void $1[[Fo$3^o]]() override; + virtual void B$2^ar(); + void Concrete(); // No implementations for concrete methods. + }; + struct Child2 : Child1 { + void $3[[Foo]]() override; + void $2[[Bar]]() override; + }; + void FromReference() { + Base* B; + B->Fo$1^o(); + B->C$4^oncrete(); + &Base::Fo$1^o; + Child1 * C1; + C1->B$2^ar(); + C1->Fo$3^o(); + } + )cpp"; + + Annotations Code(Test); + auto TU = TestTU::withCode(Code.code()); + auto AST = TU.build(); + for (const std::string &Label : {"1", "2", "3", "4"}) { + for (const auto &Point : Code.points(Label)) { + EXPECT_THAT(findImplementations(AST, Point, TU.index().get()), + UnorderedPointwise(DeclRange(), Code.ranges(Label))) + << Code.code() << " at " << Point << " for Label " << Label; + } + } +} + +TEST(FindImplementations, CaptureDefintion) { + llvm::StringRef Test = R"cpp( + struct Base { + virtual void F^oo(); + }; + struct Child1 : Base { + void $Decl[[Foo]]() override; + }; + struct Child2 : Base { + void $Child2[[Foo]]() override; + }; + void Child1::$Def[[Foo]]() { /* Definition */ } + )cpp"; + Annotations Code(Test); + auto TU = TestTU::withCode(Code.code()); + auto AST = TU.build(); + EXPECT_THAT( + findImplementations(AST, Code.point(), TU.index().get()), + UnorderedElementsAre(Sym("Foo", Code.range("Decl"), Code.range("Def")), + Sym("Foo", Code.range("Child2"), llvm::None))) + << Test; +} + TEST(FindReferences, WithinAST) { const char *Tests[] = { R"cpp(// Local variable