[clang] 255db37 - [clangd] Retrieve documentation for class member instance from index (#153337)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 6 10:30:06 PDT 2025
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;
+}
More information about the cfe-commits
mailing list