From 8f5fae99ff2aae9dafd377f1aff302e398077907 Mon Sep 17 00:00:00 2001 From: Nathan Ridge Date: Tue, 12 Aug 2025 22:54:32 -0400 Subject: [PATCH] [clangd] Retrieve documentation for member function instance from index Fixes https://github.com/clangd/clangd/issues/2290 --- clang-tools-extra/clangd/CodeComplete.cpp | 14 +++++++++- .../clangd/unittests/CodeCompleteTests.cpp | 26 +++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index 9c17b4ca9b706..da51998b9eec0 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1887,7 +1887,19 @@ class CodeCompleteFlow { for (auto &Cand : C.first) { if (Cand.SemaResult && Cand.SemaResult->Kind == CodeCompletionResult::RK_Declaration) { - auto ID = clangd::getSymbolID(Cand.SemaResult->getDeclaration()); + const NamedDecl *DeclToLookup = Cand.SemaResult->getDeclaration(); + // For instantiations of members of class templates, the + // documentation will be stored at the member's original + // declaration. + // FIXME: We'd like to handle fields too but FieldDecl is missing a + // method equivalent to getInstantiatedFromMemberFunction(). + if (const auto *FD = dyn_cast(DeclToLookup)) { + if (const auto *InstantiatedFrom = + FD->getInstantiatedFromMemberFunction()) { + DeclToLookup = InstantiatedFrom; + } + } + auto ID = clangd::getSymbolID(DeclToLookup); if (!ID) continue; Req.IDs.insert(ID); diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index 5a5d815076e2a..57eb9fcdecb21 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -1154,23 +1154,45 @@ TEST(CompletionTest, CommentsOnMembersFromHeader) { /// This is a member function. int delta(); }; + + template + struct beta { + /// This is a member field inside a template. + int omega; + + /// This is a member function inside a template. + int epsilon(); + }; )cpp"; auto File = testPath("foo.cpp"); Annotations Test(R"cpp( #include "foo.h" alpha a; -int x = a.^ +beta b; +int x = a.$p1^; +int y = b.$p2^; )cpp"); runAddDocument(Server, File, Test.code()); auto CompletionList = - llvm::cantFail(runCodeComplete(Server, File, Test.point(), {})); + llvm::cantFail(runCodeComplete(Server, File, Test.point("p1"), {})); EXPECT_THAT(CompletionList.Completions, Contains(AllOf(named("gamma"), doc("This is a member field.")))); EXPECT_THAT( CompletionList.Completions, Contains(AllOf(named("delta"), doc("This is a member function.")))); + + CompletionList = + llvm::cantFail(runCodeComplete(Server, File, Test.point("p2"), {})); + + EXPECT_THAT(CompletionList.Completions, + Contains(AllOf(named("omega") + /* FIXME: Doc retrieval does not work yet*/))); + EXPECT_THAT( + CompletionList.Completions, + Contains(AllOf(named("epsilon"), + doc("This is a member function inside a template.")))); } TEST(CompletionTest, CommentsOnMembersFromHeaderOverloadBundling) {