nridge updated this revision to Diff 318910.
nridge marked 4 inline comments as done.
nridge added a comment.

Address (many of the) review comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D92290/new/

https://reviews.llvm.org/D92290

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  clang-tools-extra/clangd/FindTarget.cpp
  clang-tools-extra/clangd/FindTarget.h
  clang-tools-extra/clangd/HeuristicResolver.cpp
  clang-tools-extra/clangd/HeuristicResolver.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

Index: clang-tools-extra/clangd/unittests/FindTargetTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -86,7 +86,8 @@
     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;
   }
@@ -974,16 +975,20 @@
 
     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";
 
Index: clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
+++ clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
@@ -148,34 +148,37 @@
   // 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);
Index: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
+++ clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
@@ -171,16 +171,19 @@
   //
   // 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,
+                        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 @@
                                        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 @@
     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);
Index: clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
+++ clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
@@ -144,7 +144,8 @@
 // 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,
+                      HeuristicResolver *Resolver) {
   auto &AST = FD->getASTContext();
   auto &SM = AST.getSourceManager();
   auto TargetContext = findContextForNS(TargetNamespace, FD->getDeclContext());
@@ -156,31 +157,36 @@
 
   // 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 @@
       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();
 
Index: clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
+++ clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
@@ -139,7 +139,8 @@
 // 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,
+                                            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 @@
 
   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 @@
 /// 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,
+                 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 @@
         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 @@
       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();
 
Index: clang-tools-extra/clangd/refactor/Rename.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/Rename.cpp
+++ clang-tools-extra/clangd/refactor/Rename.cpp
@@ -164,7 +164,8 @@
   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;
@@ -303,16 +304,19 @@
 
   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;
Index: clang-tools-extra/clangd/XRefs.cpp
===================================================================
--- clang-tools-extra/clangd/XRefs.cpp
+++ clang-tools-extra/clangd/XRefs.cpp
@@ -182,7 +182,8 @@
     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 @@
   }
 
   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 @@
     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 different kinds, deduplicate them.
@@ -1597,7 +1600,7 @@
 
 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;
 
@@ -1605,7 +1608,8 @@
     // 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;
 
@@ -1833,13 +1837,16 @@
   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
Index: clang-tools-extra/clangd/SemanticHighlighting.cpp
===================================================================
--- clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -390,10 +390,13 @@
   // 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) {
-    if (auto Kind = kindForReference(R))
-      Builder.addToken(R.NameLoc, *Kind);
-  });
+  findExplicitReferences(
+      C,
+      [&](ReferenceLoc R) {
+        if (auto Kind = kindForReference(R))
+          Builder.addToken(R.NameLoc, *Kind);
+      },
+      AST.getHeuristicResolver());
   // Add highlightings for macro references.
   for (const auto &SIDToRefs : AST.getMacros().MacroRefs) {
     for (const auto &M : SIDToRefs.second)
Index: clang-tools-extra/clangd/ParsedAST.h
===================================================================
--- clang-tools-extra/clangd/ParsedAST.h
+++ 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,8 @@
   /// AST. Might be None if no Preamble is used.
   llvm::Optional<llvm::StringRef> preambleVersion() const;
 
+  HeuristicResolver *getHeuristicResolver() const { return Resolver.get(); }
+
 private:
   ParsedAST(llvm::StringRef Version,
             std::shared_ptr<const PreambleData> Preamble,
@@ -144,6 +147,7 @@
   std::vector<Decl *> LocalTopLevelDecls;
   IncludeStructure Includes;
   CanonicalIncludes CanonIncludes;
+  std::unique_ptr<HeuristicResolver> Resolver;
 };
 
 } // namespace clangd
Index: clang-tools-extra/clangd/ParsedAST.cpp
===================================================================
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -542,6 +542,7 @@
       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);
 }
Index: clang-tools-extra/clangd/Hover.cpp
===================================================================
--- clang-tools-extra/clangd/Hover.cpp
+++ clang-tools-extra/clangd/Hover.cpp
@@ -887,7 +887,8 @@
     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.
Index: clang-tools-extra/clangd/HeuristicResolver.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/HeuristicResolver.h
@@ -0,0 +1,104 @@
+//===--- 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;
+
+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);
+};
+
+// 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) {}
+
+  // 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 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().
+  std::vector<const NamedDecl *>
+  resolveDependentMember(const Type *T, DeclarationName NameFactory,
+                         llvm::function_ref<bool(const NamedDecl *ND)> Filter);
+
+  // Try to heuristically resolve certain types of expressions to one
+  // or more declarations that the expression likely references.
+  std::vector<const NamedDecl *>
+  resolveMemberExpr(const CXXDependentScopeMemberExpr *ME);
+  std::vector<const NamedDecl *>
+  resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE);
+  std::vector<const NamedDecl *> resolveCallExpr(const CallExpr *CE);
+
+  // Try to heuristically resolve the type of a possibly-dependent nested name
+  // specifier.
+  const Type *resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS);
+
+private:
+  ASTContext &Ctx;
+
+  // Try to heuristically resolve the type of a possibly-dependent expression
+  // `E`.
+  const Type *resolveExprToType(const Expr *E);
+
+  // 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);
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
Index: clang-tools-extra/clangd/HeuristicResolver.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/HeuristicResolver.cpp
@@ -0,0 +1,189 @@
+//===--- 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 {
+
+// 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) {
+  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 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) {
+  return resolveDependentMember(RE->getQualifier()->getAsType(),
+                                RE->getDeclName(), StaticFilter);
+}
+
+std::vector<const NamedDecl *>
+HeuristicResolver::resolveCallExpr(const CallExpr *CE) {
+  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 {};
+}
+
+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) {
+  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();
+  /*if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
+    return resolveDeclsToType({DRE->getFoundDecl()});
+  return {};*/
+}
+
+const Type *HeuristicResolver::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(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) {
+  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
Index: clang-tools-extra/clangd/FindTarget.h
===================================================================
--- clang-tools-extra/clangd/FindTarget.h
+++ 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.
@@ -81,14 +83,15 @@
 ///
 /// FIXME: some AST nodes cannot be DynTypedNodes, these cannot be specified.
 llvm::SmallVector<const NamedDecl *, 1> targetDecl(const DynTypedNode &,
-                                                   DeclRelationSet Mask);
+                                                   DeclRelationSet Mask,
+                                                   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 &, HeuristicResolver *);
 
 enum class DeclRelation : unsigned {
   // Template options apply when the declaration is an instantiated template.
@@ -146,11 +149,14 @@
 /// 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,
+                            HeuristicResolver *Resolver);
 void findExplicitReferences(const Decl *D,
-                            llvm::function_ref<void(ReferenceLoc)> Out);
+                            llvm::function_ref<void(ReferenceLoc)> Out,
+                            HeuristicResolver *Resolver);
 void findExplicitReferences(const ASTContext &AST,
-                            llvm::function_ref<void(ReferenceLoc)> Out);
+                            llvm::function_ref<void(ReferenceLoc)> Out,
+                            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 @@
 ///    ^~~ 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,
+                         HeuristicResolver *Resolver);
 
 // Boring implementation details of bitfield.
 
Index: clang-tools-extra/clangd/FindTarget.cpp
===================================================================
--- clang-tools-extra/clangd/FindTarget.cpp
+++ 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 @@
   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 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.
-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 @@
   using Rel = DeclRelation;
 
 private:
+  HeuristicResolver *Resolver;
   llvm::SmallDenseMap<const NamedDecl *,
                       std::pair<RelSet, /*InsertionOrder*/ size_t>>
       Decls;
@@ -346,6 +144,8 @@
   }
 
 public:
+  TargetFinder(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,9 +185,8 @@
       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(); },
+      for (const NamedDecl *Target : Resolver->resolveDependentMember(
+               UUVD->getQualifier()->getAsType(), UUVD->getNameInfo().getName(),
                ValueFilter)) {
         add(Target, Flags); // no Underlying as this is a non-renaming alias
       }
@@ -486,12 +285,12 @@
       }
       void
       VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E) {
-        for (const NamedDecl *D : resolveExprToDecls(E)) {
+        for (const NamedDecl *D : Outer.Resolver->resolveMemberExpr(E)) {
           Outer.add(D, Flags);
         }
       }
       void VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *E) {
-        for (const NamedDecl *D : resolveExprToDecls(E)) {
+        for (const NamedDecl *D : Outer.Resolver->resolveDeclRefExpr(E)) {
           Outer.add(D, Flags);
         }
       }
@@ -571,19 +370,19 @@
           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)) {
+        for (const NamedDecl *ND : Outer.Resolver->resolveDependentMember(
+                 Outer.Resolver->resolveNestedNameSpecifierToType(
+                     DNT->getQualifier()),
+                 DNT->getIdentifier(), TypeFilter)) {
           Outer.add(ND, Flags);
         }
       }
       void VisitDependentTemplateSpecializationType(
           const DependentTemplateSpecializationType *DTST) {
-        for (const NamedDecl *ND : getMembersReferencedViaDependentName(
-                 resolveNestedNameSpecifierToType(DTST->getQualifier()),
-                 [DTST](ASTContext &) { return DTST->getIdentifier(); },
-                 TemplateFilter)) {
+        for (const NamedDecl *ND : Outer.Resolver->resolveDependentMember(
+                 Outer.Resolver->resolveNestedNameSpecifierToType(
+                     DTST->getQualifier()),
+                 DTST->getIdentifier(), TemplateFilter)) {
           Outer.add(ND, Flags);
         }
       }
@@ -649,9 +448,11 @@
       add(NNS->getAsNamespaceAlias(), Flags);
       return;
     case NestedNameSpecifier::Identifier:
+      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 +491,9 @@
 } // namespace
 
 llvm::SmallVector<std::pair<const NamedDecl *, DeclRelationSet>, 1>
-allTargetDecls(const DynTypedNode &N) {
+allTargetDecls(const DynTypedNode &N, 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);
@@ -714,10 +515,11 @@
   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,
+           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);
   }
@@ -725,11 +527,12 @@
 }
 
 llvm::SmallVector<const NamedDecl *, 1>
-explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask) {
+explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask,
+                         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.
@@ -756,8 +559,12 @@
 }
 
 namespace {
-llvm::SmallVector<ReferenceLoc> refInDecl(const Decl *D) {
+llvm::SmallVector<ReferenceLoc> refInDecl(const Decl *D,
+                                          HeuristicResolver *Resolver) {
   struct Visitor : ConstDeclVisitor<Visitor> {
+    Visitor(HeuristicResolver *Resolver) : Resolver(Resolver) {}
+
+    HeuristicResolver *Resolver;
     llvm::SmallVector<ReferenceLoc> Refs;
 
     void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) {
@@ -771,10 +578,10 @@
 
     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) {
@@ -820,13 +627,17 @@
     }
   };
 
-  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,
+                                          HeuristicResolver *Resolver) {
   struct Visitor : ConstStmtVisitor<Visitor> {
+    Visitor(HeuristicResolver *Resolver) : Resolver(Resolver) {}
+
+    HeuristicResolver *Resolver;
     // FIXME: handle more complicated cases: more ObjC, designated initializers.
     llvm::SmallVector<ReferenceLoc> Refs;
 
@@ -847,7 +658,7 @@
     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) {
@@ -863,10 +674,10 @@
 
     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) {
@@ -889,7 +700,7 @@
           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) {
@@ -921,13 +732,17 @@
     }
   };
 
-  Visitor V;
+  Visitor V{Resolver};
   V.Visit(S);
   return V.Refs;
 }
 
-llvm::SmallVector<ReferenceLoc> refInTypeLoc(TypeLoc L) {
+llvm::SmallVector<ReferenceLoc> refInTypeLoc(TypeLoc L,
+                                             HeuristicResolver *Resolver) {
   struct Visitor : TypeLocVisitor<Visitor> {
+    Visitor(HeuristicResolver *Resolver) : Resolver(Resolver) {}
+
+    HeuristicResolver *Resolver;
     llvm::Optional<ReferenceLoc> Ref;
 
     void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) {
@@ -966,14 +781,14 @@
       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) {
@@ -985,15 +800,16 @@
 
     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) {
@@ -1004,7 +820,7 @@
     }
   };
 
-  Visitor V;
+  Visitor V{Resolver};
   V.Visit(L.getUnqualifiedLoc());
   if (!V.Ref)
     return {};
@@ -1014,8 +830,9 @@
 class ExplicitReferenceCollector
     : public RecursiveASTVisitor<ExplicitReferenceCollector> {
 public:
-  ExplicitReferenceCollector(llvm::function_ref<void(ReferenceLoc)> Out)
-      : Out(Out) {
+  ExplicitReferenceCollector(llvm::function_ref<void(ReferenceLoc)> Out,
+                             HeuristicResolver *Resolver)
+      : Out(Out), Resolver(Resolver) {
     assert(Out);
   }
 
@@ -1122,19 +939,19 @@
   ///     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.
@@ -1167,6 +984,7 @@
   }
 
   llvm::function_ref<void(ReferenceLoc)> Out;
+  HeuristicResolver *Resolver;
   /// TypeLocs starting at these locations must be skipped, see
   /// TraverseElaboratedTypeSpecifierLoc for details.
   llvm::DenseSet<SourceLocation> TypeLocsToSkip;
@@ -1174,18 +992,22 @@
 } // namespace
 
 void findExplicitReferences(const Stmt *S,
-                            llvm::function_ref<void(ReferenceLoc)> Out) {
+                            llvm::function_ref<void(ReferenceLoc)> Out,
+                            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,
+                            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,
+                            HeuristicResolver *Resolver) {
+  ExplicitReferenceCollector(Out, Resolver)
+      .TraverseAST(const_cast<ASTContext &>(AST));
 }
 
 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, DeclRelation R) {
Index: clang-tools-extra/clangd/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/CMakeLists.txt
+++ clang-tools-extra/clangd/CMakeLists.txt
@@ -70,6 +70,7 @@
   GlobalCompilationDatabase.cpp
   Headers.cpp
   HeaderSourceSwitch.cpp
+  HeuristicResolver.cpp
   Hover.cpp
   IncludeFixer.cpp
   JSONTransport.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to