Merge pull request #85741 from hamishknight/related-fix

[Index] Handle implicit reference relations for type sugar
This commit is contained in:
Hamish Knight
2025-12-01 22:50:57 +00:00
committed by GitHub
3 changed files with 174 additions and 65 deletions

View File

@@ -103,12 +103,8 @@ static bool isMemberwiseInit(swift::ValueDecl *D) {
}
static SourceLoc getLocForExtension(ExtensionDecl *D) {
// Use the 'End' token of the range, in case it is a compound name, e.g.
// extension A.B {}
// we want the location of 'B' token.
if (auto *repr = D->getExtendedTypeRepr()) {
return repr->getSourceRange().End;
}
if (auto *repr = D->getExtendedTypeRepr())
return repr->getLoc();
return SourceLoc();
}
@@ -1037,12 +1033,13 @@ private:
bool reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isImplicit, SymbolRoleSet Relations, Decl *Related);
/// Report a related type relation for a given TypeRepr.
/// Report a related type relation for a given TypeLoc. Don't call this
/// directly, use \c reportRelatedTypeRef instead.
///
/// NOTE: If the dependent type is a typealias, report the underlying types as
/// well.
///
/// \param TR The type being referenced.
/// \param TL The type being referenced.
/// \param Relations The relationship between the referenced type and the
/// passed Decl.
/// \param Related The Decl that is referencing the type.
@@ -1050,8 +1047,9 @@ private:
/// typealias' underlying type.
/// \param ParentLoc The parent location of the reference that should be used
/// for implicit references.
bool reportRelatedTypeRepr(const TypeRepr *TR, SymbolRoleSet Relations,
Decl *Related, bool Implicit, SourceLoc ParentLoc);
bool reportRelatedTypeRefImpl(const TypeLoc &TL, SymbolRoleSet Relations,
Decl *Related, bool Implicit,
SourceLoc ParentLoc);
/// Report a related type relation for a Type at a given location.
///
@@ -1531,66 +1529,85 @@ bool IndexSwiftASTWalker::reportInheritedTypeRefs(InheritedTypes Inherited,
return true;
}
bool IndexSwiftASTWalker::reportRelatedTypeRepr(const TypeRepr *TR,
SymbolRoleSet Relations,
Decl *Related, bool Implicit,
SourceLoc ParentLoc) {
// Look through parens/specifiers/attributes.
while (true) {
if (TR->isParenType()) {
TR = TR->getWithoutParens();
continue;
}
if (auto *SPR = dyn_cast<SpecifierTypeRepr>(TR)) {
TR = SPR->getBase();
continue;
}
if (auto *ATR = dyn_cast<AttributedTypeRepr>(TR)) {
TR = ATR->getTypeRepr();
continue;
}
break;
}
// NOTE: We don't yet handle InverseTypeRepr since we don't have an inverse
// relation for inheritance.
if (auto *composite = dyn_cast<CompositionTypeRepr>(TR)) {
for (auto *Type : composite->getTypes()) {
if (!reportRelatedTypeRepr(Type, Relations, Related, Implicit,
ParentLoc)) {
return false;
bool IndexSwiftASTWalker::reportRelatedTypeRefImpl(const TypeLoc &TL,
SymbolRoleSet Relations,
Decl *Related, bool Implicit,
SourceLoc ParentLoc) {
auto *TR = TL.getTypeRepr();
if (TR) {
// Look through parens/specifiers/attributes.
while (true) {
if (TR->isParenType()) {
TR = TR->getWithoutParens();
continue;
}
if (auto *SPR = dyn_cast<SpecifierTypeRepr>(TR)) {
TR = SPR->getBase();
continue;
}
if (auto *ATR = dyn_cast<AttributedTypeRepr>(TR)) {
TR = ATR->getTypeRepr();
continue;
}
break;
}
// NOTE: We don't yet handle InverseTypeRepr since we don't have an inverse
// relation for inheritance.
if (auto *composite = dyn_cast<CompositionTypeRepr>(TR)) {
for (auto *Type : composite->getTypes()) {
// Note this doesn't handle type sugar cases where the decl is only
// available on the semantic type. This isn't currently something that
// happens for type compositions though.
if (!reportRelatedTypeRefImpl(Type, Relations, Related, Implicit,
ParentLoc)) {
return false;
}
}
return true;
}
}
auto *declRefTR = dyn_cast<DeclRefTypeRepr>(TR);
if (!declRefTR)
return true;
SourceLoc Loc = [&]() {
if (ParentLoc)
return ParentLoc;
if (TR)
return TR->getLoc();
return SourceLoc();
}();
auto *declRefTR = dyn_cast_or_null<DeclRefTypeRepr>(TR);
if (!declRefTR) {
if (!TL.getType())
return true;
// If we don't have a TypeRepr, we're e.g indexing a Swift module, so want
// to look at the Type instead. If we have a TypeRepr but have no explicit
// decl reference, fall back to indexing an implicit reference to handle
// type sugar e.g Array for `[Int]`.
return reportRelatedType(TL.getType(), Relations, Related,
Implicit || TR, Loc);
}
auto *VD = declRefTR->getBoundDecl();
if (!VD)
return true;
SourceLoc IdLoc = ParentLoc.isValid() ? ParentLoc : declRefTR->getLoc();
if (auto *TAD = dyn_cast<TypeAliasDecl>(VD)) {
IndexSymbol Info;
if (Implicit)
Info.roles |= (unsigned)SymbolRole::Implicit;
if (!reportRef(TAD, IdLoc, Info, std::nullopt))
if (!reportRef(TAD, Loc, Info, std::nullopt))
return false;
// Recurse into the underlying type and report any found references as
// implicit references at the location of the typealias reference.
if (auto *UTR = TAD->getUnderlyingTypeRepr()) {
return reportRelatedTypeRepr(UTR, Relations, Related,
/*Implicit*/ true, /*ParentLoc*/ IdLoc);
}
// If we don't have a TypeRepr available, this is a typealias in another
// module, consult the computed underlying type.
return reportRelatedType(TAD->getUnderlyingType(), Relations, Related,
/*Implicit*/ true, /*ParentLoc*/ IdLoc);
TypeLoc UnderlyingTL(TAD->getUnderlyingTypeRepr(),
TAD->getUnderlyingType());
return reportRelatedTypeRefImpl(UnderlyingTL, Relations, Related,
/*Implicit*/ true, /*ParentLoc*/ Loc);
}
if (auto *NTD = dyn_cast<NominalTypeDecl>(VD)) {
if (!reportRelatedRef(NTD, IdLoc, Implicit, Relations, Related))
if (!reportRelatedRef(NTD, Loc, Implicit, Relations, Related))
return false;
}
return true;
@@ -1618,19 +1635,8 @@ bool IndexSwiftASTWalker::reportRelatedType(Type Ty, SymbolRoleSet Relations,
bool IndexSwiftASTWalker::reportRelatedTypeRef(const TypeLoc &TL,
SymbolRoleSet Relations,
Decl *Related) {
// If we have a TypeRepr, prefer that since it lets us match up source
// locations with the code the user wrote.
if (auto *TR = TL.getTypeRepr()) {
return reportRelatedTypeRepr(TR, Relations, Related, /*Implicit*/ false,
/*ParentLoc*/ SourceLoc());
}
// Otherwise fall back to reporting the Type, this is necessary when indexing
// swiftmodules.
if (auto Ty = TL.getType()) {
return reportRelatedType(Ty, Relations, Related,
/*Implicit*/ false, SourceLoc());
}
return true;
return reportRelatedTypeRefImpl(TL, Relations, Related, /*Implicit*/ false,
/*ParentLoc*/ SourceLoc());
}
bool IndexSwiftASTWalker::reportPseudoAccessor(AbstractStorageDecl *D,

View File

@@ -29,6 +29,48 @@ public struct NonCopyable: NonCopyableProto & ~Copyable {}
// We don't currently have a relation for Copyable.
// LIB-NOT: s:s8CopyableP
extension [Int] {
public func foo() {}
}
// LIB: 0:0 | extension/ext-struct/Swift | Array | s:e:s:Sa3LibSiRszlE3fooyyF | Def | rel: 0
// LIB-NEXT: 0:0 | struct/Swift | Array | s:Sa | Ref,RelExt | rel: 1
// LIB-NEXT: RelExt | extension/ext-struct/Swift | Array | s:e:s:Sa3LibSiRszlE3fooyyF
extension Array where Element == Int {
public func bar() {}
}
// LIB: 0:0 | extension/ext-struct/Swift | Array | s:e:s:Sa3LibSiRszlE3baryyF | Def | rel: 0
// LIB-NEXT: 0:0 | struct/Swift | Array | s:Sa | Ref,RelExt | rel: 1
// LIB-NEXT: RelExt | extension/ext-struct/Swift | Array | s:e:s:Sa3LibSiRszlE3baryyF
extension Int? {
public func baz() {}
}
// LIB: 0:0 | extension/ext-enum/Swift | Optional | s:e:s:Sq3LibSiRszlE3bazyyF | Def | rel: 0
// LIB-NEXT: 0:0 | enum/Swift | Optional | s:Sq | Ref,RelExt | rel: 1
// LIB-NEXT: RelExt | extension/ext-enum/Swift | Optional | s:e:s:Sq3LibSiRszlE3bazyyF
public typealias IntArray = [Int]
public typealias ArrayOf<T> = [T]
extension IntArray {
public func qux() {}
}
// We don't currently report references to typealiases in imported modules, so
// this is just an extension of Array.
// LIB: 0:0 | extension/ext-struct/Swift | Array | s:e:s:Sa3LibSiRszlE3quxyyF | Def | rel: 0
// LIB-NEXT: 0:0 | struct/Swift | Array | s:Sa | Ref,RelExt | rel: 1
// LIB-NEXT: RelExt | extension/ext-struct/Swift | Array | s:e:s:Sa3LibSiRszlE3quxyyF
extension ArrayOf<Int> {
public func flim() {}
}
// We don't currently report references to typealiases in imported modules, so
// this is just an extension of Array.
// LIB: 0:0 | extension/ext-struct/Swift | Array | s:e:s:Sa3LibSiRszlE4flimyyF | Def | rel: 0
// LIB-NEXT: 0:0 | struct/Swift | Array | s:Sa | Ref,RelExt | rel: 1
// LIB-NEXT: RelExt | extension/ext-struct/Swift | Array | s:e:s:Sa3LibSiRszlE4flimyyF
//--- main.swift
import Lib
@@ -41,3 +83,19 @@ struct K: P & X {}
// CHECK-NEXT: RelBase | struct/Swift | K | s:14swift_ide_test1KV
// CHECK-NEXT: [[@LINE-6]]:15 | protocol/Swift | R | s:3Lib1RP | Ref,Impl,RelBase | rel: 1
// CHECK-NEXT: RelBase | struct/Swift | K | s:14swift_ide_test1KV
extension IntArray {
// CHECK: [[@LINE-1]]:11 | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE4flamyyF | Def | rel: 0
// CHECK-NEXT: [[@LINE-2]]:11 | type-alias/Swift | IntArray | s:3Lib8IntArraya | Ref | rel: 0
// CHECK-NEXT: [[@LINE-3]]:11 | struct/Swift | Array | s:Sa | Ref,Impl,RelExt | rel: 1
// CHECK-NEXT: RelExt | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE4flamyyF
func flam() {}
}
extension ArrayOf<Int> {
// CHECK: [[@LINE-1]]:11 | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE4bishyyF | Def | rel: 0
// CHECK-NEXT: [[@LINE-2]]:11 | type-alias/Swift | ArrayOf | s:3Lib7ArrayOfa | Ref | rel: 0
// CHECK-NEXT: [[@LINE-3]]:11 | struct/Swift | Array | s:Sa | Ref,Impl,RelExt | rel: 1
// CHECK-NEXT: RelExt | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE4bishyyF
func bish() {}
}

View File

@@ -564,3 +564,48 @@ class Subclass: BaseClass {
// CHECK-NEXT: RelChild,RelAcc | instance-property/Swift | x | s:14swift_ide_test8SubclassC1xSivp
}
}
extension [Int] {
// CHECK: [[@LINE-1]]:11 | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE3fooyyF | Def | rel: 0
// CHECK-NEXT: [[@LINE-2]]:11 | struct/Swift | Array | s:Sa | Ref,Impl,RelExt | rel: 1
// CHECK-NEXT: RelExt | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE3fooyyF
// CHECK-NEXT: [[@LINE-4]]:12 | struct/Swift | Int | s:Si | Ref | rel: 0
func foo() {}
}
extension Array where Element == Int {
// CHECK: [[@LINE-1]]:11 | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE3baryyF | Def | rel: 0
// CHECK-NEXT: [[@LINE-2]]:11 | struct/Swift | Array | s:Sa | Ref,RelExt | rel: 1
// CHECK-NEXT: RelExt | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE3baryyF
// CHECK-NEXT: [[@LINE-4]]:23 | type-alias/generic-type-param/Swift | Element | s:Sa7Elementxmfp | Ref | rel: 0
// CHECK-NEXT: [[@LINE-5]]:34 | struct/Swift | Int | s:Si | Ref | rel: 0
func bar() {}
}
extension Int? {
// CHECK: [[@LINE-1]]:14 | extension/ext-enum/Swift | Optional | s:e:s:Sq14swift_ide_testSiRszlE3bazyyF | Def | rel: 0
// CHECK-NEXT: [[@LINE-2]]:14 | enum/Swift | Optional | s:Sq | Ref,Impl,RelExt | rel: 1
// CHECK-NEXT: RelExt | extension/ext-enum/Swift | Optional | s:e:s:Sq14swift_ide_testSiRszlE3bazyyF
// CHECK-NEXT: [[@LINE-4]]:11 | struct/Swift | Int | s:Si | Ref | rel: 0
func baz() {}
}
typealias IntArray = [Int]
typealias ArrayOf<T> = [T]
extension IntArray {
// CHECK: [[@LINE-1]]:11 | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE4flamyyF | Def | rel: 0
// CHECK-NEXT: [[@LINE-2]]:11 | type-alias/Swift | IntArray | s:14swift_ide_test8IntArraya | Ref | rel: 0
// CHECK-NEXT: [[@LINE-3]]:11 | struct/Swift | Array | s:Sa | Ref,Impl,RelExt | rel: 1
// CHECK-NEXT: RelExt | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE4flamyyF
func flam() {}
}
extension ArrayOf<Int> {
// CHECK: [[@LINE-1]]:11 | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE4bishyyF | Def | rel: 0
// CHECK-NEXT: [[@LINE-2]]:11 | type-alias/Swift | ArrayOf | s:14swift_ide_test7ArrayOfa | Ref | rel: 0
// CHECK-NEXT: [[@LINE-3]]:11 | struct/Swift | Array | s:Sa | Ref,Impl,RelExt | rel: 1
// CHECK-NEXT: RelExt | extension/ext-struct/Swift | Array | s:e:s:Sa14swift_ide_testSiRszlE4bishyyF
func bish() {}
}