diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -79,8 +79,6 @@ // formally size() is unresolved, but the primary template is a good guess. // This affects: // - DependentTemplateSpecializationType, -// - DependentScopeMemberExpr -// - DependentScopeDeclRefExpr // - DependentNameType struct TargetFinder { using RelSet = DeclRelationSet; @@ -212,6 +210,25 @@ break; } } + void + VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E) { + const Type *BaseType = E->getBaseType().getTypePtrOrNull(); + if (E->isArrow()) { + if (!BaseType || !BaseType->isPointerType()) { + return; + } + BaseType = BaseType->getAs() + ->getPointeeType() + .getTypePtrOrNull(); + } + addMembersReferencedViaDependentName(BaseType, E->getMember(), + /*IsNonstaticMember=*/true); + } + void VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *E) { + addMembersReferencedViaDependentName(E->getQualifier()->getAsType(), + E->getDeclName(), + /*IsNonstaticMember=*/false); + } void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *OIRE) { Outer.add(OIRE->getDecl(), Flags); } @@ -231,6 +248,37 @@ void VisitObjCProtocolExpr(const ObjCProtocolExpr *OPE) { Outer.add(OPE->getProtocol(), Flags); } + + void addMembersReferencedViaDependentName(const Type *T, + const DeclarationName &Name, + bool IsNonstaticMember) { + // This code was adapted in part from indexDependentReference() in + // IndexBody.cpp. + if (!T) + return; + if (auto *ICNT = T->getAs()) { + T = ICNT->getInjectedSpecializationType().getTypePtrOrNull(); + } + auto *TST = T->getAs(); + if (!TST) + return; + const ClassTemplateDecl *TD = dyn_cast_or_null( + TST->getTemplateName().getAsTemplateDecl()); + if (!TD) + return; + CXXRecordDecl *RD = TD->getTemplatedDecl(); + if (!RD->hasDefinition()) + return; + RD = RD->getDefinition(); + std::vector Decls = + RD->lookupDependentName(Name, [=](const NamedDecl *D) { + return IsNonstaticMember ? D->isCXXInstanceMember() + : !D->isCXXInstanceMember(); + }); + for (const NamedDecl *D : Decls) { + Outer.add(D, Flags); + } + } }; Visitor(*this, Flags).Visit(S); } diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -384,8 +384,8 @@ // different kinds, deduplicate them. std::vector Result; for (const auto &Ref : References) { - if (auto Range = getTokenRange(AST.getSourceManager(), - AST.getLangOpts(), Ref.Loc)) { + if (auto Range = + getTokenRange(AST.getSourceManager(), AST.getLangOpts(), Ref.Loc)) { DocumentHighlight DH; DH.range = *Range; if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write)) diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -465,6 +465,39 @@ template struct Fo^o {}; + )cpp", + + R"cpp(// Heuristic resolution of method + template + struct S { + void [[bar]]() {} + }; + + template + void foo(S arg) { + arg.ba^r(); + } + )cpp", + + R"cpp(// Heuristic resolution of method via this-> + template + struct S { + void [[foo]]() { + this->fo^o(); + } + }; + )cpp", + + R"cpp(// Heuristic resolution of static method + template + struct S { + static void [[bar]]() {} + }; + + template + void foo() { + S::ba^r(); + } )cpp"}; for (const char *Test : Tests) { Annotations T(Test); @@ -525,6 +558,21 @@ Foo abcde$10^("asdf"); Foo foox2 = Foo$11^("asdf"); } + + template + struct S { + void $NonstaticOverload1[[bar]](int); + void $NonstaticOverload2[[bar]](float); + + static void $StaticOverload1[[baz]](int); + static void $StaticOverload2[[baz]](int); + }; + + template + void dependent_call(S s, U u) { + s.ba$12^r(u); + S::ba$13^z(u); + } )cpp"); auto AST = TestTU::withCode(T.code()).build(); // Ordered assertions are deliberate: we expect a predictable order. @@ -544,6 +592,15 @@ ElementsAre(Sym("Foo", T.range("ConstructorLoc")))); EXPECT_THAT(locateSymbolAt(AST, T.point("11")), ElementsAre(Sym("Foo", T.range("ConstructorLoc")))); + // These assertions are unordered because the order comes from + // CXXRecordDecl::lookupDependentName() which doesn't appear to provide + // an order guarantee. + EXPECT_THAT(locateSymbolAt(AST, T.point("12")), + UnorderedElementsAre(Sym("bar", T.range("NonstaticOverload1")), + Sym("bar", T.range("NonstaticOverload2")))); + EXPECT_THAT(locateSymbolAt(AST, T.point("13")), + UnorderedElementsAre(Sym("baz", T.range("StaticOverload1")), + Sym("baz", T.range("StaticOverload2")))); } TEST(LocateSymbol, TemplateTypedefs) {