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
GlobalCompilationDatabase.cpp
Headers.cpp
HeaderSourceSwitch.cpp
+ HeuristicResolver.cpp
Hover.cpp
IncludeFixer.cpp
JSONTransport.cpp
#include "FindTarget.h"
#include "AST.h"
+#include "HeuristicResolver.h"
#include "support/Logger.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Decl.h"
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())
using Rel = DeclRelation;
private:
+ const HeuristicResolver *Resolver;
llvm::SmallDenseMap<const NamedDecl *,
std::pair<RelSet, /*InsertionOrder*/ size_t>>
Decls;
}
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;
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)) {
}
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) {
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) {
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!
} // 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);
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);
}
}
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.
}
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) {
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) {
}
};
- 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;
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) {
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) {
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) {
}
};
- 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) {
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) {
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) {
}
};
- Visitor V;
+ Visitor V{Resolver};
V.Visit(L.getUnqualifiedLoc());
if (!V.Ref)
return {};
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);
}
/// 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.
}
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;
} // 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) {
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.
/// 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.
/// 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
/// ^~~ 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.
--- /dev/null
+//===--- 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
--- /dev/null
+//===--- 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
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.
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);
}
#include "Compiler.h"
#include "Diagnostics.h"
#include "Headers.h"
+#include "HeuristicResolver.h"
#include "Preamble.h"
#include "index/CanonicalIncludes.h"
#include "support/Path.h"
/// 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,
std::vector<Decl *> LocalTopLevelDecls;
IncludeStructure Includes;
CanonicalIncludes CanonIncludes;
+ std::unique_ptr<HeuristicResolver> Resolver;
};
} // namespace clangd
// 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);
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();
}
auto Decls = targetDecl(DynTypedNode::create(Type.getNonReferenceType()),
- DeclRelation::TemplatePattern | DeclRelation::Alias);
+ DeclRelation::TemplatePattern | DeclRelation::Alias,
+ AST.getHeuristicResolver());
if (Decls.empty())
return {};
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.
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;
// 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;
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
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;
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;
// 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
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(
/// 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,
if (It == ParamToNewName.end())
return;
RefLocs[Target].push_back(Ref.NameLoc);
- });
+ },
+ Resolver);
// Now try to generate edits for all the refs.
tooling::Replacements Replacements;
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();
// 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());
// 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.
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();
//
// 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())
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;
}
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);
// 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);
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;
}
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";