Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion clang-tools-extra/clangd/CodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -1883,7 +1884,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);
Expand Down
26 changes: 24 additions & 2 deletions clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
70 changes: 0 additions & 70 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,76 +351,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 {
Expand Down
67 changes: 67 additions & 0 deletions clang/lib/AST/DeclTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1706,3 +1706,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;
}