[clang-tools-extra] 9510b09 - [clangd] Factor out the heuristic resolver code into its own class

Nathan Ridge via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 16 01:11:15 PST 2021


Author: Nathan Ridge
Date: 2021-02-16T04:10:52-05:00
New Revision: 9510b09402659e6ba6e29b9caf0504c89bc72871

URL: https://github.com/llvm/llvm-project/commit/9510b09402659e6ba6e29b9caf0504c89bc72871
DIFF: https://github.com/llvm/llvm-project/commit/9510b09402659e6ba6e29b9caf0504c89bc72871.diff

LOG: [clangd] Factor out the heuristic resolver code into its own class

The patch also does some cleanup on the interface of the entry
points from TargetFinder into the heuristic resolution code.

Since the heuristic resolver is created in a place where the
ASTContext is available, it can store the ASTContext and the
NameFactory hack can be removed.

Differential revision: https://reviews.llvm.org/D92290

Added: 
    clang-tools-extra/clangd/HeuristicResolver.cpp
    clang-tools-extra/clangd/HeuristicResolver.h

Modified: 
    clang-tools-extra/clangd/ASTSignals.cpp
    clang-tools-extra/clangd/CMakeLists.txt
    clang-tools-extra/clangd/FindTarget.cpp
    clang-tools-extra/clangd/FindTarget.h
    clang-tools-extra/clangd/Hover.cpp
    clang-tools-extra/clangd/ParsedAST.cpp
    clang-tools-extra/clangd/ParsedAST.h
    clang-tools-extra/clangd/SemanticHighlighting.cpp
    clang-tools-extra/clangd/XRefs.cpp
    clang-tools-extra/clangd/refactor/Rename.cpp
    clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
    clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
    clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
    clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
    clang-tools-extra/clangd/unittests/FindTargetTests.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/ASTSignals.cpp b/clang-tools-extra/clangd/ASTSignals.cpp
index b8cc7f05927a..58c57e147d41 100644
--- a/clang-tools-extra/clangd/ASTSignals.cpp
+++ b/clang-tools-extra/clangd/ASTSignals.cpp
@@ -15,27 +15,30 @@ namespace clangd {
 ASTSignals ASTSignals::derive(const ParsedAST &AST) {
   ASTSignals Signals;
   const SourceManager &SM = AST.getSourceManager();
-  findExplicitReferences(AST.getASTContext(), [&](ReferenceLoc Ref) {
-    for (const NamedDecl *ND : Ref.Targets) {
-      if (!isInsideMainFile(Ref.NameLoc, SM))
-        continue;
-      SymbolID ID = getSymbolID(ND);
-      if (!ID)
-        continue;
-      unsigned &SymbolCount = Signals.ReferencedSymbols[ID];
-      SymbolCount++;
-      // Process namespace only when we see the symbol for the first time.
-      if (SymbolCount != 1)
-        continue;
-      if (const auto *NSD = dyn_cast<NamespaceDecl>(ND->getDeclContext())) {
-        if (NSD->isAnonymousNamespace())
-          continue;
-        std::string NS = printNamespaceScope(*NSD);
-        if (!NS.empty())
-          Signals.RelatedNamespaces[NS]++;
-      }
-    }
-  });
+  findExplicitReferences(
+      AST.getASTContext(),
+      [&](ReferenceLoc Ref) {
+        for (const NamedDecl *ND : Ref.Targets) {
+          if (!isInsideMainFile(Ref.NameLoc, SM))
+            continue;
+          SymbolID ID = getSymbolID(ND);
+          if (!ID)
+            continue;
+          unsigned &SymbolCount = Signals.ReferencedSymbols[ID];
+          SymbolCount++;
+          // Process namespace only when we see the symbol for the first time.
+          if (SymbolCount != 1)
+            continue;
+          if (const auto *NSD = dyn_cast<NamespaceDecl>(ND->getDeclContext())) {
+            if (NSD->isAnonymousNamespace())
+              continue;
+            std::string NS = printNamespaceScope(*NSD);
+            if (!NS.empty())
+              Signals.RelatedNamespaces[NS]++;
+          }
+        }
+      },
+      AST.getHeuristicResolver());
   return Signals;
 }
 } // namespace clangd

diff  --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt
index 1d12e7e2355d..3de510665723 100644
--- a/clang-tools-extra/clangd/CMakeLists.txt
+++ b/clang-tools-extra/clangd/CMakeLists.txt
@@ -71,6 +71,7 @@ add_clang_library(clangDaemon
   GlobalCompilationDatabase.cpp
   Headers.cpp
   HeaderSourceSwitch.cpp
+  HeuristicResolver.cpp
   Hover.cpp
   IncludeFixer.cpp
   JSONTransport.cpp

diff  --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp
index 46c98685dabb..2dabae47b519 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -8,6 +8,7 @@
 
 #include "FindTarget.h"
 #include "AST.h"
+#include "HeuristicResolver.h"
 #include "support/Logger.h"
 #include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/Decl.h"
@@ -56,210 +57,6 @@ LLVM_ATTRIBUTE_UNUSED std::string nodeToString(const DynTypedNode &N) {
   return S;
 }
 
-// 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) {
-  assert(T);
-
-  if (const auto *RT = T->getAs<RecordType>())
-    return dyn_cast<CXXRecordDecl>(RT->getDecl());
-
-  if (const auto *ICNT = T->getAs<InjectedClassNameType>())
-    T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
-  if (!T)
-    return nullptr;
-
-  const 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 tag-decl 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 
diff erent 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.
-std::vector<const NamedDecl *> getMembersReferencedViaDependentName(
-    const Type *T,
-    llvm::function_ref<DeclarationName(ASTContext &)> NameFactory,
-    llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
-  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, Filter);
-  }
-  return {};
-}
-
-const auto NonStaticFilter = [](const NamedDecl *D) {
-  return D->isCXXInstanceMember();
-};
-const auto StaticFilter = [](const NamedDecl *D) {
-  return !D->isCXXInstanceMember();
-};
-const auto ValueFilter = [](const NamedDecl *D) { return isa<ValueDecl>(D); };
-const auto TypeFilter = [](const NamedDecl *D) { return isa<TypeDecl>(D); };
-const auto TemplateFilter = [](const NamedDecl *D) {
-  return isa<TemplateDecl>(D);
-};
-
-// 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 up the name appearing on the RHS.
-const Type *getPointeeType(const Type *T) {
-  if (!T)
-    return nullptr;
-
-  if (T->isPointerType()) {
-    return T->getAs<PointerType>()->getPointeeType().getTypePtrOrNull();
-  }
-
-  // Try to handle smart pointer types.
-
-  // Look up operator-> in the primary template. If we find one, it's probably a
-  // smart pointer type.
-  auto ArrowOps = getMembersReferencedViaDependentName(
-      T,
-      [](ASTContext &Ctx) {
-        return Ctx.DeclarationNames.getCXXOperatorName(OO_Arrow);
-      },
-      NonStaticFilter);
-  if (ArrowOps.empty())
-    return nullptr;
-
-  // Getting the return type of the found operator-> method decl isn't useful,
-  // because we discarded template arguments to perform lookup in the primary
-  // template scope, so the return type would just have the form U* where U is a
-  // template parameter type.
-  // Instead, just handle the common case where the smart pointer type has the
-  // form of SmartPtr<X, ...>, and assume X is the pointee type.
-  auto *TST = T->getAs<TemplateSpecializationType>();
-  if (!TST)
-    return nullptr;
-  if (TST->getNumArgs() == 0)
-    return nullptr;
-  const TemplateArgument &FirstArg = TST->getArg(0);
-  if (FirstArg.getKind() != TemplateArgument::Type)
-    return nullptr;
-  return FirstArg.getAsType().getTypePtrOrNull();
-}
-
-// Forward declaration, needed as this function is mutually recursive
-// with resolveExprToDecls.
-const Type *resolveExprToType(const Expr *E);
-
-// Try to heuristically resolve a possibly-dependent expression `E` to one
-// or more declarations that it likely references.
-std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E) {
-  if (const auto *ME = dyn_cast<CXXDependentScopeMemberExpr>(E)) {
-    const Type *BaseType = ME->getBaseType().getTypePtrOrNull();
-    if (ME->isArrow()) {
-      BaseType = getPointeeType(BaseType);
-    }
-    if (!BaseType)
-      return {};
-    if (const auto *BT = BaseType->getAs<BuiltinType>()) {
-      // If BaseType is the type of a dependent expression, it's just
-      // represented as BultinType::Dependent which gives us no information. We
-      // can get further by analyzing the depedent expression.
-      Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
-      if (Base && BT->getKind() == BuiltinType::Dependent) {
-        BaseType = resolveExprToType(Base);
-      }
-    }
-    return getMembersReferencedViaDependentName(
-        BaseType, [ME](ASTContext &) { return ME->getMember(); },
-        NonStaticFilter);
-  }
-  if (const auto *RE = dyn_cast<DependentScopeDeclRefExpr>(E)) {
-    return getMembersReferencedViaDependentName(
-        RE->getQualifier()->getAsType(),
-        [RE](ASTContext &) { return RE->getDeclName(); }, StaticFilter);
-  }
-  if (const auto *CE = dyn_cast<CallExpr>(E)) {
-    const auto *CalleeType = resolveExprToType(CE->getCallee());
-    if (!CalleeType)
-      return {};
-    if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
-      CalleeType = FnTypePtr->getPointeeType().getTypePtr();
-    if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
-      if (const auto *D =
-              resolveTypeToRecordDecl(FnType->getReturnType().getTypePtr())) {
-        return {D};
-      }
-    }
-  }
-  if (const auto *ME = dyn_cast<MemberExpr>(E))
-    return {ME->getMemberDecl()};
-  if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
-    return {DRE->getFoundDecl()};
-  return {};
-}
-
-const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls) {
-  if (Decls.size() != 1) // Names an overload set -- just bail.
-    return nullptr;
-  if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
-    return TD->getTypeForDecl();
-  }
-  if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
-    return VD->getType().getTypePtrOrNull();
-  }
-  return nullptr;
-}
-
-// Try to heuristically resolve the type of a possibly-dependent expression `E`.
-const Type *resolveExprToType(const Expr *E) {
-  return resolveDeclsToType(resolveExprToDecls(E));
-}
-
-// Try to heuristically resolve the type of a possibly-dependent nested name
-// specifier.
-const Type *resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS) {
-  if (!NNS)
-    return nullptr;
-
-  switch (NNS->getKind()) {
-  case NestedNameSpecifier::TypeSpec:
-  case NestedNameSpecifier::TypeSpecWithTemplate:
-    return NNS->getAsType();
-  case NestedNameSpecifier::Identifier: {
-    return resolveDeclsToType(getMembersReferencedViaDependentName(
-        resolveNestedNameSpecifierToType(NNS->getPrefix()),
-        [&](const ASTContext &) { return NNS->getAsIdentifier(); },
-        TypeFilter));
-  }
-  default:
-    break;
-  }
-  return nullptr;
-}
-
 const NamedDecl *getTemplatePattern(const NamedDecl *D) {
   if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(D)) {
     if (const auto *Result = CRD->getTemplateInstantiationPattern())
@@ -327,6 +124,7 @@ struct TargetFinder {
   using Rel = DeclRelation;
 
 private:
+  const HeuristicResolver *Resolver;
   llvm::SmallDenseMap<const NamedDecl *,
                       std::pair<RelSet, /*InsertionOrder*/ size_t>>
       Decls;
@@ -346,6 +144,8 @@ struct TargetFinder {
   }
 
 public:
+  TargetFinder(const HeuristicResolver *Resolver) : Resolver(Resolver) {}
+
   llvm::SmallVector<std::pair<const NamedDecl *, RelSet>, 1> takeDecls() const {
     using ValTy = std::pair<const NamedDecl *, RelSet>;
     llvm::SmallVector<ValTy, 1> Result;
@@ -385,11 +185,10 @@ struct TargetFinder {
       Flags |= Rel::Alias; // continue with the alias
     } else if (const UnresolvedUsingValueDecl *UUVD =
                    dyn_cast<UnresolvedUsingValueDecl>(D)) {
-      for (const NamedDecl *Target : getMembersReferencedViaDependentName(
-               UUVD->getQualifier()->getAsType(),
-               [UUVD](ASTContext &) { return UUVD->getNameInfo().getName(); },
-               ValueFilter)) {
-        add(Target, Flags); // no Underlying as this is a non-renaming alias
+      if (Resolver) {
+        for (const NamedDecl *Target : Resolver->resolveUsingValueDecl(UUVD)) {
+          add(Target, Flags); // no Underlying as this is a non-renaming alias
+        }
       }
       Flags |= Rel::Alias; // continue with the alias
     } else if (const UsingShadowDecl *USD = dyn_cast<UsingShadowDecl>(D)) {
@@ -486,13 +285,17 @@ struct TargetFinder {
       }
       void
       VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E) {
-        for (const NamedDecl *D : resolveExprToDecls(E)) {
-          Outer.add(D, Flags);
+        if (Outer.Resolver) {
+          for (const NamedDecl *D : Outer.Resolver->resolveMemberExpr(E)) {
+            Outer.add(D, Flags);
+          }
         }
       }
       void VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *E) {
-        for (const NamedDecl *D : resolveExprToDecls(E)) {
-          Outer.add(D, Flags);
+        if (Outer.Resolver) {
+          for (const NamedDecl *D : Outer.Resolver->resolveDeclRefExpr(E)) {
+            Outer.add(D, Flags);
+          }
         }
       }
       void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *OIRE) {
@@ -571,20 +374,20 @@ struct TargetFinder {
           Outer.add(TD->getTemplatedDecl(), Flags | Rel::TemplatePattern);
       }
       void VisitDependentNameType(const DependentNameType *DNT) {
-        for (const NamedDecl *ND : getMembersReferencedViaDependentName(
-                 resolveNestedNameSpecifierToType(DNT->getQualifier()),
-                 [DNT](ASTContext &) { return DNT->getIdentifier(); },
-                 TypeFilter)) {
-          Outer.add(ND, Flags);
+        if (Outer.Resolver) {
+          for (const NamedDecl *ND :
+               Outer.Resolver->resolveDependentNameType(DNT)) {
+            Outer.add(ND, Flags);
+          }
         }
       }
       void VisitDependentTemplateSpecializationType(
           const DependentTemplateSpecializationType *DTST) {
-        for (const NamedDecl *ND : getMembersReferencedViaDependentName(
-                 resolveNestedNameSpecifierToType(DTST->getQualifier()),
-                 [DTST](ASTContext &) { return DTST->getIdentifier(); },
-                 TemplateFilter)) {
-          Outer.add(ND, Flags);
+        if (Outer.Resolver) {
+          for (const NamedDecl *ND :
+               Outer.Resolver->resolveTemplateSpecializationType(DTST)) {
+            Outer.add(ND, Flags);
+          }
         }
       }
       void VisitTypedefType(const TypedefType *TT) {
@@ -649,9 +452,14 @@ struct TargetFinder {
       add(NNS->getAsNamespaceAlias(), Flags);
       return;
     case NestedNameSpecifier::Identifier:
+      if (Resolver) {
+        add(QualType(Resolver->resolveNestedNameSpecifierToType(NNS), 0),
+            Flags);
+      }
+      return;
     case NestedNameSpecifier::TypeSpec:
     case NestedNameSpecifier::TypeSpecWithTemplate:
-      add(QualType(resolveNestedNameSpecifierToType(NNS), 0), Flags);
+      add(QualType(NNS->getAsType(), 0), Flags);
       return;
     case NestedNameSpecifier::Global:
       // This should be TUDecl, but we can't get a pointer to it!
@@ -690,9 +498,9 @@ struct TargetFinder {
 } // namespace
 
 llvm::SmallVector<std::pair<const NamedDecl *, DeclRelationSet>, 1>
-allTargetDecls(const DynTypedNode &N) {
+allTargetDecls(const DynTypedNode &N, const HeuristicResolver *Resolver) {
   dlog("allTargetDecls({0})", nodeToString(N));
-  TargetFinder Finder;
+  TargetFinder Finder(Resolver);
   DeclRelationSet Flags;
   if (const Decl *D = N.get<Decl>())
     Finder.add(D, Flags);
@@ -715,10 +523,11 @@ allTargetDecls(const DynTypedNode &N) {
   return Finder.takeDecls();
 }
 
-llvm::SmallVector<const NamedDecl *, 1> targetDecl(const DynTypedNode &N,
-                                                   DeclRelationSet Mask) {
+llvm::SmallVector<const NamedDecl *, 1>
+targetDecl(const DynTypedNode &N, DeclRelationSet Mask,
+           const HeuristicResolver *Resolver) {
   llvm::SmallVector<const NamedDecl *, 1> Result;
-  for (const auto &Entry : allTargetDecls(N)) {
+  for (const auto &Entry : allTargetDecls(N, Resolver)) {
     if (!(Entry.second & ~Mask))
       Result.push_back(Entry.first);
   }
@@ -726,11 +535,12 @@ llvm::SmallVector<const NamedDecl *, 1> targetDecl(const DynTypedNode &N,
 }
 
 llvm::SmallVector<const NamedDecl *, 1>
-explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask) {
+explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask,
+                         const HeuristicResolver *Resolver) {
   assert(!(Mask & (DeclRelation::TemplatePattern |
                    DeclRelation::TemplateInstantiation)) &&
          "explicitReferenceTargets handles templates on its own");
-  auto Decls = allTargetDecls(N);
+  auto Decls = allTargetDecls(N, Resolver);
 
   // We prefer to return template instantiation, but fallback to template
   // pattern if instantiation is not available.
@@ -757,8 +567,12 @@ explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask) {
 }
 
 namespace {
-llvm::SmallVector<ReferenceLoc> refInDecl(const Decl *D) {
+llvm::SmallVector<ReferenceLoc> refInDecl(const Decl *D,
+                                          const HeuristicResolver *Resolver) {
   struct Visitor : ConstDeclVisitor<Visitor> {
+    Visitor(const HeuristicResolver *Resolver) : Resolver(Resolver) {}
+
+    const HeuristicResolver *Resolver;
     llvm::SmallVector<ReferenceLoc> Refs;
 
     void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) {
@@ -772,10 +586,10 @@ llvm::SmallVector<ReferenceLoc> refInDecl(const Decl *D) {
 
     void VisitUsingDecl(const UsingDecl *D) {
       // "using ns::identifier;" is a non-declaration reference.
-      Refs.push_back(
-          ReferenceLoc{D->getQualifierLoc(), D->getLocation(), /*IsDecl=*/false,
-                       explicitReferenceTargets(DynTypedNode::create(*D),
-                                                DeclRelation::Underlying)});
+      Refs.push_back(ReferenceLoc{
+          D->getQualifierLoc(), D->getLocation(), /*IsDecl=*/false,
+          explicitReferenceTargets(DynTypedNode::create(*D),
+                                   DeclRelation::Underlying, Resolver)});
     }
 
     void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) {
@@ -821,13 +635,17 @@ llvm::SmallVector<ReferenceLoc> refInDecl(const Decl *D) {
     }
   };
 
-  Visitor V;
+  Visitor V{Resolver};
   V.Visit(D);
   return V.Refs;
 }
 
-llvm::SmallVector<ReferenceLoc> refInStmt(const Stmt *S) {
+llvm::SmallVector<ReferenceLoc> refInStmt(const Stmt *S,
+                                          const HeuristicResolver *Resolver) {
   struct Visitor : ConstStmtVisitor<Visitor> {
+    Visitor(const HeuristicResolver *Resolver) : Resolver(Resolver) {}
+
+    const HeuristicResolver *Resolver;
     // FIXME: handle more complicated cases: more ObjC, designated initializers.
     llvm::SmallVector<ReferenceLoc> Refs;
 
@@ -848,7 +666,7 @@ llvm::SmallVector<ReferenceLoc> refInStmt(const Stmt *S) {
     void VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *E) {
       Refs.push_back(ReferenceLoc{
           E->getQualifierLoc(), E->getNameInfo().getLoc(), /*IsDecl=*/false,
-          explicitReferenceTargets(DynTypedNode::create(*E), {})});
+          explicitReferenceTargets(DynTypedNode::create(*E), {}, Resolver)});
     }
 
     void VisitMemberExpr(const MemberExpr *E) {
@@ -864,10 +682,10 @@ llvm::SmallVector<ReferenceLoc> refInStmt(const Stmt *S) {
 
     void
     VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E) {
-      Refs.push_back(
-          ReferenceLoc{E->getQualifierLoc(), E->getMemberNameInfo().getLoc(),
-                       /*IsDecl=*/false,
-                       explicitReferenceTargets(DynTypedNode::create(*E), {})});
+      Refs.push_back(ReferenceLoc{
+          E->getQualifierLoc(), E->getMemberNameInfo().getLoc(),
+          /*IsDecl=*/false,
+          explicitReferenceTargets(DynTypedNode::create(*E), {}, Resolver)});
     }
 
     void VisitOverloadExpr(const OverloadExpr *E) {
@@ -890,7 +708,7 @@ llvm::SmallVector<ReferenceLoc> refInStmt(const Stmt *S) {
           NestedNameSpecifierLoc(), E->getLocation(),
           /*IsDecl=*/false,
           // Select the getter, setter, or @property depending on the call.
-          explicitReferenceTargets(DynTypedNode::create(*E), {})});
+          explicitReferenceTargets(DynTypedNode::create(*E), {}, Resolver)});
     }
 
     void VisitDesignatedInitExpr(const DesignatedInitExpr *DIE) {
@@ -922,13 +740,17 @@ llvm::SmallVector<ReferenceLoc> refInStmt(const Stmt *S) {
     }
   };
 
-  Visitor V;
+  Visitor V{Resolver};
   V.Visit(S);
   return V.Refs;
 }
 
-llvm::SmallVector<ReferenceLoc> refInTypeLoc(TypeLoc L) {
+llvm::SmallVector<ReferenceLoc>
+refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) {
   struct Visitor : TypeLocVisitor<Visitor> {
+    Visitor(const HeuristicResolver *Resolver) : Resolver(Resolver) {}
+
+    const HeuristicResolver *Resolver;
     llvm::Optional<ReferenceLoc> Ref;
 
     void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) {
@@ -967,14 +789,14 @@ llvm::SmallVector<ReferenceLoc> refInTypeLoc(TypeLoc L) {
       Ref = ReferenceLoc{
           NestedNameSpecifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false,
           explicitReferenceTargets(DynTypedNode::create(L.getType()),
-                                   DeclRelation::Alias)};
+                                   DeclRelation::Alias, Resolver)};
     }
     void VisitDeducedTemplateSpecializationTypeLoc(
         DeducedTemplateSpecializationTypeLoc L) {
       Ref = ReferenceLoc{
           NestedNameSpecifierLoc(), L.getNameLoc(), /*IsDecl=*/false,
           explicitReferenceTargets(DynTypedNode::create(L.getType()),
-                                   DeclRelation::Alias)};
+                                   DeclRelation::Alias, Resolver)};
     }
 
     void VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) {
@@ -986,15 +808,16 @@ llvm::SmallVector<ReferenceLoc> refInTypeLoc(TypeLoc L) {
 
     void VisitDependentTemplateSpecializationTypeLoc(
         DependentTemplateSpecializationTypeLoc L) {
-      Ref = ReferenceLoc{
-          L.getQualifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false,
-          explicitReferenceTargets(DynTypedNode::create(L.getType()), {})};
+      Ref = ReferenceLoc{L.getQualifierLoc(), L.getTemplateNameLoc(),
+                         /*IsDecl=*/false,
+                         explicitReferenceTargets(
+                             DynTypedNode::create(L.getType()), {}, Resolver)};
     }
 
     void VisitDependentNameTypeLoc(DependentNameTypeLoc L) {
-      Ref = ReferenceLoc{
-          L.getQualifierLoc(), L.getNameLoc(), /*IsDecl=*/false,
-          explicitReferenceTargets(DynTypedNode::create(L.getType()), {})};
+      Ref = ReferenceLoc{L.getQualifierLoc(), L.getNameLoc(), /*IsDecl=*/false,
+                         explicitReferenceTargets(
+                             DynTypedNode::create(L.getType()), {}, Resolver)};
     }
 
     void VisitTypedefTypeLoc(TypedefTypeLoc L) {
@@ -1005,7 +828,7 @@ llvm::SmallVector<ReferenceLoc> refInTypeLoc(TypeLoc L) {
     }
   };
 
-  Visitor V;
+  Visitor V{Resolver};
   V.Visit(L.getUnqualifiedLoc());
   if (!V.Ref)
     return {};
@@ -1015,8 +838,9 @@ llvm::SmallVector<ReferenceLoc> refInTypeLoc(TypeLoc L) {
 class ExplicitReferenceCollector
     : public RecursiveASTVisitor<ExplicitReferenceCollector> {
 public:
-  ExplicitReferenceCollector(llvm::function_ref<void(ReferenceLoc)> Out)
-      : Out(Out) {
+  ExplicitReferenceCollector(llvm::function_ref<void(ReferenceLoc)> Out,
+                             const HeuristicResolver *Resolver)
+      : Out(Out), Resolver(Resolver) {
     assert(Out);
   }
 
@@ -1123,19 +947,19 @@ class ExplicitReferenceCollector
   ///     function will return the corresponding reference.
   llvm::SmallVector<ReferenceLoc> explicitReference(DynTypedNode N) {
     if (auto *D = N.get<Decl>())
-      return refInDecl(D);
+      return refInDecl(D, Resolver);
     if (auto *S = N.get<Stmt>())
-      return refInStmt(S);
+      return refInStmt(S, Resolver);
     if (auto *NNSL = N.get<NestedNameSpecifierLoc>()) {
       // (!) 'DeclRelation::Alias' ensures we do not loose namespace aliases.
       return {ReferenceLoc{
           NNSL->getPrefix(), NNSL->getLocalBeginLoc(), false,
           explicitReferenceTargets(
               DynTypedNode::create(*NNSL->getNestedNameSpecifier()),
-              DeclRelation::Alias)}};
+              DeclRelation::Alias, Resolver)}};
     }
     if (const TypeLoc *TL = N.get<TypeLoc>())
-      return refInTypeLoc(*TL);
+      return refInTypeLoc(*TL, Resolver);
     if (const CXXCtorInitializer *CCI = N.get<CXXCtorInitializer>()) {
       // Other type initializers (e.g. base initializer) are handled by visiting
       // the typeLoc.
@@ -1168,6 +992,7 @@ class ExplicitReferenceCollector
   }
 
   llvm::function_ref<void(ReferenceLoc)> Out;
+  const HeuristicResolver *Resolver;
   /// TypeLocs starting at these locations must be skipped, see
   /// TraverseElaboratedTypeSpecifierLoc for details.
   llvm::DenseSet<SourceLocation> TypeLocsToSkip;
@@ -1175,18 +1000,22 @@ class ExplicitReferenceCollector
 } // namespace
 
 void findExplicitReferences(const Stmt *S,
-                            llvm::function_ref<void(ReferenceLoc)> Out) {
+                            llvm::function_ref<void(ReferenceLoc)> Out,
+                            const HeuristicResolver *Resolver) {
   assert(S);
-  ExplicitReferenceCollector(Out).TraverseStmt(const_cast<Stmt *>(S));
+  ExplicitReferenceCollector(Out, Resolver).TraverseStmt(const_cast<Stmt *>(S));
 }
 void findExplicitReferences(const Decl *D,
-                            llvm::function_ref<void(ReferenceLoc)> Out) {
+                            llvm::function_ref<void(ReferenceLoc)> Out,
+                            const HeuristicResolver *Resolver) {
   assert(D);
-  ExplicitReferenceCollector(Out).TraverseDecl(const_cast<Decl *>(D));
+  ExplicitReferenceCollector(Out, Resolver).TraverseDecl(const_cast<Decl *>(D));
 }
 void findExplicitReferences(const ASTContext &AST,
-                            llvm::function_ref<void(ReferenceLoc)> Out) {
-  ExplicitReferenceCollector(Out).TraverseAST(const_cast<ASTContext &>(AST));
+                            llvm::function_ref<void(ReferenceLoc)> Out,
+                            const HeuristicResolver *Resolver) {
+  ExplicitReferenceCollector(Out, Resolver)
+      .TraverseAST(const_cast<ASTContext &>(AST));
 }
 
 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, DeclRelation R) {

diff  --git a/clang-tools-extra/clangd/FindTarget.h b/clang-tools-extra/clangd/FindTarget.h
index 92e4354d1eaa..3e4cf8b0c9a0 100644
--- a/clang-tools-extra/clangd/FindTarget.h
+++ b/clang-tools-extra/clangd/FindTarget.h
@@ -37,6 +37,8 @@
 
 namespace clang {
 namespace clangd {
+class HeuristicResolver;
+
 /// Describes the link between an AST node and a Decl it refers to.
 enum class DeclRelation : unsigned;
 /// A bitfield of DeclRelations.
@@ -80,15 +82,16 @@ class DeclRelationSet;
 /// If callers want to support such decls, they should cast the node directly.
 ///
 /// FIXME: some AST nodes cannot be DynTypedNodes, these cannot be specified.
-llvm::SmallVector<const NamedDecl *, 1> targetDecl(const DynTypedNode &,
-                                                   DeclRelationSet Mask);
+llvm::SmallVector<const NamedDecl *, 1>
+targetDecl(const DynTypedNode &, DeclRelationSet Mask,
+           const HeuristicResolver *Resolver);
 
 /// Similar to targetDecl(), however instead of applying a filter, all possible
 /// decls are returned along with their DeclRelationSets.
 /// This is suitable for indexing, where everything is recorded and filtering
 /// is applied later.
 llvm::SmallVector<std::pair<const NamedDecl *, DeclRelationSet>, 1>
-allTargetDecls(const DynTypedNode &);
+allTargetDecls(const DynTypedNode &, const HeuristicResolver *);
 
 enum class DeclRelation : unsigned {
   // Template options apply when the declaration is an instantiated template.
@@ -146,11 +149,14 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceLoc R);
 /// FIXME: currently this does not report references to overloaded operators.
 /// FIXME: extend to report location information about declaration names too.
 void findExplicitReferences(const Stmt *S,
-                            llvm::function_ref<void(ReferenceLoc)> Out);
+                            llvm::function_ref<void(ReferenceLoc)> Out,
+                            const HeuristicResolver *Resolver);
 void findExplicitReferences(const Decl *D,
-                            llvm::function_ref<void(ReferenceLoc)> Out);
+                            llvm::function_ref<void(ReferenceLoc)> Out,
+                            const HeuristicResolver *Resolver);
 void findExplicitReferences(const ASTContext &AST,
-                            llvm::function_ref<void(ReferenceLoc)> Out);
+                            llvm::function_ref<void(ReferenceLoc)> Out,
+                            const HeuristicResolver *Resolver);
 
 /// Find declarations explicitly referenced in the source code defined by \p N.
 /// For templates, will prefer to return a template instantiation whenever
@@ -162,7 +168,8 @@ void findExplicitReferences(const ASTContext &AST,
 ///    ^~~ there is no Decl for 'Ptr<int>', so we return the template pattern.
 /// \p Mask should not contain TemplatePattern or TemplateInstantiation.
 llvm::SmallVector<const NamedDecl *, 1>
-explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask);
+explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask,
+                         const HeuristicResolver *Resolver);
 
 // Boring implementation details of bitfield.
 

diff  --git a/clang-tools-extra/clangd/HeuristicResolver.cpp b/clang-tools-extra/clangd/HeuristicResolver.cpp
new file mode 100644
index 000000000000..27ff0c7ea7cf
--- /dev/null
+++ b/clang-tools-extra/clangd/HeuristicResolver.cpp
@@ -0,0 +1,225 @@
+//===--- HeuristicResolver.cpp ---------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeuristicResolver.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprCXX.h"
+
+namespace clang {
+namespace clangd {
+
+// Convenience lambdas for use as the 'Filter' parameter of
+// HeuristicResolver::resolveDependentMember().
+const auto NonStaticFilter = [](const NamedDecl *D) {
+  return D->isCXXInstanceMember();
+};
+const auto StaticFilter = [](const NamedDecl *D) {
+  return !D->isCXXInstanceMember();
+};
+const auto ValueFilter = [](const NamedDecl *D) { return isa<ValueDecl>(D); };
+const auto TypeFilter = [](const NamedDecl *D) { return isa<TypeDecl>(D); };
+const auto TemplateFilter = [](const NamedDecl *D) {
+  return isa<TemplateDecl>(D);
+};
+
+// Helper function for HeuristicResolver::resolveDependentMember()
+// 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) {
+  assert(T);
+
+  if (const auto *RT = T->getAs<RecordType>())
+    return dyn_cast<CXXRecordDecl>(RT->getDecl());
+
+  if (const auto *ICNT = T->getAs<InjectedClassNameType>())
+    T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
+  if (!T)
+    return nullptr;
+
+  const 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();
+}
+
+const Type *HeuristicResolver::getPointeeType(const Type *T) const {
+  if (!T)
+    return nullptr;
+
+  if (T->isPointerType()) {
+    return T->getAs<PointerType>()->getPointeeType().getTypePtrOrNull();
+  }
+
+  // Try to handle smart pointer types.
+
+  // Look up operator-> in the primary template. If we find one, it's probably a
+  // smart pointer type.
+  auto ArrowOps = resolveDependentMember(
+      T, Ctx.DeclarationNames.getCXXOperatorName(OO_Arrow), NonStaticFilter);
+  if (ArrowOps.empty())
+    return nullptr;
+
+  // Getting the return type of the found operator-> method decl isn't useful,
+  // because we discarded template arguments to perform lookup in the primary
+  // template scope, so the return type would just have the form U* where U is a
+  // template parameter type.
+  // Instead, just handle the common case where the smart pointer type has the
+  // form of SmartPtr<X, ...>, and assume X is the pointee type.
+  auto *TST = T->getAs<TemplateSpecializationType>();
+  if (!TST)
+    return nullptr;
+  if (TST->getNumArgs() == 0)
+    return nullptr;
+  const TemplateArgument &FirstArg = TST->getArg(0);
+  if (FirstArg.getKind() != TemplateArgument::Type)
+    return nullptr;
+  return FirstArg.getAsType().getTypePtrOrNull();
+}
+
+std::vector<const NamedDecl *> HeuristicResolver::resolveMemberExpr(
+    const CXXDependentScopeMemberExpr *ME) const {
+  const Type *BaseType = ME->getBaseType().getTypePtrOrNull();
+  if (ME->isArrow()) {
+    BaseType = getPointeeType(BaseType);
+  }
+  if (!BaseType)
+    return {};
+  if (const auto *BT = BaseType->getAs<BuiltinType>()) {
+    // If BaseType is the type of a dependent expression, it's just
+    // represented as BultinType::Dependent which gives us no information. We
+    // can get further by analyzing the depedent expression.
+    Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
+    if (Base && BT->getKind() == BuiltinType::Dependent) {
+      BaseType = resolveExprToType(Base);
+    }
+  }
+  return resolveDependentMember(BaseType, ME->getMember(), NonStaticFilter);
+}
+
+std::vector<const NamedDecl *> HeuristicResolver::resolveDeclRefExpr(
+    const DependentScopeDeclRefExpr *RE) const {
+  return resolveDependentMember(RE->getQualifier()->getAsType(),
+                                RE->getDeclName(), StaticFilter);
+}
+
+std::vector<const NamedDecl *>
+HeuristicResolver::resolveCallExpr(const CallExpr *CE) const {
+  const auto *CalleeType = resolveExprToType(CE->getCallee());
+  if (!CalleeType)
+    return {};
+  if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
+    CalleeType = FnTypePtr->getPointeeType().getTypePtr();
+  if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
+    if (const auto *D =
+            resolveTypeToRecordDecl(FnType->getReturnType().getTypePtr())) {
+      return {D};
+    }
+  }
+  return {};
+}
+
+std::vector<const NamedDecl *> HeuristicResolver::resolveUsingValueDecl(
+    const UnresolvedUsingValueDecl *UUVD) const {
+  return resolveDependentMember(UUVD->getQualifier()->getAsType(),
+                                UUVD->getNameInfo().getName(), ValueFilter);
+}
+
+std::vector<const NamedDecl *> HeuristicResolver::resolveDependentNameType(
+    const DependentNameType *DNT) const {
+  return resolveDependentMember(
+      resolveNestedNameSpecifierToType(DNT->getQualifier()),
+      DNT->getIdentifier(), TypeFilter);
+}
+
+std::vector<const NamedDecl *>
+HeuristicResolver::resolveTemplateSpecializationType(
+    const DependentTemplateSpecializationType *DTST) const {
+  return resolveDependentMember(
+      resolveNestedNameSpecifierToType(DTST->getQualifier()),
+      DTST->getIdentifier(), TemplateFilter);
+}
+
+const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls) {
+  if (Decls.size() != 1) // Names an overload set -- just bail.
+    return nullptr;
+  if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
+    return TD->getTypeForDecl();
+  }
+  if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
+    return VD->getType().getTypePtrOrNull();
+  }
+  return nullptr;
+}
+
+const Type *HeuristicResolver::resolveExprToType(const Expr *E) const {
+  if (const auto *ME = dyn_cast<CXXDependentScopeMemberExpr>(E)) {
+    return resolveDeclsToType(resolveMemberExpr(ME));
+  }
+  if (const auto *RE = dyn_cast<DependentScopeDeclRefExpr>(E)) {
+    return resolveDeclsToType(resolveDeclRefExpr(RE));
+  }
+  if (const auto *CE = dyn_cast<CallExpr>(E)) {
+    return resolveDeclsToType(resolveCallExpr(CE));
+  }
+  if (const auto *ME = dyn_cast<MemberExpr>(E))
+    return resolveDeclsToType({ME->getMemberDecl()});
+
+  return E->getType().getTypePtr();
+}
+
+const Type *HeuristicResolver::resolveNestedNameSpecifierToType(
+    const NestedNameSpecifier *NNS) const {
+  if (!NNS)
+    return nullptr;
+
+  // The purpose of this function is to handle the dependent (Kind ==
+  // Identifier) case, but we need to recurse on the prefix because
+  // that may be dependent as well, so for convenience handle
+  // the TypeSpec cases too.
+  switch (NNS->getKind()) {
+  case NestedNameSpecifier::TypeSpec:
+  case NestedNameSpecifier::TypeSpecWithTemplate:
+    return NNS->getAsType();
+  case NestedNameSpecifier::Identifier: {
+    return resolveDeclsToType(resolveDependentMember(
+        resolveNestedNameSpecifierToType(NNS->getPrefix()),
+        NNS->getAsIdentifier(), TypeFilter));
+  }
+  default:
+    break;
+  }
+  return nullptr;
+}
+
+std::vector<const NamedDecl *> HeuristicResolver::resolveDependentMember(
+    const Type *T, DeclarationName Name,
+    llvm::function_ref<bool(const NamedDecl *ND)> Filter) const {
+  if (!T)
+    return {};
+  if (auto *ET = T->getAs<EnumType>()) {
+    auto Result = ET->getDecl()->lookup(Name);
+    return {Result.begin(), Result.end()};
+  }
+  if (auto *RD = resolveTypeToRecordDecl(T)) {
+    if (!RD->hasDefinition())
+      return {};
+    RD = RD->getDefinition();
+    return RD->lookupDependentName(Name, Filter);
+  }
+  return {};
+}
+
+} // namespace clangd
+} // namespace clang
\ No newline at end of file

diff  --git a/clang-tools-extra/clangd/HeuristicResolver.h b/clang-tools-extra/clangd/HeuristicResolver.h
new file mode 100644
index 000000000000..30f8eb16bd58
--- /dev/null
+++ b/clang-tools-extra/clangd/HeuristicResolver.h
@@ -0,0 +1,99 @@
+//===--- HeuristicResolver.h - Resolution of dependent names -----*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEURISTIC_RESOLVER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEURISTIC_RESOLVER_H
+
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/STLExtras.h"
+#include <vector>
+
+namespace clang {
+
+class ASTContext;
+class CallExpr;
+class CXXDependentScopeMemberExpr;
+class DeclarationName;
+class DependentScopeDeclRefExpr;
+class NamedDecl;
+class Type;
+class UnresolvedUsingValueDecl;
+
+namespace clangd {
+
+// This class heuristic resolution of declarations and types in template code.
+//
+// As a compiler, clang only needs to perform certain types of processing on
+// template code (such as resolving dependent names to declarations, or
+// resolving the type of a dependent expression) after instantiation. Indeed,
+// C++ language features such as template specialization mean such resolution
+// cannot be done accurately before instantiation
+//
+// However, template code is written and read in uninstantiated form, and clangd
+// would like to provide editor features like go-to-definition in template code
+// where possible. To this end, clangd attempts to resolve declarations and
+// types in uninstantiated code by using heuristics, understanding that the
+// results may not be fully accurate but that this is better than nothing.
+//
+// At this time, the heuristic used is a simple but effective one: assume that
+// template instantiations are based on the primary template definition and not
+// not a specialization. More advanced heuristics may be added in the future.
+class HeuristicResolver {
+public:
+  HeuristicResolver(ASTContext &Ctx) : Ctx(Ctx) {}
+
+  // Try to heuristically resolve certain types of expressions, declarations, or
+  // types to one or more likely-referenced declarations.
+  std::vector<const NamedDecl *>
+  resolveMemberExpr(const CXXDependentScopeMemberExpr *ME) const;
+  std::vector<const NamedDecl *>
+  resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) const;
+  std::vector<const NamedDecl *> resolveCallExpr(const CallExpr *CE) const;
+  std::vector<const NamedDecl *>
+  resolveUsingValueDecl(const UnresolvedUsingValueDecl *UUVD) const;
+  std::vector<const NamedDecl *>
+  resolveDependentNameType(const DependentNameType *DNT) const;
+  std::vector<const NamedDecl *> resolveTemplateSpecializationType(
+      const DependentTemplateSpecializationType *DTST) const;
+
+  // Try to heuristically resolve a dependent nested name specifier
+  // to the type it likely denotes. Note that *dependent* name specifiers always
+  // denote types, not namespaces.
+  const Type *
+  resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS) const;
+
+private:
+  ASTContext &Ctx;
+
+  // Given a tag-decl 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 
diff erent 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().
+  std::vector<const NamedDecl *> resolveDependentMember(
+      const Type *T, DeclarationName Name,
+      llvm::function_ref<bool(const NamedDecl *ND)> Filter) const;
+
+  // Try to heuristically resolve the type of a possibly-dependent expression
+  // `E`.
+  const Type *resolveExprToType(const Expr *E) const;
+
+  // 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 up the name appearing on the RHS.
+  const Type *getPointeeType(const Type *T) const;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif

diff  --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp
index 42acebd7f293..82c3ccbab47c 100644
--- a/clang-tools-extra/clangd/Hover.cpp
+++ b/clang-tools-extra/clangd/Hover.cpp
@@ -902,7 +902,8 @@ llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
     std::vector<const Decl *> Result;
     if (const SelectionTree::Node *N = ST.commonAncestor()) {
       // FIXME: Fill in HighlightRange with range coming from N->ASTNode.
-      auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Alias);
+      auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Alias,
+                                            AST.getHeuristicResolver());
       if (!Decls.empty()) {
         HI = getHoverContents(Decls.front(), PP, Index);
         // Layout info only shown when hovering on the field/class itself.

diff  --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp
index 1020282f5ee8..24038f0ec35f 100644
--- a/clang-tools-extra/clangd/ParsedAST.cpp
+++ b/clang-tools-extra/clangd/ParsedAST.cpp
@@ -548,6 +548,7 @@ ParsedAST::ParsedAST(llvm::StringRef Version,
       Macros(std::move(Macros)), Diags(std::move(Diags)),
       LocalTopLevelDecls(std::move(LocalTopLevelDecls)),
       Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) {
+  Resolver = std::make_unique<HeuristicResolver>(getASTContext());
   assert(this->Clang);
   assert(this->Action);
 }

diff  --git a/clang-tools-extra/clangd/ParsedAST.h b/clang-tools-extra/clangd/ParsedAST.h
index 05818b9dba80..263a097b14cb 100644
--- a/clang-tools-extra/clangd/ParsedAST.h
+++ b/clang-tools-extra/clangd/ParsedAST.h
@@ -24,6 +24,7 @@
 #include "Compiler.h"
 #include "Diagnostics.h"
 #include "Headers.h"
+#include "HeuristicResolver.h"
 #include "Preamble.h"
 #include "index/CanonicalIncludes.h"
 #include "support/Path.h"
@@ -109,6 +110,10 @@ class ParsedAST {
   /// AST. Might be None if no Preamble is used.
   llvm::Optional<llvm::StringRef> preambleVersion() const;
 
+  const HeuristicResolver *getHeuristicResolver() const {
+    return Resolver.get();
+  }
+
 private:
   ParsedAST(llvm::StringRef Version,
             std::shared_ptr<const PreambleData> Preamble,
@@ -144,6 +149,7 @@ class ParsedAST {
   std::vector<Decl *> LocalTopLevelDecls;
   IncludeStructure Includes;
   CanonicalIncludes CanonIncludes;
+  std::unique_ptr<HeuristicResolver> Resolver;
 };
 
 } // namespace clangd

diff  --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp
index 23a58b68ce32..da8ee7e41ebd 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -535,34 +535,37 @@ std::vector<HighlightingToken> getSemanticHighlightings(ParsedAST &AST) {
   // Highlight 'decltype' and 'auto' as their underlying types.
   CollectExtraHighlightings(Builder).TraverseAST(C);
   // Highlight all decls and references coming from the AST.
-  findExplicitReferences(C, [&](ReferenceLoc R) {
-    for (const NamedDecl *Decl : R.Targets) {
-      if (!canHighlightName(Decl->getDeclName()))
-        continue;
-      auto Kind = kindForDecl(Decl);
-      if (!Kind)
-        continue;
-      auto &Tok = Builder.addToken(R.NameLoc, *Kind);
-
-      // The attribute tests don't want to look at the template.
-      if (auto *TD = dyn_cast<TemplateDecl>(Decl)) {
-        if (auto *Templated = TD->getTemplatedDecl())
-          Decl = Templated;
-      }
-      if (auto Mod = scopeModifier(Decl))
-        Tok.addModifier(*Mod);
-      if (isConst(Decl))
-        Tok.addModifier(HighlightingModifier::Readonly);
-      if (isStatic(Decl))
-        Tok.addModifier(HighlightingModifier::Static);
-      if (isAbstract(Decl))
-        Tok.addModifier(HighlightingModifier::Abstract);
-      if (Decl->isDeprecated())
-        Tok.addModifier(HighlightingModifier::Deprecated);
-      if (R.IsDecl)
-        Tok.addModifier(HighlightingModifier::Declaration);
-    }
-  });
+  findExplicitReferences(
+      C,
+      [&](ReferenceLoc R) {
+        for (const NamedDecl *Decl : R.Targets) {
+          if (!canHighlightName(Decl->getDeclName()))
+            continue;
+          auto Kind = kindForDecl(Decl);
+          if (!Kind)
+            continue;
+          auto &Tok = Builder.addToken(R.NameLoc, *Kind);
+
+          // The attribute tests don't want to look at the template.
+          if (auto *TD = dyn_cast<TemplateDecl>(Decl)) {
+            if (auto *Templated = TD->getTemplatedDecl())
+              Decl = Templated;
+          }
+          if (auto Mod = scopeModifier(Decl))
+            Tok.addModifier(*Mod);
+          if (isConst(Decl))
+            Tok.addModifier(HighlightingModifier::Readonly);
+          if (isStatic(Decl))
+            Tok.addModifier(HighlightingModifier::Static);
+          if (isAbstract(Decl))
+            Tok.addModifier(HighlightingModifier::Abstract);
+          if (Decl->isDeprecated())
+            Tok.addModifier(HighlightingModifier::Deprecated);
+          if (R.IsDecl)
+            Tok.addModifier(HighlightingModifier::Declaration);
+        }
+      },
+      AST.getHeuristicResolver());
   // Add highlightings for macro references.
   auto AddMacro = [&](const MacroOccurrence &M) {
     auto &T = Builder.addToken(M.Rng, HighlightingKind::Macro);

diff  --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index c4a73ebad3e8..ff3b8d73b64e 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -182,7 +182,8 @@ getDeclAtPositionWithRelations(ParsedAST &AST, SourceLocation Pos,
     if (const SelectionTree::Node *N = ST.commonAncestor()) {
       if (NodeKind)
         *NodeKind = N->ASTNode.getNodeKind();
-      llvm::copy_if(allTargetDecls(N->ASTNode), std::back_inserter(Result),
+      llvm::copy_if(allTargetDecls(N->ASTNode, AST.getHeuristicResolver()),
+                    std::back_inserter(Result),
                     [&](auto &Entry) { return !(Entry.second & ~Relations); });
     }
     return !Result.empty();
@@ -496,7 +497,8 @@ std::vector<LocatedSymbol> locateSymbolForType(const ParsedAST &AST,
   }
 
   auto Decls = targetDecl(DynTypedNode::create(Type.getNonReferenceType()),
-                          DeclRelation::TemplatePattern | DeclRelation::Alias);
+                          DeclRelation::TemplatePattern | DeclRelation::Alias,
+                          AST.getHeuristicResolver());
   if (Decls.empty())
     return {};
 
@@ -1213,7 +1215,8 @@ std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
     if (const SelectionTree::Node *N = ST.commonAncestor()) {
       DeclRelationSet Relations =
           DeclRelation::TemplatePattern | DeclRelation::Alias;
-      auto Decls = targetDecl(N->ASTNode, Relations);
+      auto Decls =
+          targetDecl(N->ASTNode, Relations, AST.getHeuristicResolver());
       if (!Decls.empty()) {
         // FIXME: we may get multiple DocumentHighlights with the same location
         // and 
diff erent kinds, deduplicate them.
@@ -1706,7 +1709,7 @@ static void fillSuperTypes(const CXXRecordDecl &CXXRD, ASTContext &ASTCtx,
 
 const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
   auto RecordFromNode =
-      [](const SelectionTree::Node *N) -> const CXXRecordDecl * {
+      [&AST](const SelectionTree::Node *N) -> const CXXRecordDecl * {
     if (!N)
       return nullptr;
 
@@ -1714,7 +1717,8 @@ const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
     // instantiations and template patterns, and prefer the former if available
     // (generally, one will be available for non-dependent specializations of a
     // class template).
-    auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Underlying);
+    auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Underlying,
+                                          AST.getHeuristicResolver());
     if (Decls.empty())
       return nullptr;
 
@@ -1942,13 +1946,16 @@ llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
   if (!FD->hasBody())
     return {};
   llvm::DenseSet<const Decl *> DeclRefs;
-  findExplicitReferences(FD, [&](ReferenceLoc Ref) {
-    for (const Decl *D : Ref.Targets) {
-      if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter() &&
-          !Ref.IsDecl)
-        DeclRefs.insert(D);
-    }
-  });
+  findExplicitReferences(
+      FD,
+      [&](ReferenceLoc Ref) {
+        for (const Decl *D : Ref.Targets) {
+          if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter() &&
+              !Ref.IsDecl)
+            DeclRefs.insert(D);
+        }
+      },
+      AST.getHeuristicResolver());
   return DeclRefs;
 }
 } // namespace clangd

diff  --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp
index 96cd79bcea35..cc6830d0ab37 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -149,7 +149,8 @@ llvm::DenseSet<const NamedDecl *> locateDeclAt(ParsedAST &AST,
   llvm::DenseSet<const NamedDecl *> Result;
   for (const NamedDecl *D :
        targetDecl(SelectedNode->ASTNode,
-                  DeclRelation::Alias | DeclRelation::TemplatePattern)) {
+                  DeclRelation::Alias | DeclRelation::TemplatePattern,
+                  AST.getHeuristicResolver())) {
     Result.insert(canonicalRenameDecl(D));
   }
   return Result;
@@ -259,16 +260,19 @@ std::vector<SourceLocation> findOccurrencesWithinFile(ParsedAST &AST,
 
   std::vector<SourceLocation> Results;
   for (Decl *TopLevelDecl : AST.getLocalTopLevelDecls()) {
-    findExplicitReferences(TopLevelDecl, [&](ReferenceLoc Ref) {
-      if (Ref.Targets.empty())
-        return;
-      for (const auto *Target : Ref.Targets) {
-        if (canonicalRenameDecl(Target) == &ND) {
-          Results.push_back(Ref.NameLoc);
-          return;
-        }
-      }
-    });
+    findExplicitReferences(
+        TopLevelDecl,
+        [&](ReferenceLoc Ref) {
+          if (Ref.Targets.empty())
+            return;
+          for (const auto *Target : Ref.Targets) {
+            if (canonicalRenameDecl(Target) == &ND) {
+              Results.push_back(Ref.NameLoc);
+              return;
+            }
+          }
+        },
+        AST.getHeuristicResolver());
   }
 
   return Results;

diff  --git a/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
index f1c6f666e7d0..14d2d082ce0f 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
@@ -139,7 +139,8 @@ bool checkDeclsAreVisible(const llvm::DenseSet<const Decl *> &DeclRefs,
 // Rewrites body of FD by re-spelling all of the names to make sure they are
 // still valid in context of Target.
 llvm::Expected<std::string> qualifyAllDecls(const FunctionDecl *FD,
-                                            const FunctionDecl *Target) {
+                                            const FunctionDecl *Target,
+                                            const HeuristicResolver *Resolver) {
   // There are three types of spellings that needs to be qualified in a function
   // body:
   // - Types:       Foo                 -> ns::Foo
@@ -162,48 +163,54 @@ llvm::Expected<std::string> qualifyAllDecls(const FunctionDecl *FD,
 
   tooling::Replacements Replacements;
   bool HadErrors = false;
-  findExplicitReferences(FD->getBody(), [&](ReferenceLoc Ref) {
-    // Since we want to qualify only the first qualifier, skip names with a
-    // qualifier.
-    if (Ref.Qualifier)
-      return;
-    // There might be no decl in dependent contexts, there's nothing much we can
-    // do in such cases.
-    if (Ref.Targets.empty())
-      return;
-    // Do not qualify names introduced by macro expansions.
-    if (Ref.NameLoc.isMacroID())
-      return;
+  findExplicitReferences(
+      FD->getBody(),
+      [&](ReferenceLoc Ref) {
+        // Since we want to qualify only the first qualifier, skip names with a
+        // qualifier.
+        if (Ref.Qualifier)
+          return;
+        // There might be no decl in dependent contexts, there's nothing much we
+        // can do in such cases.
+        if (Ref.Targets.empty())
+          return;
+        // Do not qualify names introduced by macro expansions.
+        if (Ref.NameLoc.isMacroID())
+          return;
 
-    for (const NamedDecl *ND : Ref.Targets) {
-      if (ND->getDeclContext() != Ref.Targets.front()->getDeclContext()) {
-        elog("define inline: Targets from multiple contexts: {0}, {1}",
-             printQualifiedName(*Ref.Targets.front()), printQualifiedName(*ND));
-        HadErrors = true;
-        return;
-      }
-    }
-    // All Targets are in the same scope, so we can safely chose first one.
-    const NamedDecl *ND = Ref.Targets.front();
-    // Skip anything from a non-namespace scope, these can be:
-    // - Function or Method scopes, which means decl is local and doesn't need
-    //   qualification.
-    // - From Class/Struct/Union scope, which again doesn't need any qualifiers,
-    //   rather the left side of it requires qualification, like:
-    //   namespace a { class Bar { public: static int x; } }
-    //   void foo() { Bar::x; }
-    //                ~~~~~ -> we need to qualify Bar not x.
-    if (!ND->getDeclContext()->isNamespace())
-      return;
+        for (const NamedDecl *ND : Ref.Targets) {
+          if (ND->getDeclContext() != Ref.Targets.front()->getDeclContext()) {
+            elog("define inline: Targets from multiple contexts: {0}, {1}",
+                 printQualifiedName(*Ref.Targets.front()),
+                 printQualifiedName(*ND));
+            HadErrors = true;
+            return;
+          }
+        }
+        // All Targets are in the same scope, so we can safely chose first one.
+        const NamedDecl *ND = Ref.Targets.front();
+        // Skip anything from a non-namespace scope, these can be:
+        // - Function or Method scopes, which means decl is local and doesn't
+        // need
+        //   qualification.
+        // - From Class/Struct/Union scope, which again doesn't need any
+        // qualifiers,
+        //   rather the left side of it requires qualification, like:
+        //   namespace a { class Bar { public: static int x; } }
+        //   void foo() { Bar::x; }
+        //                ~~~~~ -> we need to qualify Bar not x.
+        if (!ND->getDeclContext()->isNamespace())
+          return;
 
-    const std::string Qualifier = getQualification(
-        FD->getASTContext(), TargetContext, Target->getBeginLoc(), ND);
-    if (auto Err = Replacements.add(
-            tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier))) {
-      HadErrors = true;
-      elog("define inline: Failed to add quals: {0}", std::move(Err));
-    }
-  });
+        const std::string Qualifier = getQualification(
+            FD->getASTContext(), TargetContext, Target->getBeginLoc(), ND);
+        if (auto Err = Replacements.add(
+                tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier))) {
+          HadErrors = true;
+          elog("define inline: Failed to add quals: {0}", std::move(Err));
+        }
+      },
+      Resolver);
 
   if (HadErrors)
     return error(
@@ -230,7 +237,8 @@ llvm::Expected<std::string> qualifyAllDecls(const FunctionDecl *FD,
 /// Generates Replacements for changing template and function parameter names in
 /// \p Dest to be the same as in \p Source.
 llvm::Expected<tooling::Replacements>
-renameParameters(const FunctionDecl *Dest, const FunctionDecl *Source) {
+renameParameters(const FunctionDecl *Dest, const FunctionDecl *Source,
+                 const HeuristicResolver *Resolver) {
   llvm::DenseMap<const Decl *, std::string> ParamToNewName;
   llvm::DenseMap<const NamedDecl *, std::vector<SourceLocation>> RefLocs;
   auto HandleParam = [&](const NamedDecl *DestParam,
@@ -286,7 +294,8 @@ renameParameters(const FunctionDecl *Dest, const FunctionDecl *Source) {
         if (It == ParamToNewName.end())
           return;
         RefLocs[Target].push_back(Ref.NameLoc);
-      });
+      },
+      Resolver);
 
   // Now try to generate edits for all the refs.
   tooling::Replacements Replacements;
@@ -451,11 +460,13 @@ class DefineInline : public Tweak {
       return error("Couldn't find semicolon for target declaration.");
 
     auto AddInlineIfNecessary = addInlineIfInHeader(Target);
-    auto ParamReplacements = renameParameters(Target, Source);
+    auto ParamReplacements =
+        renameParameters(Target, Source, Sel.AST->getHeuristicResolver());
     if (!ParamReplacements)
       return ParamReplacements.takeError();
 
-    auto QualifiedBody = qualifyAllDecls(Source, Target);
+    auto QualifiedBody =
+        qualifyAllDecls(Source, Target, Sel.AST->getHeuristicResolver());
     if (!QualifiedBody)
       return QualifiedBody.takeError();
 

diff  --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
index 7a40ffae3d1c..4cdd36cbd4c9 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
@@ -144,7 +144,8 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD,
 // FIXME: Drop attributes in function signature.
 llvm::Expected<std::string>
 getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
-                      const syntax::TokenBuffer &TokBuf) {
+                      const syntax::TokenBuffer &TokBuf,
+                      const HeuristicResolver *Resolver) {
   auto &AST = FD->getASTContext();
   auto &SM = AST.getSourceManager();
   auto TargetContext = findContextForNS(TargetNamespace, FD->getDeclContext());
@@ -156,31 +157,36 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
 
   // Finds the first unqualified name in function return type and name, then
   // qualifies those to be valid in TargetContext.
-  findExplicitReferences(FD, [&](ReferenceLoc Ref) {
-    // It is enough to qualify the first qualifier, so skip references with a
-    // qualifier. Also we can't do much if there are no targets or name is
-    // inside a macro body.
-    if (Ref.Qualifier || Ref.Targets.empty() || Ref.NameLoc.isMacroID())
-      return;
-    // Only qualify return type and function name.
-    if (Ref.NameLoc != FD->getReturnTypeSourceRange().getBegin() &&
-        Ref.NameLoc != FD->getLocation())
-      return;
-
-    for (const NamedDecl *ND : Ref.Targets) {
-      if (ND->getDeclContext() != Ref.Targets.front()->getDeclContext()) {
-        elog("Targets from multiple contexts: {0}, {1}",
-             printQualifiedName(*Ref.Targets.front()), printQualifiedName(*ND));
-        return;
-      }
-    }
-    const NamedDecl *ND = Ref.Targets.front();
-    const std::string Qualifier = getQualification(
-        AST, *TargetContext, SM.getLocForStartOfFile(SM.getMainFileID()), ND);
-    if (auto Err = DeclarationCleanups.add(
-            tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier)))
-      Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
-  });
+  findExplicitReferences(
+      FD,
+      [&](ReferenceLoc Ref) {
+        // It is enough to qualify the first qualifier, so skip references with
+        // a qualifier. Also we can't do much if there are no targets or name is
+        // inside a macro body.
+        if (Ref.Qualifier || Ref.Targets.empty() || Ref.NameLoc.isMacroID())
+          return;
+        // Only qualify return type and function name.
+        if (Ref.NameLoc != FD->getReturnTypeSourceRange().getBegin() &&
+            Ref.NameLoc != FD->getLocation())
+          return;
+
+        for (const NamedDecl *ND : Ref.Targets) {
+          if (ND->getDeclContext() != Ref.Targets.front()->getDeclContext()) {
+            elog("Targets from multiple contexts: {0}, {1}",
+                 printQualifiedName(*Ref.Targets.front()),
+                 printQualifiedName(*ND));
+            return;
+          }
+        }
+        const NamedDecl *ND = Ref.Targets.front();
+        const std::string Qualifier =
+            getQualification(AST, *TargetContext,
+                             SM.getLocForStartOfFile(SM.getMainFileID()), ND);
+        if (auto Err = DeclarationCleanups.add(
+                tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier)))
+          Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
+      },
+      Resolver);
 
   // Get rid of default arguments, since they should not be specified in
   // out-of-line definition.
@@ -421,7 +427,8 @@ class DefineOutline : public Tweak {
       return InsertionPoint.takeError();
 
     auto FuncDef = getFunctionSourceCode(
-        Source, InsertionPoint->EnclosingNamespace, Sel.AST->getTokens());
+        Source, InsertionPoint->EnclosingNamespace, Sel.AST->getTokens(),
+        Sel.AST->getHeuristicResolver());
     if (!FuncDef)
       return FuncDef.takeError();
 

diff  --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
index 8eec42876d6b..8255ea519f65 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
@@ -171,16 +171,19 @@ struct ExtractionZone {
   //
   // This performs a partial AST traversal proportional to the size of the
   // enclosing function, so it is possibly expensive.
-  bool requiresHoisting(const SourceManager &SM) const {
+  bool requiresHoisting(const SourceManager &SM,
+                        const HeuristicResolver *Resolver) const {
     // First find all the declarations that happened inside extraction zone.
     llvm::SmallSet<const Decl *, 1> DeclsInExtZone;
     for (auto *RootStmt : RootStmts) {
-      findExplicitReferences(RootStmt,
-                             [&DeclsInExtZone](const ReferenceLoc &Loc) {
-                               if (!Loc.IsDecl)
-                                 return;
-                               DeclsInExtZone.insert(Loc.Targets.front());
-                             });
+      findExplicitReferences(
+          RootStmt,
+          [&DeclsInExtZone](const ReferenceLoc &Loc) {
+            if (!Loc.IsDecl)
+              return;
+            DeclsInExtZone.insert(Loc.Targets.front());
+          },
+          Resolver);
     }
     // Early exit without performing expensive traversal below.
     if (DeclsInExtZone.empty())
@@ -191,15 +194,18 @@ struct ExtractionZone {
                                        ZoneRange.getEnd()))
         continue;
       bool HasPostUse = false;
-      findExplicitReferences(S, [&](const ReferenceLoc &Loc) {
-        if (HasPostUse ||
-            SM.isBeforeInTranslationUnit(Loc.NameLoc, ZoneRange.getEnd()))
-          return;
-        HasPostUse =
-            llvm::any_of(Loc.Targets, [&DeclsInExtZone](const Decl *Target) {
-              return DeclsInExtZone.contains(Target);
-            });
-      });
+      findExplicitReferences(
+          S,
+          [&](const ReferenceLoc &Loc) {
+            if (HasPostUse ||
+                SM.isBeforeInTranslationUnit(Loc.NameLoc, ZoneRange.getEnd()))
+              return;
+            HasPostUse = llvm::any_of(Loc.Targets,
+                                      [&DeclsInExtZone](const Decl *Target) {
+                                        return DeclsInExtZone.contains(Target);
+                                      });
+          },
+          Resolver);
       if (HasPostUse)
         return true;
     }
@@ -741,7 +747,7 @@ bool ExtractFunction::prepare(const Selection &Inputs) {
     return false;
 
   // FIXME: Get rid of this check once we support hoisting.
-  if (MaybeExtZone->requiresHoisting(SM))
+  if (MaybeExtZone->requiresHoisting(SM, Inputs.AST->getHeuristicResolver()))
     return false;
 
   ExtZone = std::move(*MaybeExtZone);

diff  --git a/clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp b/clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
index 344445d8605d..d0394430778c 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
@@ -148,34 +148,37 @@ Expected<Tweak::Effect> RemoveUsingNamespace::apply(const Selection &Inputs) {
   // removing the directive.
   std::vector<SourceLocation> IdentsToQualify;
   for (auto &D : Inputs.AST->getLocalTopLevelDecls()) {
-    findExplicitReferences(D, [&](ReferenceLoc Ref) {
-      if (Ref.Qualifier)
-        return; // This reference is already qualified.
-
-      for (auto *T : Ref.Targets) {
-        if (!visibleContext(T->getDeclContext())
-                 ->Equals(TargetDirective->getNominatedNamespace()))
-          return;
-      }
-      SourceLocation Loc = Ref.NameLoc;
-      if (Loc.isMacroID()) {
-        // Avoid adding qualifiers before macro expansions, it's probably
-        // incorrect, e.g.
-        //   namespace std { int foo(); }
-        //   #define FOO 1 + foo()
-        //   using namespace foo; // provides matrix
-        //   auto x = FOO; // Must not changed to auto x = std::FOO
-        if (!SM.isMacroArgExpansion(Loc))
-          return; // FIXME: report a warning to the users.
-        Loc = SM.getFileLoc(Ref.NameLoc);
-      }
-      assert(Loc.isFileID());
-      if (SM.getFileID(Loc) != SM.getMainFileID())
-        return; // FIXME: report these to the user as warnings?
-      if (SM.isBeforeInTranslationUnit(Loc, FirstUsingDirectiveLoc))
-        return; // Directive was not visible before this point.
-      IdentsToQualify.push_back(Loc);
-    });
+    findExplicitReferences(
+        D,
+        [&](ReferenceLoc Ref) {
+          if (Ref.Qualifier)
+            return; // This reference is already qualified.
+
+          for (auto *T : Ref.Targets) {
+            if (!visibleContext(T->getDeclContext())
+                     ->Equals(TargetDirective->getNominatedNamespace()))
+              return;
+          }
+          SourceLocation Loc = Ref.NameLoc;
+          if (Loc.isMacroID()) {
+            // Avoid adding qualifiers before macro expansions, it's probably
+            // incorrect, e.g.
+            //   namespace std { int foo(); }
+            //   #define FOO 1 + foo()
+            //   using namespace foo; // provides matrix
+            //   auto x = FOO; // Must not changed to auto x = std::FOO
+            if (!SM.isMacroArgExpansion(Loc))
+              return; // FIXME: report a warning to the users.
+            Loc = SM.getFileLoc(Ref.NameLoc);
+          }
+          assert(Loc.isFileID());
+          if (SM.getFileID(Loc) != SM.getMainFileID())
+            return; // FIXME: report these to the user as warnings?
+          if (SM.isBeforeInTranslationUnit(Loc, FirstUsingDirectiveLoc))
+            return; // Directive was not visible before this point.
+          IdentsToQualify.push_back(Loc);
+        },
+        Inputs.AST->getHeuristicResolver());
   }
   // Remove duplicates.
   llvm::sort(IdentsToQualify);

diff  --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
index 2f690284b0c7..c9f035e7e971 100644
--- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -86,7 +86,8 @@ class TargetDeclTest : public ::testing::Test {
     EXPECT_EQ(N->kind(), NodeType) << Selection;
 
     std::vector<PrintedDecl> ActualDecls;
-    for (const auto &Entry : allTargetDecls(N->ASTNode))
+    for (const auto &Entry :
+         allTargetDecls(N->ASTNode, AST.getHeuristicResolver()))
       ActualDecls.emplace_back(Entry.first, Entry.second);
     return ActualDecls;
   }
@@ -978,16 +979,20 @@ class FindExplicitReferencesTest : public ::testing::Test {
 
     std::vector<ReferenceLoc> Refs;
     if (const auto *Func = llvm::dyn_cast<FunctionDecl>(TestDecl))
-      findExplicitReferences(Func->getBody(), [&Refs](ReferenceLoc R) {
-        Refs.push_back(std::move(R));
-      });
+      findExplicitReferences(
+          Func->getBody(),
+          [&Refs](ReferenceLoc R) { Refs.push_back(std::move(R)); },
+          AST.getHeuristicResolver());
     else if (const auto *NS = llvm::dyn_cast<NamespaceDecl>(TestDecl))
-      findExplicitReferences(NS, [&Refs, &NS](ReferenceLoc R) {
-        // Avoid adding the namespace foo decl to the results.
-        if (R.Targets.size() == 1 && R.Targets.front() == NS)
-          return;
-        Refs.push_back(std::move(R));
-      });
+      findExplicitReferences(
+          NS,
+          [&Refs, &NS](ReferenceLoc R) {
+            // Avoid adding the namespace foo decl to the results.
+            if (R.Targets.size() == 1 && R.Targets.front() == NS)
+              return;
+            Refs.push_back(std::move(R));
+          },
+          AST.getHeuristicResolver());
     else
       ADD_FAILURE() << "Failed to find ::foo decl for test";
 


        


More information about the cfe-commits mailing list