mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-23 13:50:11 +00:00
[clangd] Factor out the heuristic resolver code into its own class
The patch also does some cleanup on the interface of the entry points from TargetFinder into the heuristic resolution code. Since the heuristic resolver is created in a place where the ASTContext is available, it can store the ASTContext and the NameFactory hack can be removed. Differential revision: https://reviews.llvm.org/D92290
This commit is contained in:
parent
54a14c264a
commit
9510b09402
@ -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
|
||||
|
@ -71,6 +71,7 @@ add_clang_library(clangDaemon
|
||||
GlobalCompilationDatabase.cpp
|
||||
Headers.cpp
|
||||
HeaderSourceSwitch.cpp
|
||||
HeuristicResolver.cpp
|
||||
Hover.cpp
|
||||
IncludeFixer.cpp
|
||||
JSONTransport.cpp
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "FindTarget.h"
|
||||
#include "AST.h"
|
||||
#include "HeuristicResolver.h"
|
||||
#include "support/Logger.h"
|
||||
#include "clang/AST/ASTTypeTraits.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
@ -56,210 +57,6 @@ LLVM_ATTRIBUTE_UNUSED std::string nodeToString(const DynTypedNode &N) {
|
||||
return S;
|
||||
}
|
||||
|
||||
// Helper function for getMembersReferencedViaDependentName()
|
||||
// which takes a possibly-dependent type `T` and heuristically
|
||||
// resolves it to a CXXRecordDecl in which we can try name lookup.
|
||||
CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) {
|
||||
assert(T);
|
||||
|
||||
if (const auto *RT = T->getAs<RecordType>())
|
||||
return dyn_cast<CXXRecordDecl>(RT->getDecl());
|
||||
|
||||
if (const auto *ICNT = T->getAs<InjectedClassNameType>())
|
||||
T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
|
||||
if (!T)
|
||||
return nullptr;
|
||||
|
||||
const auto *TST = T->getAs<TemplateSpecializationType>();
|
||||
if (!TST)
|
||||
return nullptr;
|
||||
|
||||
const ClassTemplateDecl *TD = dyn_cast_or_null<ClassTemplateDecl>(
|
||||
TST->getTemplateName().getAsTemplateDecl());
|
||||
if (!TD)
|
||||
return nullptr;
|
||||
|
||||
return TD->getTemplatedDecl();
|
||||
}
|
||||
|
||||
// Given a tag-decl type and a member name, heuristically resolve the
|
||||
// name to one or more declarations.
|
||||
// The current heuristic is simply to look up the name in the primary
|
||||
// template. This is a heuristic because the template could potentially
|
||||
// have specializations that declare 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) {
|
||||
|
@ -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.
|
||||
|
||||
|
225
clang-tools-extra/clangd/HeuristicResolver.cpp
Normal file
225
clang-tools-extra/clangd/HeuristicResolver.cpp
Normal file
@ -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
|
99
clang-tools-extra/clangd/HeuristicResolver.h
Normal file
99
clang-tools-extra/clangd/HeuristicResolver.h
Normal file
@ -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
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
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);
|
||||
}
|
||||
});
|
||||
// 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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
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));
|
||||
});
|
||||
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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
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);
|
||||
});
|
||||
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);
|
||||
|
@ -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";
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user