Index: cfe/trunk/include/clang/AST/CXXInheritance.h =================================================================== --- cfe/trunk/include/clang/AST/CXXInheritance.h +++ cfe/trunk/include/clang/AST/CXXInheritance.h @@ -161,7 +161,8 @@ void ComputeDeclsFound(); bool lookupInBases(ASTContext &Context, const CXXRecordDecl *Record, - CXXRecordDecl::BaseMatchesCallback BaseMatches); + CXXRecordDecl::BaseMatchesCallback BaseMatches, + bool LookupInDependent = false); public: typedef std::list::iterator paths_iterator; Index: cfe/trunk/include/clang/AST/DeclCXX.h =================================================================== --- cfe/trunk/include/clang/AST/DeclCXX.h +++ cfe/trunk/include/clang/AST/DeclCXX.h @@ -1563,10 +1563,13 @@ /// \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. @@ -1608,6 +1611,16 @@ 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. /// /// This callback can be used with \c lookupInBases() to find members @@ -1633,6 +1646,14 @@ /// \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 + lookupDependentName(const DeclarationName &Name, + llvm::function_ref Filter); + /// Renders and displays an inheritance diagram /// for this C++ class and all of its base classes (transitively) using /// GraphViz. Index: cfe/trunk/lib/AST/CXXInheritance.cpp =================================================================== --- cfe/trunk/lib/AST/CXXInheritance.cpp +++ cfe/trunk/lib/AST/CXXInheritance.cpp @@ -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 @@ -174,9 +175,10 @@ 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 @@ // 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 @@ return FoundPath; } } else if (VisitBase) { - CXXRecordDecl *BaseRecord - = cast(BaseSpec.getType()->castAs() - ->getDecl()); - if (lookupInBases(Context, BaseRecord, BaseMatches)) { + CXXRecordDecl *BaseRecord; + if (LookupInDependent) { + BaseRecord = nullptr; + const TemplateSpecializationType *TST = + BaseSpec.getType()->getAs(); + if (!TST) { + if (auto *RT = BaseSpec.getType()->getAs()) + BaseRecord = cast(RT->getDecl()); + } else { + TemplateName TN = TST->getTemplateName(); + if (auto *TD = + dyn_cast_or_null(TN.getAsTemplateDecl())) + BaseRecord = TD->getTemplatedDecl(); + } + } else { + BaseRecord = cast( + BaseSpec.getType()->castAs()->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 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 @@ return false; } -bool CXXRecordDecl::FindOrdinaryMember(const CXXBaseSpecifier *Specifier, - CXXBasePath &Path, - DeclarationName Name) { - RecordDecl *BaseRecord = - Specifier->getType()->castAs()->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()->getDecl(); + return findOrdinaryMember(BaseRecord, Path, Name); +} + +bool CXXRecordDecl::FindOrdinaryMemberInDependentClasses( + const CXXBaseSpecifier *Specifier, CXXBasePath &Path, + DeclarationName Name) { + const TemplateSpecializationType *TST = + Specifier->getType()->getAs(); + if (!TST) { + auto *RT = Specifier->getType()->getAs(); + if (!RT) + return false; + return findOrdinaryMember(RT->getDecl(), Path, Name); + } + TemplateName TN = TST->getTemplateName(); + const auto *TD = dyn_cast_or_null(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 @@ return false; } +std::vector CXXRecordDecl::lookupDependentName( + const DeclarationName &Name, + llvm::function_ref Filter) { + std::vector 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 &SubobjectOverrides Index: cfe/trunk/lib/Index/IndexBody.cpp =================================================================== --- cfe/trunk/lib/Index/IndexBody.cpp +++ cfe/trunk/lib/Index/IndexBody.cpp @@ -150,6 +150,50 @@ Parent, ParentDC, Roles, Relations, E); } + bool indexDependentReference( + const Expr *E, const Type *T, const DeclarationNameInfo &NameInfo, + llvm::function_ref Filter) { + if (!T) + return true; + const TemplateSpecializationType *TST = + T->getAs(); + if (!TST) + return true; + TemplateName TN = TST->getTemplateName(); + const ClassTemplateDecl *TD = + dyn_cast_or_null(TN.getAsTemplateDecl()); + if (!TD) + return true; + CXXRecordDecl *RD = TD->getTemplatedDecl(); + std::vector 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 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()) Index: cfe/trunk/lib/Index/IndexTypeSourceInfo.cpp =================================================================== --- cfe/trunk/lib/Index/IndexTypeSourceInfo.cpp +++ cfe/trunk/lib/Index/IndexTypeSourceInfo.cpp @@ -141,6 +141,31 @@ 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(); + if (!TST) + return true; + TemplateName TN = TST->getTemplateName(); + const ClassTemplateDecl *TD = + dyn_cast_or_null(TN.getAsTemplateDecl()); + if (!TD) + return true; + CXXRecordDecl *RD = TD->getTemplatedDecl(); + DeclarationName Name(DNT->getIdentifier()); + std::vector Symbols = RD->lookupDependentName( + Name, [](const NamedDecl *ND) { return isa(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; Index: cfe/trunk/test/Index/Core/index-dependent-source.cpp =================================================================== --- cfe/trunk/test/Index/Core/index-dependent-source.cpp +++ cfe/trunk/test/Index/Core/index-dependent-source.cpp @@ -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 +class BaseTemplate { +public: + T baseTemplateFunction(); + + T baseTemplateField; + + static T baseTemplateVariable; +}; + +template +class TemplateClass: public Base , public BaseTemplate { +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 +void indexSimpleDependentDeclarations(const TemplateClass &object) { + // Valid instance members: + object.function(); +// CHECK: [[@LINE-1]]:10 | instance-method/C++ | function | c:@ST>2#T#T@TemplateClass@F@function# | | Ref,Call,RelCall,RelCont | rel: 1 + object.field; +// CHECK: [[@LINE-1]]:10 | field/C++ | field | c:@ST>2#T#T@TemplateClass@FI@field | | 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 | | Ref,RelCont | rel: 1 + object.baseTemplateFunction(); +// CHECK: [[@LINE-1]]:10 | instance-method/C++ | baseTemplateFunction | c:@ST>1#T@BaseTemplate@F@baseTemplateFunction# | | Ref,Call,RelCall,RelCont | rel: 1 + object.baseTemplateField; +// CHECK: [[@LINE-1]]:10 | field/C++ | baseTemplateField | c:@ST>1#T@BaseTemplate@FI@baseTemplateField | | 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::staticFunction(); +// CHECK: [[@LINE-1]]:24 | static-method/C++ | staticFunction | c:@ST>2#T#T@TemplateClass@F@staticFunction#S | | Ref,Call,RelCall,RelCont | rel: 1 + TemplateClass::variable; +// CHECK: [[@LINE-1]]:24 | static-property/C++ | variable | c:@ST>2#T#T@TemplateClass@variable | __ZN13TemplateClass8variableE | Ref,RelCont | rel: 1 + TemplateClass::staticBaseFunction(); +// CHECK: [[@LINE-1]]:24 | static-method/C++ | staticBaseFunction | c:@S@Base@F@staticBaseFunction#S | __ZN4Base18staticBaseFunctionEv | Ref,Call,RelCall,RelCont | rel: 1 + TemplateClass::baseTemplateVariable; +// CHECK: [[@LINE-1]]:24 | static-property/C++ | baseTemplateVariable | c:@ST>1#T@BaseTemplate@baseTemplateVariable | __ZN12BaseTemplate20baseTemplateVariableE | Ref,RelCont | rel: 1 + TemplateClass::EnumValue; +// CHECK: [[@LINE-1]]:24 | enumerator/C | EnumValue | c:@ST>2#T#T@TemplateClass@E@Enum@EnumValue | | Ref,RelCont | rel: 1 + TemplateClass::Struct(); +// CHECK: [[@LINE-1]]:24 | struct/C | Struct | c:@ST>2#T#T@TemplateClass@S@Struct | | Ref,Call,RelCall,RelCont | rel: 1 + + // Invalid static members: + TemplateClass::field; +// CHECK-NOT: [[@LINE-1]]:24 + TemplateClass::function(); +// CHECK-NOT: [[@LINE-1]]:24 + + // Valid type names: + typename TemplateClass::Struct Val; +// CHECK: [[@LINE-1]]:33 | struct/C | Struct | c:@ST>2#T#T@TemplateClass@S@Struct | | Ref,RelCont | rel: 1 + typename TemplateClass::Enum EnumVal; +// CHECK: [[@LINE-1]]:33 | enum/C | Enum | c:@ST>2#T#T@TemplateClass@E@Enum | | Ref,RelCont | rel: 1 + typename TemplateClass::TypeAlias Val2; +// CHECK: [[@LINE-1]]:33 | type-alias/C++ | TypeAlias | c:@ST>2#T#T@TemplateClass@TypeAlias | | Ref,RelCont | rel: 1 + typename TemplateClass::Typedef Val3; +// CHECK: [[@LINE-1]]:33 | type-alias/C | Typedef | c:{{.*}}index-dependent-source.cpp@ST>2#T#T@TemplateClass@T@Typedef | | Ref,RelCont | rel: 1 + + // Invalid type names: + typename TemplateClass::field Val4; +// CHECK-NOT: [[@LINE-1]]:33 + typename TemplateClass::staticFunction Val5; +// CHECK-NOT: [[@LINE-1]]:33 + + + object.invalid; +// CHECK-NOT: [[@LINE-1]]:10 + TemplateClass::invalid; +// CHECK-NOT: [[@LINE-1]]:24 +} + +template +void indexDependentOverloads(const TemplateClass &object) { + object.overload1(T()); +// CHECK-NOT: [[@LINE-1]] + object.overload1(S()); +// CHECK-NOT: [[@LINE-1]] + object.overload1(Y()); +// CHECK-NOT: [[@LINE-1]] +}