Author: Nathan Ridge Date: 2025-10-06T13:30:02-04:00 New Revision: 255db37e778e0cb61417765f12a280605efc11b8
URL: https://github.com/llvm/llvm-project/commit/255db37e778e0cb61417765f12a280605efc11b8 DIFF: https://github.com/llvm/llvm-project/commit/255db37e778e0cb61417765f12a280605efc11b8.diff LOG: [clangd] Retrieve documentation for class member instance from index (#153337) Fixes https://github.com/clangd/clangd/issues/2290 Added: Modified: clang-tools-extra/clangd/CodeComplete.cpp clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp clang/include/clang/AST/DeclTemplate.h clang/lib/AST/ASTContext.cpp clang/lib/AST/DeclTemplate.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index c6deed3ab1e7b..e4df7581f1315 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -43,6 +43,7 @@ #include "support/Trace.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" +#include "clang/AST/DeclTemplate.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" @@ -1886,7 +1887,15 @@ 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. + if (const NamedDecl *Adjusted = + dyn_cast<NamedDecl>(&adjustDeclToTemplate(*DeclToLookup))) { + DeclToLookup = Adjusted; + } + 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 7640569128172..768f88f177e56 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 <typename T> + 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<int> 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) { diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index bba72365089f9..a3c67a60d0329 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3399,6 +3399,11 @@ inline UnsignedOrNone getExpandedPackSize(const NamedDecl *Param) { /// for their AssociatedDecl. TemplateParameterList *getReplacedTemplateParameterList(const Decl *D); +/// If we have a 'templated' declaration for a template, adjust 'D' to +/// refer to the actual template. +/// If we have an implicit instantiation, adjust 'D' to refer to template. +const Decl &adjustDeclToTemplate(const Decl &D); + } // namespace clang #endif // LLVM_CLANG_AST_DECLTEMPLATE_H diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 056bfe36b2a0a..a074990cce7fe 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -330,76 +330,6 @@ void ASTContext::addComment(const RawComment &RC) { Comments.addComment(RC, LangOpts.CommentOpts, BumpAlloc); } -/// If we have a 'templated' declaration for a template, adjust 'D' to -/// refer to the actual template. -/// If we have an implicit instantiation, adjust 'D' to refer to template. -static const Decl &adjustDeclToTemplate(const Decl &D) { - if (const auto *FD = dyn_cast<FunctionDecl>(&D)) { - // Is this function declaration part of a function template? - if (const FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate()) - return *FTD; - - // Nothing to do if function is not an implicit instantiation. - if (FD->getTemplateSpecializationKind() != TSK_ImplicitInstantiation) - return D; - - // Function is an implicit instantiation of a function template? - if (const FunctionTemplateDecl *FTD = FD->getPrimaryTemplate()) - return *FTD; - - // Function is instantiated from a member definition of a class template? - if (const FunctionDecl *MemberDecl = - FD->getInstantiatedFromMemberFunction()) - return *MemberDecl; - - return D; - } - if (const auto *VD = dyn_cast<VarDecl>(&D)) { - // Static data member is instantiated from a member definition of a class - // template? - if (VD->isStaticDataMember()) - if (const VarDecl *MemberDecl = VD->getInstantiatedFromStaticDataMember()) - return *MemberDecl; - - return D; - } - if (const auto *CRD = dyn_cast<CXXRecordDecl>(&D)) { - // Is this class declaration part of a class template? - if (const ClassTemplateDecl *CTD = CRD->getDescribedClassTemplate()) - return *CTD; - - // Class is an implicit instantiation of a class template or partial - // specialization? - if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CRD)) { - if (CTSD->getSpecializationKind() != TSK_ImplicitInstantiation) - return D; - llvm::PointerUnion<ClassTemplateDecl *, - ClassTemplatePartialSpecializationDecl *> - PU = CTSD->getSpecializedTemplateOrPartial(); - return isa<ClassTemplateDecl *>(PU) - ? *static_cast<const Decl *>(cast<ClassTemplateDecl *>(PU)) - : *static_cast<const Decl *>( - cast<ClassTemplatePartialSpecializationDecl *>(PU)); - } - - // Class is instantiated from a member definition of a class template? - if (const MemberSpecializationInfo *Info = - CRD->getMemberSpecializationInfo()) - return *Info->getInstantiatedFrom(); - - return D; - } - if (const auto *ED = dyn_cast<EnumDecl>(&D)) { - // Enum is instantiated from a member definition of a class template? - if (const EnumDecl *MemberDecl = ED->getInstantiatedFromMemberEnum()) - return *MemberDecl; - - return D; - } - // FIXME: Adjust alias templates? - return D; -} - const RawComment *ASTContext::getRawCommentForAnyRedecl( const Decl *D, const Decl **OriginalDecl) const { diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index b6bb6117d42af..e5fba1bf9d6bc 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1708,3 +1708,70 @@ TemplateParameterList *clang::getReplacedTemplateParameterList(const Decl *D) { llvm_unreachable("Unhandled templated declaration kind"); } } + +const Decl &clang::adjustDeclToTemplate(const Decl &D) { + if (const auto *FD = dyn_cast<FunctionDecl>(&D)) { + // Is this function declaration part of a function template? + if (const FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate()) + return *FTD; + + // Nothing to do if function is not an implicit instantiation. + if (FD->getTemplateSpecializationKind() != TSK_ImplicitInstantiation) + return D; + + // Function is an implicit instantiation of a function template? + if (const FunctionTemplateDecl *FTD = FD->getPrimaryTemplate()) + return *FTD; + + // Function is instantiated from a member definition of a class template? + if (const FunctionDecl *MemberDecl = + FD->getInstantiatedFromMemberFunction()) + return *MemberDecl; + + return D; + } + if (const auto *VD = dyn_cast<VarDecl>(&D)) { + // Static data member is instantiated from a member definition of a class + // template? + if (VD->isStaticDataMember()) + if (const VarDecl *MemberDecl = VD->getInstantiatedFromStaticDataMember()) + return *MemberDecl; + + return D; + } + if (const auto *CRD = dyn_cast<CXXRecordDecl>(&D)) { + // Is this class declaration part of a class template? + if (const ClassTemplateDecl *CTD = CRD->getDescribedClassTemplate()) + return *CTD; + + // Class is an implicit instantiation of a class template or partial + // specialization? + if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CRD)) { + if (CTSD->getSpecializationKind() != TSK_ImplicitInstantiation) + return D; + llvm::PointerUnion<ClassTemplateDecl *, + ClassTemplatePartialSpecializationDecl *> + PU = CTSD->getSpecializedTemplateOrPartial(); + return isa<ClassTemplateDecl *>(PU) + ? *static_cast<const Decl *>(cast<ClassTemplateDecl *>(PU)) + : *static_cast<const Decl *>( + cast<ClassTemplatePartialSpecializationDecl *>(PU)); + } + + // Class is instantiated from a member definition of a class template? + if (const MemberSpecializationInfo *Info = + CRD->getMemberSpecializationInfo()) + return *Info->getInstantiatedFrom(); + + return D; + } + if (const auto *ED = dyn_cast<EnumDecl>(&D)) { + // Enum is instantiated from a member definition of a class template? + if (const EnumDecl *MemberDecl = ED->getInstantiatedFromMemberEnum()) + return *MemberDecl; + + return D; + } + // FIXME: Adjust alias templates? + return D; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
