Index: lib/Index/IndexDecl.cpp =================================================================== --- lib/Index/IndexDecl.cpp +++ lib/Index/IndexDecl.cpp @@ -138,6 +138,53 @@ return true; } + /// Gather the declarations which the given declaration \D overrides in a + /// pseudo-override manner. + /// + /// Pseudo-overrides occur when a class template specialization declares + /// a declaration that has the same name as a similar declaration in the + /// non-specialized template. + void + gatherTemplatePseudoOverrides(const NamedDecl *D, + SmallVectorImpl &Relations) { + if (!IndexCtx.getLangOpts().CPlusPlus) + return; + const auto *CTSD = + dyn_cast(D->getLexicalDeclContext()); + if (!CTSD) + return; + llvm::PointerUnion + Template = CTSD->getSpecializedTemplateOrPartial(); + if (const auto *CTD = Template.dyn_cast()) { + const CXXRecordDecl *Pattern = CTD->getTemplatedDecl(); + bool TypeOverride = isa(D); + for (const NamedDecl *ND : Pattern->lookup(D->getDeclName())) { + if (const auto *CTD = dyn_cast(ND)) + ND = CTD->getTemplatedDecl(); + if (ND->isImplicit()) + continue; + // Types can override other types. + if (!TypeOverride) { + if (ND->getKind() != D->getKind()) + continue; + } else if (!isa(ND)) + continue; + if (const auto *FD = dyn_cast(ND)) { + const auto *DFD = cast(D); + // Function overrides are approximated using the number of parameters. + if (FD->getStorageClass() != DFD->getStorageClass() || + FD->getNumParams() != DFD->getNumParams()) + continue; + } + Relations.emplace_back( + SymbolRoleSet(SymbolRole::RelationOverrideOf) | + SymbolRoleSet(SymbolRole::RelationSpecializationOf), + ND); + } + } + } + bool VisitFunctionDecl(const FunctionDecl *D) { if (D->isDeleted()) return true; @@ -152,6 +199,7 @@ Relations.emplace_back((unsigned)SymbolRole::RelationOverrideOf, *I); } } + gatherTemplatePseudoOverrides(D, Relations); if (!IndexCtx.handleDecl(D, Roles, Relations)) return false; @@ -189,7 +237,9 @@ } bool VisitVarDecl(const VarDecl *D) { - if (!IndexCtx.handleDecl(D)) + SmallVector Relations; + gatherTemplatePseudoOverrides(D, Relations); + if (!IndexCtx.handleDecl(D, SymbolRoleSet(), Relations)) return false; handleDeclarator(D); IndexCtx.indexBody(D->getInit(), D); @@ -197,7 +247,9 @@ } bool VisitFieldDecl(const FieldDecl *D) { - if (!IndexCtx.handleDecl(D)) + SmallVector Relations; + gatherTemplatePseudoOverrides(D, Relations); + if (!IndexCtx.handleDecl(D, SymbolRoleSet(), Relations)) return false; handleDeclarator(D); if (D->isBitField()) @@ -231,9 +283,12 @@ } bool VisitTypedefNameDecl(const TypedefNameDecl *D) { - if (!D->isTransparentTag()) - if (!IndexCtx.handleDecl(D)) + if (!D->isTransparentTag()) { + SmallVector Relations; + gatherTemplatePseudoOverrides(D, Relations); + if (!IndexCtx.handleDecl(D, SymbolRoleSet(), Relations)) return false; + } IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); return true; } @@ -242,7 +297,9 @@ // Non-free standing tags are handled in indexTypeSourceInfo. if (D->isFreeStanding()) { if (D->isThisDeclarationADefinition()) { - IndexCtx.indexTagDecl(D); + SmallVector Relations; + gatherTemplatePseudoOverrides(D, Relations); + IndexCtx.indexTagDecl(D, Relations); } else { auto *Parent = dyn_cast(D->getDeclContext()); return IndexCtx.handleReference(D, D->getLocation(), Parent, Index: lib/Index/IndexingContext.h =================================================================== --- lib/Index/IndexingContext.h +++ lib/Index/IndexingContext.h @@ -48,6 +48,8 @@ void setASTContext(ASTContext &ctx) { Ctx = &ctx; } + const LangOptions &getLangOpts() const; + bool shouldSuppressRefs() const { return false; } Index: lib/Index/IndexingContext.cpp =================================================================== --- lib/Index/IndexingContext.cpp +++ lib/Index/IndexingContext.cpp @@ -17,6 +17,10 @@ using namespace clang; using namespace index; +const LangOptions &IndexingContext::getLangOpts() const { + return Ctx->getLangOpts(); +} + bool IndexingContext::shouldIndexFunctionLocalSymbols() const { return IndexOpts.IndexFunctionLocals; } Index: test/Index/Core/index-source.cpp =================================================================== --- test/Index/Core/index-source.cpp +++ test/Index/Core/index-source.cpp @@ -111,3 +111,112 @@ class PartialSpecilizationClass { }; // CHECK: [[@LINE-1]]:7 | class(Gen,TS)/C++ | PartialSpecilizationClass | c:@S@PartialSpecilizationClass>#I#I | | Def,RelSpecialization | rel: 1 // CHECK-NEXT: RelSpecialization | PartialSpecilizationClass | c:@ST>2#T#T@PartialSpecilizationClass + +template +class PseudoOverridesInSpecializations { + void function() { } + void function(int) { } + + static void staticFunction() { } + + int field; + static int variable; + + typedef T TypeDef; + using TypeAlias = T; + + enum anEnum { }; + + struct Struct { }; + union Union { }; + + using TypealiasOrRecord = void; + + template struct InnerTemplate { }; + template struct InnerTemplate { }; +}; + +template<> +class PseudoOverridesInSpecializations { + void function() { } +// CHECK: [[@LINE-1]]:8 | instance-method/C++ | function | c:@S@PseudoOverridesInSpecializations>#d#I@F@function# | __ZN32PseudoOverridesInSpecializationsIdiE8functionEv | Def,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | function | c:@ST>2#T#T@PseudoOverridesInSpecializations@F@function# + + void staticFunction() { } +// CHECK: [[@LINE-1]]:8 | instance-method/C++ | staticFunction | c:@S@PseudoOverridesInSpecializations>#d#I@F@staticFunction# | __ZN32PseudoOverridesInSpecializationsIdiE14staticFunctionEv | Def,RelChild | rel: 1 +// CHECK-NOT: RelOver + + int notOverridingField = 0; + +// CHECK-LABEL: checLabelBreak + int checLabelBreak = 0; + + int field = 0; +// CHECK: [[@LINE-1]]:7 | field/C++ | field | c:@S@PseudoOverridesInSpecializations>#d#I@FI@field | | Def,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | field | c:@ST>2#T#T@PseudoOverridesInSpecializations@FI@field + + static double variable; +// CHECK: [[@LINE-1]]:17 | static-property/C++ | variable | c:@S@PseudoOverridesInSpecializations>#d#I@variable | __ZN32PseudoOverridesInSpecializationsIdiE8variableE | Decl,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | variable | c:@ST>2#T#T@PseudoOverridesInSpecializations@variable + + typedef double TypeDef; +// CHECK: [[@LINE-1]]:18 | type-alias/C | TypeDef | c:index-source.cpp@S@PseudoOverridesInSpecializations>#d#I@T@TypeDef | | Def,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | TypeDef | c:index-source.cpp@ST>2#T#T@PseudoOverridesInSpecializations@T@TypeDef + + using TypeAlias = int; +// CHECK: [[@LINE-1]]:9 | type-alias/C++ | TypeAlias | c:@S@PseudoOverridesInSpecializations>#d#I@TypeAlias | | Def,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | TypeAlias | c:@ST>2#T#T@PseudoOverridesInSpecializations@TypeAlias + + enum anEnum { }; +// CHECK: [[@LINE-1]]:8 | enum/C | anEnum | c:@S@PseudoOverridesInSpecializations>#d#I@E@anEnum | | Def,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | anEnum | c:@ST>2#T#T@PseudoOverridesInSpecializations@E@anEnum + class Struct { }; +// CHECK: [[@LINE-1]]:9 | class/C++ | Struct | c:@S@PseudoOverridesInSpecializations>#d#I@S@Struct | | Def,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | Struct | c:@ST>2#T#T@PseudoOverridesInSpecializations@S@Struct + union Union { }; +// CHECK: [[@LINE-1]]:9 | union/C | Union | c:@S@PseudoOverridesInSpecializations>#d#I@U@Union | | Def,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | Union | c:@ST>2#T#T@PseudoOverridesInSpecializations@U@Union + + struct TypealiasOrRecord { }; +// CHECK: [[@LINE-1]]:10 | struct/C | TypealiasOrRecord | c:@S@PseudoOverridesInSpecializations>#d#I@S@TypealiasOrRecord | | Def,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | TypealiasOrRecord | c:@ST>2#T#T@PseudoOverridesInSpecializations@TypealiasOrRecord + + template struct InnerTemplate { }; +// CHECK: [[@LINE-1]]:31 | struct(Gen)/C++ | InnerTemplate | c:@S@PseudoOverridesInSpecializations>#d#I@ST>1#T@InnerTemplate | | Def,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | InnerTemplate | c:@ST>2#T#T@PseudoOverridesInSpecializations@ST>1#T@InnerTemplate + template struct InnerTemplate { }; +// CHECK-NOT: RelOver +}; + +template +class PseudoOverridesInSpecializations { + typedef float TypealiasOrRecord; +// CHECK: [[@LINE-1]]:17 | type-alias/C | TypealiasOrRecord | c:index-source.cpp@SP>1#T@PseudoOverridesInSpecializations>#f#t0.0@T@TypealiasOrRecord | | Def,RelChild,RelOver,RelSpecialization | rel: 2 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | TypealiasOrRecord | c:@ST>2#T#T@PseudoOverridesInSpecializations@TypealiasOrRecord +}; + +template +class ConflictingPseudoOverridesInSpecialization { + void foo(T x); + void foo(U x); +}; + +template +class ConflictingPseudoOverridesInSpecialization { + void foo(T x); +// CHECK: [[@LINE-1]]:8 | instance-method/C++ | foo | c:@SP>1#T@ConflictingPseudoOverridesInSpecialization>#I#t0.0@F@foo#S0_# | | Decl,RelChild,RelOver,RelSpecialization | rel: 3 +// CHECK-NEXT: RelChild +// CHECK-NEXT: RelOver,RelSpecialization | foo | c:@ST>2#T#T@ConflictingPseudoOverridesInSpecialization@F@foo#t0.0# +// CHECK-NEXT: RelOver,RelSpecialization | foo | c:@ST>2#T#T@ConflictingPseudoOverridesInSpecialization@F@foo#t0.1# +};