Skip to content

Commit 16f47cf

Browse files
[clangd] Heuristically resolve dependent call through smart pointer type
Summary: Fixes clangd/clangd#227 Reviewers: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D71644
1 parent 751d4da commit 16f47cf

File tree

2 files changed

+59
-17
lines changed

2 files changed

+59
-17
lines changed

clang-tools-extra/clangd/FindTarget.cpp

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "clang/AST/TypeLoc.h"
2828
#include "clang/AST/TypeLocVisitor.h"
2929
#include "clang/Basic/LangOptions.h"
30+
#include "clang/Basic/OperatorKinds.h"
3031
#include "clang/Basic/SourceLocation.h"
3132
#include "llvm/ADT/STLExtras.h"
3233
#include "llvm/ADT/SmallVector.h"
@@ -61,9 +62,14 @@ nodeToString(const ast_type_traits::DynTypedNode &N) {
6162
// (e.g. an overloaded method in the primary template).
6263
// This heuristic will give the desired answer in many cases, e.g.
6364
// for a call to vector<T>::size().
64-
std::vector<const NamedDecl *>
65-
getMembersReferencedViaDependentName(const Type *T, const DeclarationName &Name,
66-
bool IsNonstaticMember) {
65+
// The name to look up is provided in the form of a factory that takes
66+
// an ASTContext, because an ASTContext may be needed to obtain the
67+
// name (e.g. if it's an operator name), but the caller may not have
68+
// access to an ASTContext.
69+
std::vector<const NamedDecl *> getMembersReferencedViaDependentName(
70+
const Type *T,
71+
llvm::function_ref<DeclarationName(ASTContext &)> NameFactory,
72+
bool IsNonstaticMember) {
6773
if (!T)
6874
return {};
6975
if (auto *ICNT = T->getAs<InjectedClassNameType>()) {
@@ -80,12 +86,54 @@ getMembersReferencedViaDependentName(const Type *T, const DeclarationName &Name,
8086
if (!RD->hasDefinition())
8187
return {};
8288
RD = RD->getDefinition();
89+
DeclarationName Name = NameFactory(RD->getASTContext());
8390
return RD->lookupDependentName(Name, [=](const NamedDecl *D) {
8491
return IsNonstaticMember ? D->isCXXInstanceMember()
8592
: !D->isCXXInstanceMember();
8693
});
8794
}
8895

96+
// Given the type T of a dependent expression that appears of the LHS of a "->",
97+
// heuristically find a corresponding pointee type in whose scope we could look
98+
// up the name appearing on the RHS.
99+
const Type *getPointeeType(const Type *T) {
100+
if (!T)
101+
return nullptr;
102+
103+
if (T->isPointerType()) {
104+
return T->getAs<PointerType>()->getPointeeType().getTypePtrOrNull();
105+
}
106+
107+
// Try to handle smart pointer types.
108+
109+
// Look up operator-> in the primary template. If we find one, it's probably a
110+
// smart pointer type.
111+
auto ArrowOps = getMembersReferencedViaDependentName(
112+
T,
113+
[](ASTContext &Ctx) {
114+
return Ctx.DeclarationNames.getCXXOperatorName(OO_Arrow);
115+
},
116+
/*IsNonStaticMember=*/true);
117+
if (ArrowOps.empty())
118+
return nullptr;
119+
120+
// Getting the return type of the found operator-> method decl isn't useful,
121+
// because we discarded template arguments to perform lookup in the primary
122+
// template scope, so the return type would just have the form U* where U is a
123+
// template parameter type.
124+
// Instead, just handle the common case where the smart pointer type has the
125+
// form of SmartPtr<X, ...>, and assume X is the pointee type.
126+
auto *TST = T->getAs<TemplateSpecializationType>();
127+
if (!TST)
128+
return nullptr;
129+
if (TST->getNumArgs() == 0)
130+
return nullptr;
131+
const TemplateArgument &FirstArg = TST->getArg(0);
132+
if (FirstArg.getKind() != TemplateArgument::Type)
133+
return nullptr;
134+
return FirstArg.getAsType().getTypePtrOrNull();
135+
}
136+
89137
// TargetFinder locates the entities that an AST node refers to.
90138
//
91139
// Typically this is (possibly) one declaration and (possibly) one type, but
@@ -251,24 +299,18 @@ struct TargetFinder {
251299
VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E) {
252300
const Type *BaseType = E->getBaseType().getTypePtrOrNull();
253301
if (E->isArrow()) {
254-
// FIXME: Handle smart pointer types by looking up operator->
255-
// in the primary template.
256-
if (!BaseType || !BaseType->isPointerType()) {
257-
return;
258-
}
259-
BaseType = BaseType->getAs<PointerType>()
260-
->getPointeeType()
261-
.getTypePtrOrNull();
302+
BaseType = getPointeeType(BaseType);
262303
}
263-
for (const NamedDecl *D :
264-
getMembersReferencedViaDependentName(BaseType, E->getMember(),
265-
/*IsNonstaticMember=*/true)) {
304+
for (const NamedDecl *D : getMembersReferencedViaDependentName(
305+
BaseType, [E](ASTContext &) { return E->getMember(); },
306+
/*IsNonstaticMember=*/true)) {
266307
Outer.add(D, Flags);
267308
}
268309
}
269310
void VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *E) {
270311
for (const NamedDecl *D : getMembersReferencedViaDependentName(
271-
E->getQualifier()->getAsType(), E->getDeclName(),
312+
E->getQualifier()->getAsType(),
313+
[E](ASTContext &) { return E->getDeclName(); },
272314
/*IsNonstaticMember=*/false)) {
273315
Outer.add(D, Flags);
274316
}

clang-tools-extra/clangd/unittests/XRefsTests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,9 +485,9 @@ TEST(LocateSymbol, All) {
485485
}
486486
)cpp",
487487

488-
R"cpp(// FIXME: Heuristic resolution of dependent method
488+
R"cpp(// Heuristic resolution of dependent method
489489
// invoked via smart pointer
490-
template <typename> struct S { void foo(); };
490+
template <typename> struct S { void [[foo]]() {} };
491491
template <typename T> struct unique_ptr {
492492
T* operator->();
493493
};

0 commit comments

Comments
 (0)