Skip to content

Commit be10554

Browse files
authored
Merge pull request #36484 from bnbarham/dynamic-class-methods
[Index] Add the dynamic role for calls to class methods
2 parents 5709721 + 39bb0d0 commit be10554

File tree

2 files changed

+127
-9
lines changed

2 files changed

+127
-9
lines changed

lib/Index/Index.cpp

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,22 +1348,34 @@ static bool isSuperRefExpr(Expr *E) {
13481348
}
13491349

13501350
static bool isDynamicCall(Expr *BaseE, ValueDecl *D) {
1351-
// The call is 'dynamic' if the method is not of a struct/enum and the
1352-
// receiver is not 'super'. Note that if the receiver is 'super' that
1353-
// does not mean that the call is statically determined (an extension
1354-
// method may have injected itself in the super hierarchy).
1355-
// For our purposes 'dynamic' means that the method call cannot invoke
1356-
// a method in a subclass.
1351+
// "dynamic" here is not Swift's "dynamic" modifier, but rathar "can call a
1352+
// function in a conformance/subclass". Eg. if the receiver is "super", an
1353+
// extension may have injected itself in the parent hierarchy - this call
1354+
// would not be "dynamic" for our purposes (though would be Swift's
1355+
// "dynamic"), but it is also not statically determined.
13571356
auto TyD = getNominalParent(D);
13581357
if (!TyD)
13591358
return false;
13601359
if (isa<StructDecl>(TyD) || isa<EnumDecl>(TyD))
13611360
return false;
13621361
if (isSuperRefExpr(BaseE))
13631362
return false;
1364-
if (BaseE->getType()->is<MetatypeType>())
1363+
// `SomeType.staticOrClassMethod()`
1364+
if (isa<TypeExpr>(BaseE))
13651365
return false;
13661366

1367+
// `type(of: foo).staticOrClassMethod()`, not "dynamic" if the instance type
1368+
// is a struct/enum or if it is a class and the function is a static method
1369+
// (rather than a class method).
1370+
if (auto IT = BaseE->getType()->getAs<MetatypeType>()) {
1371+
auto InstanceType = IT->getInstanceType();
1372+
if (InstanceType->getStructOrBoundGenericStruct() ||
1373+
InstanceType->getEnumOrBoundGenericEnum())
1374+
return false;
1375+
if (InstanceType->getClassOrBoundGenericClass() && D->isFinal())
1376+
return false;
1377+
}
1378+
13671379
return true;
13681380
}
13691381

@@ -1431,17 +1443,22 @@ bool IndexSwiftASTWalker::initFuncRefIndexSymbol(ValueDecl *D, SourceLoc Loc,
14311443
if (!BaseE || BaseE == CurrentE)
14321444
return false;
14331445

1446+
if (isDynamicCall(BaseE, D))
1447+
Info.roles |= (unsigned)SymbolRole::Dynamic;
1448+
14341449
if (Type ReceiverTy = BaseE->getType()) {
14351450
if (auto LVT = ReceiverTy->getAs<LValueType>())
14361451
ReceiverTy = LVT->getObjectType();
14371452
else if (auto MetaT = ReceiverTy->getAs<MetatypeType>())
14381453
ReceiverTy = MetaT->getInstanceType();
14391454

1455+
// TODO: Handle generics and composed protocols
1456+
if (auto OpenedTy = ReceiverTy->getAs<OpenedArchetypeType>())
1457+
ReceiverTy = OpenedTy->getOpenedExistentialType();
1458+
14401459
if (auto TyD = ReceiverTy->getAnyNominal()) {
14411460
if (addRelation(Info, (SymbolRoleSet) SymbolRole::RelationReceivedBy, TyD))
14421461
return true;
1443-
if (isDynamicCall(BaseE, D))
1444-
Info.roles |= (unsigned)SymbolRole::Dynamic;
14451462
}
14461463
}
14471464

test/Index/index_static_funcs.swift

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// RUN: %empty-directory(%t)
2+
//
3+
// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck %s
4+
5+
protocol SwiftProto {
6+
// CHECK: [[@LINE-1]]:10 | protocol/Swift | SwiftProto | [[Proto_USR:.*]] | Def | rel: 0
7+
static func staticMethod()
8+
// CHECK: [[@LINE-1]]:15 | static-method/Swift | staticMethod() | [[ProtoStaticMethod_USR:.*]] | Def,RelChild | rel: 1
9+
}
10+
11+
protocol SwiftProtoSame {
12+
// CHECK: [[@LINE-1]]:10 | protocol/Swift | SwiftProtoSame | [[ProtoSame_USR:.*]] | Def | rel: 0
13+
static func staticMethod()
14+
// CHECK: [[@LINE-1]]:15 | static-method/Swift | staticMethod() | [[ProtoSameStaticMethod_USR:.*]] | Def,RelChild | rel: 1
15+
}
16+
17+
protocol SwiftProtoOther {}
18+
19+
protocol SwiftProtoComposed: SwiftProtoSame, SwiftProto, SwiftProtoOther {}
20+
21+
class SwiftClass: SwiftProtoComposed {
22+
// CHECK: [[@LINE-1]]:7 | class/Swift | SwiftClass | [[Class_USR:.*]] | Def | rel: 0
23+
static func staticMethod() {}
24+
// CHECK: [[@LINE-1]]:15 | static-method/Swift | staticMethod() | [[ClassStaticMethod_USR:.*]] | Def,RelChild,RelOver | rel: 3
25+
class func classMethod() {}
26+
// CHECK: [[@LINE-1]]:14 | class-method/Swift | classMethod() | [[ClassClassMethod_USR:.*]] | Def,Dyn,RelChild | rel: 1
27+
}
28+
29+
struct SwiftStruct: SwiftProtoComposed {
30+
// CHECK: [[@LINE-1]]:8 | struct/Swift | SwiftStruct | [[Struct_USR:.*]] | Def | rel: 0
31+
static func staticMethod() {}
32+
// CHECK: [[@LINE-1]]:15 | static-method/Swift | staticMethod() | [[StructStaticMethod_USR:.*]] | Def,RelChild,RelOver | rel: 3
33+
}
34+
35+
enum SwiftEnum: SwiftProtoComposed {
36+
// CHECK: [[@LINE-1]]:6 | enum/Swift | SwiftEnum | [[Enum_USR:.*]] | Def | rel: 0
37+
static func staticMethod() {}
38+
// CHECK: [[@LINE-1]]:15 | static-method/Swift | staticMethod() | [[EnumStaticMethod_USR:.*]] | Def,RelChild,RelOver | rel: 3
39+
}
40+
41+
func directCalls() {
42+
SwiftClass.staticMethod()
43+
// CHECK: [[@LINE-1]]:14 | static-method/Swift | staticMethod() | [[ClassStaticMethod_USR]] | Ref,Call,RelRec,RelCall,RelCont | rel: 2
44+
// CHECK-DAG: RelRec | class/Swift | SwiftClass | [[Class_USR]]
45+
SwiftClass.classMethod()
46+
// CHECK: [[@LINE-1]]:14 | class-method/Swift | classMethod() | [[ClassClassMethod_USR]] | Ref,Call,RelRec,RelCall,RelCont | rel: 2
47+
// CHECK-DAG: RelRec | class/Swift | SwiftClass | [[Class_USR]]
48+
49+
SwiftStruct.staticMethod()
50+
// CHECK: [[@LINE-1]]:15 | static-method/Swift | staticMethod() | [[StructStaticMethod_USR]] | Ref,Call,RelRec,RelCall,RelCont | rel: 2
51+
// CHECK-DAG: RelRec | struct/Swift | SwiftStruct | [[Struct_USR]]
52+
53+
SwiftEnum.staticMethod()
54+
// CHECK: [[@LINE-1]]:13 | static-method/Swift | staticMethod() | [[EnumStaticMethod_USR]] | Ref,Call,RelRec,RelCall,RelCont | rel: 2
55+
// CHECK-DAG: RelRec | enum/Swift | SwiftEnum | [[Enum_USR]]
56+
}
57+
58+
func typeofClass(c: SwiftClass) {
59+
type(of: c).staticMethod()
60+
// CHECK: [[@LINE-1]]:15 | static-method/Swift | staticMethod() | [[ClassStaticMethod_USR]] | Ref,Call,RelRec,RelCall,RelCont | rel: 2
61+
// CHECK: RelRec | class/Swift | SwiftClass | [[Class_USR]]
62+
type(of: c).classMethod()
63+
// CHECK: [[@LINE-1]]:15 | class-method/Swift | classMethod() | [[ClassClassMethod_USR]] | Ref,Call,Dyn,RelRec,RelCall,RelCont | rel: 2
64+
// CHECK: RelRec | class/Swift | SwiftClass | [[Class_USR]]
65+
}
66+
67+
func typeofStruct(s: SwiftStruct) {
68+
type(of: s).staticMethod()
69+
// CHECK: [[@LINE-1]]:15 | static-method/Swift | staticMethod() | [[StructStaticMethod_USR]] | Ref,Call,RelRec,RelCall,RelCont | rel: 2
70+
// CHECK: RelRec | struct/Swift | SwiftStruct | [[Struct_USR]]
71+
}
72+
73+
func typeofEnum(e: SwiftEnum) {
74+
type(of: e).staticMethod()
75+
// CHECK: [[@LINE-1]]:15 | static-method/Swift | staticMethod() | [[EnumStaticMethod_USR]] | Ref,Call,RelRec,RelCall,RelCont | rel: 2
76+
// CHECK: RelRec | enum/Swift | SwiftEnum | [[Enum_USR]]
77+
}
78+
79+
func typeofProtocol(proto: SwiftProto) {
80+
type(of: proto).staticMethod()
81+
// CHECK: [[@LINE-1]]:19 | static-method/Swift | staticMethod() | [[ProtoStaticMethod_USR]] | Ref,Call,Dyn,RelRec,RelCall,RelCont | rel: 2
82+
// CHECK: RelRec | protocol/Swift | SwiftProto | [[Proto_USR]]
83+
}
84+
85+
// FIXME: Add the ReceivedBy relation for generics
86+
func genericSingle<T>(proto: T) where T: SwiftProto {
87+
type(of: proto).staticMethod()
88+
// CHECK: [[@LINE-1]]:19 | static-method/Swift | staticMethod() | [[ProtoStaticMethod_USR]] | Ref,Call,Dyn,RelCall,RelCont | rel: 1
89+
}
90+
91+
// FIXME: The composed cases currently picks one of the USRs, should we output
92+
// multiple occurrences?
93+
func genericComposedType<T>(proto: T) where T: SwiftProtoComposed {
94+
type(of: proto).staticMethod()
95+
// CHECK: [[@LINE-1]]:19 | static-method/Swift | staticMethod() | [[ProtoStaticMethod_USR]] | Ref,Call,Dyn,RelCall,RelCont | rel: 1
96+
}
97+
98+
func genericComposedWhere<T>(proto: T) where T: SwiftProto & SwiftProtoSame & SwiftProtoOther {
99+
type(of: proto).staticMethod()
100+
// CHECK: [[@LINE-1]]:19 | static-method/Swift | staticMethod() | [[ProtoSameStaticMethod_USR]] | Ref,Call,Dyn,RelCall,RelCont | rel: 1
101+
}

0 commit comments

Comments
 (0)