[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:
Alex Lorenz 2017-05-10 09:47:41 +00:00
parent 836b0f48c1
commit 4e1377aff0
6 changed files with 312 additions and 21 deletions

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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())

View File

@ -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;

View File

@ -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]]
}