[clangd] Factor out the heuristic resolver code into its own class
authorNathan Ridge <zeratul976@hotmail.com>
Mon, 18 Jan 2021 07:58:43 +0000 (02:58 -0500)
committerNathan Ridge <zeratul976@hotmail.com>
Tue, 16 Feb 2021 09:10:52 +0000 (04:10 -0500)
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

17 files changed:
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/HeuristicResolver.cpp [new file with mode: 0644]
clang-tools-extra/clangd/HeuristicResolver.h [new file with mode: 0644]
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 b8cc7f0..58c57e1 100644 (file)
@@ -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
index 1d12e7e..3de5106 100644 (file)
@@ -71,6 +71,7 @@ add_clang_library(clangDaemon
   GlobalCompilationDatabase.cpp
   Headers.cpp
   HeaderSourceSwitch.cpp
+  HeuristicResolver.cpp
   Hover.cpp
   IncludeFixer.cpp
   JSONTransport.cpp
index 46c9868..2dabae4 100644 (file)
@@ -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 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 @@ struct TargetFinder {
   using Rel = DeclRelation;
 
 private:
+  const HeuristicResolver *Resolver;
   llvm::SmallDenseMap<const NamedDecl *,
                       std::pair<RelSet, /*InsertionOrder*/ size_t>>
       Decls;
@@ -346,6 +144,8 @@ private:
   }
 
 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 @@ public:
       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 @@ public:
       }
       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 @@ public:
           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 @@ public:
       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 @@ public:
 } // 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 @@ private:
   ///     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 @@ private:
   }
 
   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 @@ private:
 } // 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) {
index 92e4354..3e4cf8b 100644 (file)
@@ -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 (file)
index 0000000..27ff0c7
--- /dev/null
@@ -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 (file)
index 0000000..30f8eb1
--- /dev/null
@@ -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 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 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
index 42acebd..82c3ccb 100644 (file)
@@ -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.
index 1020282..24038f0 100644 (file)
@@ -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);
 }
index 05818b9..263a097 100644 (file)
@@ -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 @@ public:
   /// 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 @@ private:
   std::vector<Decl *> LocalTopLevelDecls;
   IncludeStructure Includes;
   CanonicalIncludes CanonIncludes;
+  std::unique_ptr<HeuristicResolver> Resolver;
 };
 
 } // namespace clangd
index 23a58b6..da8ee7e 100644 (file)
@@ -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);
index c4a73eb..ff3b8d7 100644 (file)
@@ -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 different 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
index 96cd79b..cc6830d 100644 (file)
@@ -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;
index f1c6f66..14d2d08 100644 (file)
@@ -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 @@ public:
       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 7a40ffa..4cdd36c 100644 (file)
@@ -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 @@ public:
       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 8eec428..8255ea5 100644 (file)
@@ -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);
index 344445d..d039443 100644 (file)
@@ -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);
index 2f69028..c9f035e 100644 (file)
@@ -86,7 +86,8 @@ protected:
     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 @@ protected:
 
     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";