forked from OSchip/llvm-project
[index] Index simple dependent declaration references
This commit implements basic support for indexing of dependent declaration references. Now the indexer tries to find a suitable match in the base template for a dependent member ref/decl ref/dependent type. rdar://29158210 Differential Revision: https://reviews.llvm.org/D32972 llvm-svn: 302632
This commit is contained in:
parent
836b0f48c1
commit
4e1377aff0
|
@ -161,7 +161,8 @@ class CXXBasePaths {
|
|||
void ComputeDeclsFound();
|
||||
|
||||
bool lookupInBases(ASTContext &Context, const CXXRecordDecl *Record,
|
||||
CXXRecordDecl::BaseMatchesCallback BaseMatches);
|
||||
CXXRecordDecl::BaseMatchesCallback BaseMatches,
|
||||
bool LookupInDependent = false);
|
||||
|
||||
public:
|
||||
typedef std::list<CXXBasePath>::iterator paths_iterator;
|
||||
|
|
|
@ -1563,10 +1563,13 @@ public:
|
|||
/// \param Paths used to record the paths from this class to its base class
|
||||
/// subobjects that match the search criteria.
|
||||
///
|
||||
/// \param LookupInDependent can be set to true to extend the search to
|
||||
/// dependent base classes.
|
||||
///
|
||||
/// \returns true if there exists any path from this class to a base class
|
||||
/// subobject that matches the search criteria.
|
||||
bool lookupInBases(BaseMatchesCallback BaseMatches,
|
||||
CXXBasePaths &Paths) const;
|
||||
bool lookupInBases(BaseMatchesCallback BaseMatches, CXXBasePaths &Paths,
|
||||
bool LookupInDependent = false) const;
|
||||
|
||||
/// \brief Base-class lookup callback that determines whether the given
|
||||
/// base class specifier refers to a specific class declaration.
|
||||
|
@ -1607,6 +1610,16 @@ public:
|
|||
static bool FindOrdinaryMember(const CXXBaseSpecifier *Specifier,
|
||||
CXXBasePath &Path, DeclarationName Name);
|
||||
|
||||
/// \brief Base-class lookup callback that determines whether there exists
|
||||
/// a member with the given name.
|
||||
///
|
||||
/// This callback can be used with \c lookupInBases() to find members
|
||||
/// of the given name within a C++ class hierarchy, including dependent
|
||||
/// classes.
|
||||
static bool
|
||||
FindOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier,
|
||||
CXXBasePath &Path, DeclarationName Name);
|
||||
|
||||
/// \brief Base-class lookup callback that determines whether there exists
|
||||
/// an OpenMP declare reduction member with the given name.
|
||||
///
|
||||
|
@ -1633,6 +1646,14 @@ public:
|
|||
/// \brief Get the indirect primary bases for this class.
|
||||
void getIndirectPrimaryBases(CXXIndirectPrimaryBaseSet& Bases) const;
|
||||
|
||||
/// Performs an imprecise lookup of a dependent name in this class.
|
||||
///
|
||||
/// This function does not follow strict semantic rules and should be used
|
||||
/// only when lookup rules can be relaxed, e.g. indexing.
|
||||
std::vector<const NamedDecl *>
|
||||
lookupDependentName(const DeclarationName &Name,
|
||||
llvm::function_ref<bool(const NamedDecl *ND)> Filter);
|
||||
|
||||
/// Renders and displays an inheritance diagram
|
||||
/// for this C++ class and all of its base classes (transitively) using
|
||||
/// GraphViz.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "clang/AST/CXXInheritance.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/RecordLayout.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include <algorithm>
|
||||
|
@ -174,9 +175,10 @@ bool CXXRecordDecl::forallBases(ForallBasesCallback BaseMatches,
|
|||
return AllMatches;
|
||||
}
|
||||
|
||||
bool CXXBasePaths::lookupInBases(
|
||||
ASTContext &Context, const CXXRecordDecl *Record,
|
||||
CXXRecordDecl::BaseMatchesCallback BaseMatches) {
|
||||
bool CXXBasePaths::lookupInBases(ASTContext &Context,
|
||||
const CXXRecordDecl *Record,
|
||||
CXXRecordDecl::BaseMatchesCallback BaseMatches,
|
||||
bool LookupInDependent) {
|
||||
bool FoundPath = false;
|
||||
|
||||
// The access of the path down to this record.
|
||||
|
@ -194,7 +196,7 @@ bool CXXBasePaths::lookupInBases(
|
|||
// the base class scope is not examined during unqualified name lookup
|
||||
// either at the point of definition of the class template or member or
|
||||
// during an instantiation of the class tem- plate or member.
|
||||
if (BaseType->isDependentType())
|
||||
if (!LookupInDependent && BaseType->isDependentType())
|
||||
continue;
|
||||
|
||||
// Determine whether we need to visit this base class at all,
|
||||
|
@ -262,10 +264,26 @@ bool CXXBasePaths::lookupInBases(
|
|||
return FoundPath;
|
||||
}
|
||||
} else if (VisitBase) {
|
||||
CXXRecordDecl *BaseRecord
|
||||
= cast<CXXRecordDecl>(BaseSpec.getType()->castAs<RecordType>()
|
||||
->getDecl());
|
||||
if (lookupInBases(Context, BaseRecord, BaseMatches)) {
|
||||
CXXRecordDecl *BaseRecord;
|
||||
if (LookupInDependent) {
|
||||
BaseRecord = nullptr;
|
||||
const TemplateSpecializationType *TST =
|
||||
BaseSpec.getType()->getAs<TemplateSpecializationType>();
|
||||
if (!TST) {
|
||||
if (auto *RT = BaseSpec.getType()->getAs<RecordType>())
|
||||
BaseRecord = cast<CXXRecordDecl>(RT->getDecl());
|
||||
} else {
|
||||
TemplateName TN = TST->getTemplateName();
|
||||
if (auto *TD =
|
||||
dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl()))
|
||||
BaseRecord = TD->getTemplatedDecl();
|
||||
}
|
||||
} else {
|
||||
BaseRecord = cast<CXXRecordDecl>(
|
||||
BaseSpec.getType()->castAs<RecordType>()->getDecl());
|
||||
}
|
||||
if (BaseRecord &&
|
||||
lookupInBases(Context, BaseRecord, BaseMatches, LookupInDependent)) {
|
||||
// C++ [class.member.lookup]p2:
|
||||
// A member name f in one sub-object B hides a member name f in
|
||||
// a sub-object A if A is a base class sub-object of B. Any
|
||||
|
@ -299,9 +317,11 @@ bool CXXBasePaths::lookupInBases(
|
|||
}
|
||||
|
||||
bool CXXRecordDecl::lookupInBases(BaseMatchesCallback BaseMatches,
|
||||
CXXBasePaths &Paths) const {
|
||||
CXXBasePaths &Paths,
|
||||
bool LookupInDependent) const {
|
||||
// If we didn't find anything, report that.
|
||||
if (!Paths.lookupInBases(getASTContext(), this, BaseMatches))
|
||||
if (!Paths.lookupInBases(getASTContext(), this, BaseMatches,
|
||||
LookupInDependent))
|
||||
return false;
|
||||
|
||||
// If we're not recording paths or we won't ever find ambiguities,
|
||||
|
@ -387,23 +407,49 @@ bool CXXRecordDecl::FindTagMember(const CXXBaseSpecifier *Specifier,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool CXXRecordDecl::FindOrdinaryMember(const CXXBaseSpecifier *Specifier,
|
||||
CXXBasePath &Path,
|
||||
DeclarationName Name) {
|
||||
RecordDecl *BaseRecord =
|
||||
Specifier->getType()->castAs<RecordType>()->getDecl();
|
||||
|
||||
const unsigned IDNS = IDNS_Ordinary | IDNS_Tag | IDNS_Member;
|
||||
static bool findOrdinaryMember(RecordDecl *BaseRecord, CXXBasePath &Path,
|
||||
DeclarationName Name) {
|
||||
const unsigned IDNS = clang::Decl::IDNS_Ordinary | clang::Decl::IDNS_Tag |
|
||||
clang::Decl::IDNS_Member;
|
||||
for (Path.Decls = BaseRecord->lookup(Name);
|
||||
!Path.Decls.empty();
|
||||
Path.Decls = Path.Decls.slice(1)) {
|
||||
if (Path.Decls.front()->isInIdentifierNamespace(IDNS))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CXXRecordDecl::FindOrdinaryMember(const CXXBaseSpecifier *Specifier,
|
||||
CXXBasePath &Path,
|
||||
DeclarationName Name) {
|
||||
RecordDecl *BaseRecord =
|
||||
Specifier->getType()->castAs<RecordType>()->getDecl();
|
||||
return findOrdinaryMember(BaseRecord, Path, Name);
|
||||
}
|
||||
|
||||
bool CXXRecordDecl::FindOrdinaryMemberInDependentClasses(
|
||||
const CXXBaseSpecifier *Specifier, CXXBasePath &Path,
|
||||
DeclarationName Name) {
|
||||
const TemplateSpecializationType *TST =
|
||||
Specifier->getType()->getAs<TemplateSpecializationType>();
|
||||
if (!TST) {
|
||||
auto *RT = Specifier->getType()->getAs<RecordType>();
|
||||
if (!RT)
|
||||
return false;
|
||||
return findOrdinaryMember(RT->getDecl(), Path, Name);
|
||||
}
|
||||
TemplateName TN = TST->getTemplateName();
|
||||
const auto *TD = dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl());
|
||||
if (!TD)
|
||||
return false;
|
||||
CXXRecordDecl *RD = TD->getTemplatedDecl();
|
||||
if (!RD)
|
||||
return false;
|
||||
return findOrdinaryMember(RD, Path, Name);
|
||||
}
|
||||
|
||||
bool CXXRecordDecl::FindOMPReductionMember(const CXXBaseSpecifier *Specifier,
|
||||
CXXBasePath &Path,
|
||||
DeclarationName Name) {
|
||||
|
@ -438,6 +484,36 @@ FindNestedNameSpecifierMember(const CXXBaseSpecifier *Specifier,
|
|||
return false;
|
||||
}
|
||||
|
||||
std::vector<const NamedDecl *> CXXRecordDecl::lookupDependentName(
|
||||
const DeclarationName &Name,
|
||||
llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
|
||||
std::vector<const NamedDecl *> Results;
|
||||
// Lookup in the class.
|
||||
DeclContext::lookup_result DirectResult = lookup(Name);
|
||||
if (!DirectResult.empty()) {
|
||||
for (const NamedDecl *ND : DirectResult) {
|
||||
if (Filter(ND))
|
||||
Results.push_back(ND);
|
||||
}
|
||||
return Results;
|
||||
}
|
||||
// Perform lookup into our base classes.
|
||||
CXXBasePaths Paths;
|
||||
Paths.setOrigin(this);
|
||||
if (!lookupInBases(
|
||||
[&](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
|
||||
return CXXRecordDecl::FindOrdinaryMemberInDependentClasses(
|
||||
Specifier, Path, Name);
|
||||
},
|
||||
Paths, /*LookupInDependent=*/true))
|
||||
return Results;
|
||||
for (const NamedDecl *ND : Paths.front().Decls) {
|
||||
if (Filter(ND))
|
||||
Results.push_back(ND);
|
||||
}
|
||||
return Results;
|
||||
}
|
||||
|
||||
void OverridingMethods::add(unsigned OverriddenSubobject,
|
||||
UniqueVirtualMethod Overriding) {
|
||||
SmallVectorImpl<UniqueVirtualMethod> &SubobjectOverrides
|
||||
|
|
|
@ -150,6 +150,50 @@ public:
|
|||
Parent, ParentDC, Roles, Relations, E);
|
||||
}
|
||||
|
||||
bool indexDependentReference(
|
||||
const Expr *E, const Type *T, const DeclarationNameInfo &NameInfo,
|
||||
llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
|
||||
if (!T)
|
||||
return true;
|
||||
const TemplateSpecializationType *TST =
|
||||
T->getAs<TemplateSpecializationType>();
|
||||
if (!TST)
|
||||
return true;
|
||||
TemplateName TN = TST->getTemplateName();
|
||||
const ClassTemplateDecl *TD =
|
||||
dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl());
|
||||
if (!TD)
|
||||
return true;
|
||||
CXXRecordDecl *RD = TD->getTemplatedDecl();
|
||||
std::vector<const NamedDecl *> Symbols =
|
||||
RD->lookupDependentName(NameInfo.getName(), Filter);
|
||||
// FIXME: Improve overload handling.
|
||||
if (Symbols.size() != 1)
|
||||
return true;
|
||||
SourceLocation Loc = NameInfo.getLoc();
|
||||
if (Loc.isInvalid())
|
||||
Loc = E->getLocStart();
|
||||
SmallVector<SymbolRelation, 4> Relations;
|
||||
SymbolRoleSet Roles = getRolesForRef(E, Relations);
|
||||
return IndexCtx.handleReference(Symbols[0], Loc, Parent, ParentDC, Roles,
|
||||
Relations, E);
|
||||
}
|
||||
|
||||
bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
|
||||
const DeclarationNameInfo &Info = E->getMemberNameInfo();
|
||||
return indexDependentReference(
|
||||
E, E->getBaseType().getTypePtrOrNull(), Info,
|
||||
[](const NamedDecl *D) { return D->isCXXInstanceMember(); });
|
||||
}
|
||||
|
||||
bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) {
|
||||
const DeclarationNameInfo &Info = E->getNameInfo();
|
||||
const NestedNameSpecifier *NNS = E->getQualifier();
|
||||
return indexDependentReference(
|
||||
E, NNS->getAsType(), Info,
|
||||
[](const NamedDecl *D) { return !D->isCXXInstanceMember(); });
|
||||
}
|
||||
|
||||
bool VisitDesignatedInitExpr(DesignatedInitExpr *E) {
|
||||
for (DesignatedInitExpr::Designator &D : llvm::reverse(E->designators())) {
|
||||
if (D.isFieldDesignator() && D.getField())
|
||||
|
|
|
@ -141,6 +141,31 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool VisitDependentNameTypeLoc(DependentNameTypeLoc TL) {
|
||||
const DependentNameType *DNT = TL.getTypePtr();
|
||||
const NestedNameSpecifier *NNS = DNT->getQualifier();
|
||||
const Type *T = NNS->getAsType();
|
||||
if (!T)
|
||||
return true;
|
||||
const TemplateSpecializationType *TST =
|
||||
T->getAs<TemplateSpecializationType>();
|
||||
if (!TST)
|
||||
return true;
|
||||
TemplateName TN = TST->getTemplateName();
|
||||
const ClassTemplateDecl *TD =
|
||||
dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl());
|
||||
if (!TD)
|
||||
return true;
|
||||
CXXRecordDecl *RD = TD->getTemplatedDecl();
|
||||
DeclarationName Name(DNT->getIdentifier());
|
||||
std::vector<const NamedDecl *> Symbols = RD->lookupDependentName(
|
||||
Name, [](const NamedDecl *ND) { return isa<TypeDecl>(ND); });
|
||||
if (Symbols.size() != 1)
|
||||
return true;
|
||||
return IndexCtx.handleReference(Symbols[0], TL.getNameLoc(), Parent,
|
||||
ParentDC, SymbolRoleSet(), Relations);
|
||||
}
|
||||
|
||||
bool TraverseStmt(Stmt *S) {
|
||||
IndexCtx.indexBody(S, Parent, ParentDC);
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
// RUN: c-index-test core -print-source-symbols -- %s -std=c++14 -target x86_64-apple-macosx10.7 | FileCheck %s
|
||||
|
||||
int invalid;
|
||||
|
||||
class Base {
|
||||
void baseFunction();
|
||||
|
||||
int baseField;
|
||||
|
||||
static void staticBaseFunction();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class BaseTemplate {
|
||||
public:
|
||||
T baseTemplateFunction();
|
||||
|
||||
T baseTemplateField;
|
||||
|
||||
static T baseTemplateVariable;
|
||||
};
|
||||
|
||||
template<typename T, typename S>
|
||||
class TemplateClass: public Base , public BaseTemplate<T> {
|
||||
public:
|
||||
~TemplateClass();
|
||||
|
||||
T function() { }
|
||||
|
||||
static void staticFunction() { }
|
||||
|
||||
T field;
|
||||
|
||||
static T variable;
|
||||
|
||||
struct Struct { };
|
||||
|
||||
enum Enum { EnumValue };
|
||||
|
||||
using TypeAlias = S;
|
||||
typedef T Typedef;
|
||||
|
||||
void overload1(const T &);
|
||||
void overload1(const S &);
|
||||
};
|
||||
|
||||
template<typename T, typename S>
|
||||
void indexSimpleDependentDeclarations(const TemplateClass<T, S> &object) {
|
||||
// Valid instance members:
|
||||
object.function();
|
||||
// CHECK: [[@LINE-1]]:10 | instance-method/C++ | function | c:@ST>2#T#T@TemplateClass@F@function# | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1
|
||||
object.field;
|
||||
// CHECK: [[@LINE-1]]:10 | field/C++ | field | c:@ST>2#T#T@TemplateClass@FI@field | <no-cgname> | Ref,RelCont | rel: 1
|
||||
object.baseFunction();
|
||||
// CHECK: [[@LINE-1]]:10 | instance-method/C++ | baseFunction | c:@S@Base@F@baseFunction# | __ZN4Base12baseFunctionEv | Ref,Call,RelCall,RelCont | rel: 1
|
||||
object.baseField;
|
||||
// CHECK: [[@LINE-1]]:10 | field/C++ | baseField | c:@S@Base@FI@baseField | <no-cgname> | Ref,RelCont | rel: 1
|
||||
object.baseTemplateFunction();
|
||||
// CHECK: [[@LINE-1]]:10 | instance-method/C++ | baseTemplateFunction | c:@ST>1#T@BaseTemplate@F@baseTemplateFunction# | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1
|
||||
object.baseTemplateField;
|
||||
// CHECK: [[@LINE-1]]:10 | field/C++ | baseTemplateField | c:@ST>1#T@BaseTemplate@FI@baseTemplateField | <no-cgname> | Ref,RelCont | rel: 1
|
||||
|
||||
// Invalid instance members:
|
||||
object.variable;
|
||||
// CHECK-NOT: [[@LINE-1]]:10
|
||||
object.staticFunction();
|
||||
// CHECK-NOT: [[@LINE-1]]:10
|
||||
object.Struct;
|
||||
// CHECK-NOT: [[@LINE-1]]:10
|
||||
object.EnumValue;
|
||||
// CHECK-NOT: [[@LINE-1]]:10
|
||||
|
||||
// Valid static members:
|
||||
TemplateClass<T, S>::staticFunction();
|
||||
// CHECK: [[@LINE-1]]:24 | static-method/C++ | staticFunction | c:@ST>2#T#T@TemplateClass@F@staticFunction#S | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1
|
||||
TemplateClass<T, S>::variable;
|
||||
// CHECK: [[@LINE-1]]:24 | static-property/C++ | variable | c:@ST>2#T#T@TemplateClass@variable | __ZN13TemplateClass8variableE | Ref,RelCont | rel: 1
|
||||
TemplateClass<T, S>::staticBaseFunction();
|
||||
// CHECK: [[@LINE-1]]:24 | static-method/C++ | staticBaseFunction | c:@S@Base@F@staticBaseFunction#S | __ZN4Base18staticBaseFunctionEv | Ref,Call,RelCall,RelCont | rel: 1
|
||||
TemplateClass<T, S>::baseTemplateVariable;
|
||||
// CHECK: [[@LINE-1]]:24 | static-property/C++ | baseTemplateVariable | c:@ST>1#T@BaseTemplate@baseTemplateVariable | __ZN12BaseTemplate20baseTemplateVariableE | Ref,RelCont | rel: 1
|
||||
TemplateClass<T, S>::EnumValue;
|
||||
// CHECK: [[@LINE-1]]:24 | enumerator/C | EnumValue | c:@ST>2#T#T@TemplateClass@E@Enum@EnumValue | <no-cgname> | Ref,RelCont | rel: 1
|
||||
TemplateClass<T, S>::Struct();
|
||||
// CHECK: [[@LINE-1]]:24 | struct/C | Struct | c:@ST>2#T#T@TemplateClass@S@Struct | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1
|
||||
|
||||
// Invalid static members:
|
||||
TemplateClass<T, S>::field;
|
||||
// CHECK-NOT: [[@LINE-1]]:24
|
||||
TemplateClass<T, S>::function();
|
||||
// CHECK-NOT: [[@LINE-1]]:24
|
||||
|
||||
// Valid type names:
|
||||
typename TemplateClass<T, S>::Struct Val;
|
||||
// CHECK: [[@LINE-1]]:33 | struct/C | Struct | c:@ST>2#T#T@TemplateClass@S@Struct | <no-cgname> | Ref,RelCont | rel: 1
|
||||
typename TemplateClass<T, S>::Enum EnumVal;
|
||||
// CHECK: [[@LINE-1]]:33 | enum/C | Enum | c:@ST>2#T#T@TemplateClass@E@Enum | <no-cgname> | Ref,RelCont | rel: 1
|
||||
typename TemplateClass<T, S>::TypeAlias Val2;
|
||||
// CHECK: [[@LINE-1]]:33 | type-alias/C++ | TypeAlias | c:@ST>2#T#T@TemplateClass@TypeAlias | <no-cgname> | Ref,RelCont | rel: 1
|
||||
typename TemplateClass<T, S>::Typedef Val3;
|
||||
// CHECK: [[@LINE-1]]:33 | type-alias/C | Typedef | c:{{.*}}index-dependent-source.cpp@ST>2#T#T@TemplateClass@T@Typedef | <no-cgname> | Ref,RelCont | rel: 1
|
||||
|
||||
// Invalid type names:
|
||||
typename TemplateClass<T, S>::field Val4;
|
||||
// CHECK-NOT: [[@LINE-1]]:33
|
||||
typename TemplateClass<T, S>::staticFunction Val5;
|
||||
// CHECK-NOT: [[@LINE-1]]:33
|
||||
|
||||
|
||||
object.invalid;
|
||||
// CHECK-NOT: [[@LINE-1]]:10
|
||||
TemplateClass<T, S>::invalid;
|
||||
// CHECK-NOT: [[@LINE-1]]:24
|
||||
}
|
||||
|
||||
template<typename T, typename S, typename Y>
|
||||
void indexDependentOverloads(const TemplateClass<T, S> &object) {
|
||||
object.overload1(T());
|
||||
// CHECK-NOT: [[@LINE-1]]
|
||||
object.overload1(S());
|
||||
// CHECK-NOT: [[@LINE-1]]
|
||||
object.overload1(Y());
|
||||
// CHECK-NOT: [[@LINE-1]]
|
||||
}
|
Loading…
Reference in New Issue