nridge created this revision.
Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, jkorous, 
ilya-biryukov.
Herald added a project: clang.

Fixes https://github.com/clangd/clangd/issues/441


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D82739

Files:
  clang-tools-extra/clangd/FindTarget.cpp
  clang-tools-extra/clangd/unittests/FindTargetTests.cpp

Index: clang-tools-extra/clangd/unittests/FindTargetTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -548,6 +548,23 @@
   EXPECT_DECLS("UnresolvedMemberExpr", "void func(int *)", "void func(char *)");
 }
 
+TEST_F(TargetDeclTest, DependentExprs) {
+  Flags = {"-fno-delayed-template-parsing"};
+
+  // Heuristic resolution of method of dependent field
+  Code = R"cpp(
+        struct A { void foo() {} };
+        template <typename T>
+        struct B {
+          A a;
+          void bar() {
+            this->a.[[foo]]();
+          }
+        };
+      )cpp";
+  EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()");
+}
+
 TEST_F(TargetDeclTest, ObjC) {
   Flags = {"-xobjective-c"};
   Code = R"cpp(
Index: clang-tools-extra/clangd/FindTarget.cpp
===================================================================
--- clang-tools-extra/clangd/FindTarget.cpp
+++ clang-tools-extra/clangd/FindTarget.cpp
@@ -58,50 +58,12 @@
   return S;
 }
 
-// Given a dependent type and a member name, heuristically resolve the
-// name to one or more declarations.
-// The current heuristic is simply to look up the name in the primary
-// template. This is a heuristic because the template could potentially
-// have specializations that declare different members.
-// Multiple declarations could be returned if the name is overloaded
-// (e.g. an overloaded method in the primary template).
-// This heuristic will give the desired answer in many cases, e.g.
-// for a call to vector<T>::size().
-// The name to look up is provided in the form of a factory that takes
-// an ASTContext, because an ASTContext may be needed to obtain the
-// name (e.g. if it's an operator name), but the caller may not have
-// access to an ASTContext.
+// Forward declaration, needed as this function is mutually recursive
+// with getPointeeType() and resolveDependentExprToDecls().
 std::vector<const NamedDecl *> getMembersReferencedViaDependentName(
-    const Type *T,
+    const Expr *E, const Type *T,
     llvm::function_ref<DeclarationName(ASTContext &)> NameFactory,
-    bool IsNonstaticMember) {
-  if (!T)
-    return {};
-  if (auto *ET = T->getAs<EnumType>()) {
-    auto Result =
-        ET->getDecl()->lookup(NameFactory(ET->getDecl()->getASTContext()));
-    return {Result.begin(), Result.end()};
-  }
-  if (auto *ICNT = T->getAs<InjectedClassNameType>()) {
-    T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
-  }
-  auto *TST = T->getAs<TemplateSpecializationType>();
-  if (!TST)
-    return {};
-  const ClassTemplateDecl *TD = dyn_cast_or_null<ClassTemplateDecl>(
-      TST->getTemplateName().getAsTemplateDecl());
-  if (!TD)
-    return {};
-  CXXRecordDecl *RD = TD->getTemplatedDecl();
-  if (!RD->hasDefinition())
-    return {};
-  RD = RD->getDefinition();
-  DeclarationName Name = NameFactory(RD->getASTContext());
-  return RD->lookupDependentName(Name, [=](const NamedDecl *D) {
-    return IsNonstaticMember ? D->isCXXInstanceMember()
-                             : !D->isCXXInstanceMember();
-  });
-}
+    bool IsNonstaticMember);
 
 // Given the type T of a dependent expression that appears of the LHS of a "->",
 // heuristically find a corresponding pointee type in whose scope we could look
@@ -119,7 +81,7 @@
   // Look up operator-> in the primary template. If we find one, it's probably a
   // smart pointer type.
   auto ArrowOps = getMembersReferencedViaDependentName(
-      T,
+      nullptr, T,
       [](ASTContext &Ctx) {
         return Ctx.DeclarationNames.getCXXOperatorName(OO_Arrow);
       },
@@ -144,6 +106,119 @@
   return FirstArg.getAsType().getTypePtrOrNull();
 }
 
+// Try to heuristically resolve a dependent expression `E` to one
+// or more declarations that it likely references.
+std::vector<const NamedDecl *> resolveDependentExprToDecls(const Expr *E) {
+  switch (E->getStmtClass()) {
+  case Stmt::CXXDependentScopeMemberExprClass: {
+    const auto *ME = llvm::cast<CXXDependentScopeMemberExpr>(E);
+    const Type *BaseType = ME->getBaseType().getTypePtrOrNull();
+    if (ME->isArrow()) {
+      BaseType = getPointeeType(BaseType);
+    }
+    Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
+    return getMembersReferencedViaDependentName(
+        Base, BaseType, [ME](ASTContext &) { return ME->getMember(); },
+        /*IsNonstaticMember=*/true);
+  }
+  case Stmt::DependentScopeDeclRefExprClass: {
+    const auto *RE = llvm::cast<DependentScopeDeclRefExpr>(E);
+    return getMembersReferencedViaDependentName(
+        nullptr, RE->getQualifier()->getAsType(),
+        [RE](ASTContext &) { return RE->getDeclName(); },
+        /*IsNonstaticMember=*/false);
+  }
+  default:
+    return {};
+  }
+}
+
+// Try to heuristically resolve the type of a dependent expression `E`.
+const Type *resolveDependentExprToType(const Expr *E) {
+  std::vector<const NamedDecl *> Decls = resolveDependentExprToDecls(E);
+  if (Decls.size() != 1) // Names an overload set -- just bail.
+    return nullptr;
+  if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
+    return TD->getTypeForDecl();
+  } else if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
+    return VD->getType().getTypePtrOrNull();
+  }
+  return nullptr;
+}
+
+// Helper function for getMembersReferencedViaDependentName()
+// which takes a possibly-dependent type `T` and heuristically
+// resolves it to a CXXRecordDecl in which we can try name lookup.
+CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) {
+  if (auto *RT = T->getAs<RecordType>()) {
+    return dyn_cast<CXXRecordDecl>(RT->getDecl());
+  }
+
+  if (auto *ICNT = T->getAs<InjectedClassNameType>()) {
+    T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
+  }
+  auto *TST = T->getAs<TemplateSpecializationType>();
+  if (!TST)
+    return nullptr;
+  const ClassTemplateDecl *TD = dyn_cast_or_null<ClassTemplateDecl>(
+      TST->getTemplateName().getAsTemplateDecl());
+  if (!TD)
+    return nullptr;
+  return TD->getTemplatedDecl();
+}
+
+// Given a dependent type and a member name, heuristically resolve the
+// name to one or more declarations.
+// The current heuristic is simply to look up the name in the primary
+// template. This is a heuristic because the template could potentially
+// have specializations that declare different members.
+// Multiple declarations could be returned if the name is overloaded
+// (e.g. an overloaded method in the primary template).
+// This heuristic will give the desired answer in many cases, e.g.
+// for a call to vector<T>::size().
+// The name to look up is provided in the form of a factory that takes
+// an ASTContext, because an ASTContext may be needed to obtain the
+// name (e.g. if it's an operator name), but the caller may not have
+// access to an ASTContext.
+// Optionally, in cases where the type represents the type of a
+// dependent expression, the expression `E` in question can be
+// provided, which allows us to provide richer heuristics by
+// introspecting the expression and trying to reason about its type.
+std::vector<const NamedDecl *> getMembersReferencedViaDependentName(
+    const Expr *E, const Type *T,
+    llvm::function_ref<DeclarationName(ASTContext &)> NameFactory,
+    bool IsNonstaticMember) {
+  if (!T)
+    return {};
+  if (auto *BT = T->getAs<BuiltinType>()) {
+    // If T is the type of a dependent expression, it's just represented
+    // as BultinType::Dependent which gives us no information. If the
+    // caller provides the expression `E`, we can get further by
+    // analyzing it.
+    if (E && BT->getKind() == BuiltinType::Dependent) {
+      T = resolveDependentExprToType(E);
+      if (!T)
+        return {};
+    }
+  }
+  if (auto *ET = T->getAs<EnumType>()) {
+    auto Result =
+        ET->getDecl()->lookup(NameFactory(ET->getDecl()->getASTContext()));
+    return {Result.begin(), Result.end()};
+  }
+  if (auto *RD = resolveTypeToRecordDecl(T)) {
+    if (!RD->hasDefinition())
+      return {};
+    RD = RD->getDefinition();
+    DeclarationName Name = NameFactory(RD->getASTContext());
+    return RD->lookupDependentName(Name, [=](const NamedDecl *D) {
+      return IsNonstaticMember ? D->isCXXInstanceMember()
+                               : !D->isCXXInstanceMember();
+    });
+  }
+  return {};
+}
+
 const NamedDecl *getTemplatePattern(const NamedDecl *D) {
   if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(D)) {
     if (const auto *Result = CRD->getTemplateInstantiationPattern())
@@ -341,21 +416,12 @@
       }
       void
       VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E) {
-        const Type *BaseType = E->getBaseType().getTypePtrOrNull();
-        if (E->isArrow()) {
-          BaseType = getPointeeType(BaseType);
-        }
-        for (const NamedDecl *D : getMembersReferencedViaDependentName(
-                 BaseType, [E](ASTContext &) { return E->getMember(); },
-                 /*IsNonstaticMember=*/true)) {
+        for (const NamedDecl *D : resolveDependentExprToDecls(E)) {
           Outer.add(D, Flags);
         }
       }
       void VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *E) {
-        for (const NamedDecl *D : getMembersReferencedViaDependentName(
-                 E->getQualifier()->getAsType(),
-                 [E](ASTContext &) { return E->getDeclName(); },
-                 /*IsNonstaticMember=*/false)) {
+        for (const NamedDecl *D : resolveDependentExprToDecls(E)) {
           Outer.add(D, Flags);
         }
       }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to