Skip to content

Commit 19f2313

Browse files
committed
[SourceKit/CursorInfo] Mark dynamic calls
Adds two new fields to the cursor info response: 1. is_dynamic: whether a call is dynamic 2. receivers: receivers of the call (USRs) Users of the CursorInfo request can use "is_dynamic" to decide whether to lookup overrides or not, and then the "receivers" as the starting point of the lookup. Resolves rdar://75385900
1 parent b889b07 commit 19f2313

File tree

10 files changed

+273
-77
lines changed

10 files changed

+273
-77
lines changed

include/swift/IDE/Utils.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@ struct ResolvedCursorInfo {
164164
Type ContainerType;
165165
Stmt *TrailingStmt = nullptr;
166166
Expr *TrailingExpr = nullptr;
167+
/// If this is a call, whether it is "dynamic", see ide::isDynamicCall.
168+
bool IsDynamic = false;
169+
/// If this is a call, the types of the base (multiple in the case of
170+
/// protocol composition).
171+
SmallVector<NominalTypeDecl *, 1> ReceiverTypes;
167172

168173
ResolvedCursorInfo() = default;
169174
ResolvedCursorInfo(SourceFile *SF) : SF(SF) {}
@@ -174,12 +179,9 @@ struct ResolvedCursorInfo {
174179
lhs.Loc.getOpaquePointerValue() == rhs.Loc.getOpaquePointerValue();
175180
}
176181

177-
void setValueRef(ValueDecl *ValueD,
178-
TypeDecl *CtorTyRef,
179-
ExtensionDecl *ExtTyRef,
180-
bool IsRef,
181-
Type Ty,
182-
Type ContainerType) {
182+
void setValueRef(ValueDecl *ValueD, TypeDecl *CtorTyRef,
183+
ExtensionDecl *ExtTyRef, bool IsRef,
184+
Type Ty, Type ContainerType) {
183185
Kind = CursorInfoKind::ValueRef;
184186
this->ValueD = ValueD;
185187
this->CtorTyRef = CtorTyRef;

lib/IDE/IDERequests.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class CursorInfoResolver : public SourceEntityWalker {
6060
ResolvedCursorInfo CursorInfo;
6161
Type ContainerType;
6262
Expr *OutermostCursorExpr;
63+
llvm::SmallVector<Expr*, 8> ExprStack;
6364

6465
public:
6566
explicit CursorInfoResolver(SourceFile &SrcFile) :
@@ -99,8 +100,8 @@ SourceManager &CursorInfoResolver::getSourceMgr() const
99100
}
100101

101102
bool CursorInfoResolver::tryResolve(ValueDecl *D, TypeDecl *CtorTyRef,
102-
ExtensionDecl *ExtTyRef, SourceLoc Loc,
103-
bool IsRef, Type Ty) {
103+
ExtensionDecl *ExtTyRef, SourceLoc Loc,
104+
bool IsRef, Type Ty) {
104105
if (!D->hasName())
105106
return false;
106107

@@ -116,6 +117,14 @@ bool CursorInfoResolver::tryResolve(ValueDecl *D, TypeDecl *CtorTyRef,
116117
}
117118
}
118119
}
120+
121+
if (isBeingCalled(ExprStack)) {
122+
if (Expr *BaseE = getBase(ExprStack)) {
123+
CursorInfo.IsDynamic = isDynamicCall(BaseE, D);
124+
ide::getReceiverType(BaseE, CursorInfo.ReceiverTypes);
125+
}
126+
}
127+
119128
CursorInfo.setValueRef(D, CtorTyRef, ExtTyRef, IsRef, Ty, ContainerType);
120129
return true;
121130
}
@@ -254,6 +263,8 @@ bool CursorInfoResolver::walkToExprPre(Expr *E) {
254263
if (!OutermostCursorExpr && isCursorOn(E, LocToResolve))
255264
OutermostCursorExpr = E;
256265

266+
ExprStack.push_back(E);
267+
257268
return true;
258269
}
259270

@@ -266,6 +277,8 @@ bool CursorInfoResolver::walkToExprPost(Expr *E) {
266277
return false;
267278
}
268279

280+
ExprStack.pop_back();
281+
269282
return true;
270283
}
271284

lib/IDE/Utils.cpp

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,33 +1214,41 @@ Expr *swift::ide::getBase(ArrayRef<Expr *> ExprStack) {
12141214

12151215
Expr *CurrentE = ExprStack.back();
12161216
Expr *ParentE = getContainingExpr(ExprStack, 1);
1217+
Expr *Base = nullptr;
12171218
if (auto DSE = dyn_cast_or_null<DotSyntaxCallExpr>(ParentE))
1218-
return DSE->getBase();
1219+
Base = DSE->getBase();
12191220
else if (auto MRE = dyn_cast<MemberRefExpr>(CurrentE))
1220-
return MRE->getBase();
1221+
Base = MRE->getBase();
12211222
else if (auto SE = dyn_cast<SubscriptExpr>(CurrentE))
1222-
return SE->getBase();
1223-
return nullptr;
1224-
}
1225-
1226-
static bool isSuperRefExpr(Expr *E) {
1227-
if (!E)
1228-
return false;
1229-
if (isa<SuperRefExpr>(E))
1230-
return true;
1231-
if (auto LoadE = dyn_cast<LoadExpr>(E))
1232-
return isSuperRefExpr(LoadE->getSubExpr());
1233-
return false;
1223+
Base = SE->getBase();
1224+
1225+
if (Base) {
1226+
while (auto ICE = dyn_cast<ImplicitConversionExpr>(Base))
1227+
Base = ICE->getSubExpr();
1228+
// DotSyntaxCallExpr with getBase() == CurrentE (ie. the current call is
1229+
// the base of another expression)
1230+
if (Base == CurrentE)
1231+
return nullptr;
1232+
}
1233+
return Base;
12341234
}
12351235

12361236
bool swift::ide::isDynamicCall(Expr *Base, ValueDecl *D) {
12371237
auto TyD = D->getDeclContext()->getSelfNominalTypeDecl();
12381238
if (!TyD)
12391239
return false;
1240-
if (isa<StructDecl>(TyD) || isa<EnumDecl>(TyD))
1240+
1241+
if (isa<StructDecl>(TyD) || isa<EnumDecl>(TyD) || D->isFinal())
12411242
return false;
1242-
if (isSuperRefExpr(Base))
1243+
1244+
// super.method()
1245+
// TODO: Should be dynamic if `D` is marked as dynamic and @objc, but in
1246+
// that case we really need to change the role the index outputs as
1247+
// well - the overrides we'd want to include are from the type of
1248+
// super up to `D`
1249+
if (Base->isSuperExpr())
12431250
return false;
1251+
12441252
// `SomeType.staticOrClassMethod()`
12451253
if (isa<TypeExpr>(Base))
12461254
return false;
@@ -1270,6 +1278,8 @@ void swift::ide::getReceiverType(Expr *Base,
12701278
ReceiverTy = LVT->getObjectType();
12711279
else if (auto MetaT = ReceiverTy->getAs<MetatypeType>())
12721280
ReceiverTy = MetaT->getInstanceType();
1281+
else if (auto SelfT = ReceiverTy->getAs<DynamicSelfType>())
1282+
ReceiverTy = SelfT->getSelfType();
12731283

12741284
// TODO: Handle generics and composed protocols
12751285
if (auto OpenedTy = ReceiverTy->getAs<OpenedArchetypeType>())
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
protocol ProtoA {
2+
func method()
3+
static func staticMethod()
4+
}
5+
6+
class ClassB: ProtoA {
7+
let immutableMember: Int = 1
8+
var mutableMember: Int = 1
9+
func method() {}
10+
static func staticMethod() {}
11+
class func classMethod() {}
12+
final func finalMethod() {}
13+
}
14+
15+
class ClassC: ClassB {}
16+
17+
class ClassD: ClassC {
18+
override var mutableMember: Int {
19+
get {
20+
return super.mutableMember
21+
}
22+
set {
23+
super.mutableMember = newValue + 1
24+
}
25+
}
26+
27+
override func method() {
28+
super.method()
29+
}
30+
31+
override class func classMethod() {}
32+
}
33+
34+
struct StructE: ProtoA {
35+
func method() {}
36+
37+
static func staticMethod() {}
38+
}
39+
40+
func direct() {
41+
ClassB.staticMethod()
42+
ClassB.classMethod()
43+
ClassD.classMethod()
44+
StructE.staticMethod()
45+
}
46+
47+
func protoCalls(p: ProtoA) {
48+
p.method()
49+
type(of: p).staticMethod()
50+
}
51+
52+
func classCalls(c: ClassC) {
53+
_ = c.immutableMember
54+
_ = c.mutableMember
55+
c.mutableMember = 1
56+
c.method()
57+
type(of: c).staticMethod()
58+
type(of: c).classMethod()
59+
c.finalMethod()
60+
}
61+
62+
func structCalls(e: StructE) {
63+
e.method()
64+
type(of: e).staticMethod()
65+
}
66+
67+
// RUN: %sourcekitd-test -req=cursor -pos=28:11 %s -- %s | %FileCheck -check-prefix=CHECK-SUPER %s
68+
// CHECK-SUPER: s:14cursor_dynamic6ClassBC6methodyyF
69+
// CHECK-SUPER-NOT: DYNAMIC
70+
71+
// RUN: %sourcekitd-test -req=cursor -pos=41:10 %s -- %s | %FileCheck -check-prefix=CHECK-CLASSSTATIC %s
72+
// CHECK-CLASSSTATIC: s:14cursor_dynamic6ClassBC12staticMethodyyFZ
73+
// CHECK-CLASSSTATIC-NOT: DYNAMIC
74+
75+
// RUN: %sourcekitd-test -req=cursor -pos=42:10 %s -- %s | %FileCheck -check-prefix=CHECK-CLASSCLASS %s
76+
// CHECK-CLASSCLASS: s:14cursor_dynamic6ClassBC11classMethodyyFZ
77+
// CHECK-CLASSCLASS-NOT: DYNAMIC
78+
79+
// RUN: %sourcekitd-test -req=cursor -pos=43:10 %s -- %s | %FileCheck -check-prefix=CHECK-SUBCLASSCLASS %s
80+
// CHECK-SUBCLASSCLASS: s:14cursor_dynamic6ClassDC11classMethodyyFZ
81+
// CHECK-SUBCLASSCLASS-NOT: DYNAMIC
82+
83+
// RUN: %sourcekitd-test -req=cursor -pos=44:11 %s -- %s | %FileCheck -check-prefix=CHECK-STRUCTSTATIC %s
84+
// CHECK-STRUCTSTATIC: s:14cursor_dynamic7StructEV12staticMethodyyFZ
85+
// CHECK-STRUCTSTATIC-NOT: DYNAMIC
86+
87+
// RUN: %sourcekitd-test -req=cursor -pos=48:5 %s -- %s | %FileCheck -check-prefix=CHECK-PROTOMETHOD %s
88+
// CHECK-PROTOMETHOD: s:14cursor_dynamic6ProtoAP6methodyyF
89+
// CHECK-PROTOMETHOD: DYNAMIC
90+
// CHECK-PROTOMETHOD: RECEIVERS BEGIN
91+
// CHECK-PROTOMETHOD-NEXT: s:14cursor_dynamic6ProtoAP
92+
// CHECK-PROTOMETHOD-NEXT: RECEIVERS END
93+
94+
// RUN: %sourcekitd-test -req=cursor -pos=49:15 %s -- %s | %FileCheck -check-prefix=CHECK-PROTOTOSTATIC %s
95+
// CHECK-PROTOTOSTATIC: s:14cursor_dynamic6ProtoAP12staticMethodyyFZ
96+
// CHECK-PROTOTOSTATIC: DYNAMIC
97+
// CHECK-PROTOSTATIC: RECEIVERS BEGIN
98+
// CHECK-PROTOSTATIC-NEXT: s:14cursor_dynamic6ProtoAP
99+
// CHECK-PROTOSTATIC-NEXT: RECEIVERS END
100+
101+
// RUN: %sourcekitd-test -req=cursor -pos=53:9 %s -- %s | %FileCheck -check-prefix=CHECK-CLASSIMMEMBER %s
102+
// CHECK-CLASSIMMEMBER: s:14cursor_dynamic6ClassBC15immutableMemberSivp
103+
// rdar://75645572 should include getter without dynamic (or maybe we just skip returning in that case
104+
// since it would only be used to find overrides)
105+
106+
// RUN: %sourcekitd-test -req=cursor -pos=54:9 %s -- %s | %FileCheck -check-prefix=CHECK-CLASSMEMBERREAD %s
107+
// CHECK-CLASSMEMBERREAD: s:14cursor_dynamic6ClassBC13mutableMemberSivp
108+
// rdar://75645572 should include getter with dynamic
109+
110+
// RUN: %sourcekitd-test -req=cursor -pos=55:5 %s -- %s | %FileCheck -check-prefix=CHECK-CLASSMEMBERWRITE %s
111+
// CHECK-CLASSMEMBERWRITE: s:14cursor_dynamic6ClassBC13mutableMemberSivp
112+
// rdar://75645572 should include setter with dynamic
113+
114+
// RUN: %sourcekitd-test -req=cursor -pos=56:5 %s -- %s | %FileCheck -check-prefix=CHECK-CLASSMETHOD %s
115+
// CHECK-CLASSMETHOD: s:14cursor_dynamic6ClassBC6methodyyF
116+
// CHECK-CLASSMETHOD: DYNAMIC
117+
// CHECK-CLASSMETHOD: RECEIVERS BEGIN
118+
// CHECK-CLASSMETHOD-NEXT: s:14cursor_dynamic6ClassCC
119+
// CHECK-CLASSMETHOD-NEXT: RECEIVERS END
120+
121+
// RUN: %sourcekitd-test -req=cursor -pos=57:15 %s -- %s | %FileCheck -check-prefix=CHECK-CLASSTOSTATIC %s
122+
// CHECK-CLASSTOSTATIC: s:14cursor_dynamic6ClassBC12staticMethodyyFZ
123+
// CHECK-CLASSTOSTATIC-NOT: DYNAMIC
124+
125+
// RUN: %sourcekitd-test -req=cursor -pos=58:15 %s -- %s | %FileCheck -check-prefix=CHECK-CLASSTOCLASS %s
126+
// CHECK-CLASSTOCLASS: s:14cursor_dynamic6ClassBC11classMethodyyFZ
127+
// CHECK-CLASSTOCLASS: DYNAMIC
128+
// CHECK-CLASSTOCLASS: RECEIVERS BEGIN
129+
// CHECK-CLASSTOCLASS-NEXT: s:14cursor_dynamic6ClassCC
130+
// CHECK-CLASSTOCLASS-NEXT: RECEIVERS END
131+
132+
// RUN: %sourcekitd-test -req=cursor -pos=59:5 %s -- %s | %FileCheck -check-prefix=CHECK-CLASSFINAL %s
133+
// CHECK-CLASSFINAL: s:14cursor_dynamic6ClassBC11finalMethodyyF
134+
// CHECK-CLASSFINAL-NOT: DYNAMIC
135+
136+
// RUN: %sourcekitd-test -req=cursor -pos=63:5 %s -- %s | %FileCheck -check-prefix=CHECK-STRUCTMETHOD %s
137+
// CHECK-STRUCTMETHOD: s:14cursor_dynamic7StructEV6methodyyF
138+
// CHECK-STRUCTMETHOD-NOT: DYNAMIC
139+
140+
// RUN: %sourcekitd-test -req=cursor -pos=64:15 %s -- %s | %FileCheck -check-prefix=CHECK-STRUCTTOSTATIC %s
141+
// CHECK-STRUCTTOSTATIC: s:14cursor_dynamic7StructEV12staticMethodyyFZ
142+
// CHECK-STRUCTOTSTATIC-NOT: DYNAMIC

test/SourceKit/Indexing/index_forbid_typecheck.swift.response

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@
8989
key.line: 5,
9090
key.column: 20,
9191
key.receiver_usr: "s:16forbid_typecheck6ClsSecC",
92-
key.is_dynamic: 1,
9392
key.is_implicit: 1
9493
}
9594
]

tools/SourceKit/include/SourceKit/Core/LangSupport.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,13 @@ struct CursorInfoData {
431431
/// Stores the Symbol Graph title, kind, and USR of the parent contexts of the
432432
/// symbol under the cursor.
433433
ArrayRef<ParentInfo> ParentContexts;
434+
/// For calls this lists the USRs of the receiver types (multiple only in the
435+
/// case that the base is a protocol composition).
436+
ArrayRef<StringRef> ReceiverUSRs;
434437

435438
bool IsSystem = false;
439+
bool IsDynamic = false;
440+
436441
llvm::Optional<unsigned> ParentNameOffset;
437442
};
438443

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,6 @@ static bool passCursorInfoForDecl(SourceFile* SF,
834834
}
835835
unsigned DeclEnd = SS.size();
836836

837-
838837
SmallVector<symbolgraphgen::PathComponent, 4> PathComponents;
839838
unsigned SymbolGraphBegin = SS.size();
840839
if (SymbolGraph) {
@@ -949,6 +948,14 @@ static bool passCursorInfoForDecl(SourceFile* SF,
949948
RelDeclsStream.endPiece();
950949
});
951950

951+
DelayedStringRetriever ReceiverUSRsStream(SS);
952+
for (auto *ReceiverTy : TheTok.ReceiverTypes) {
953+
ReceiverUSRsStream.startPiece();
954+
if (SwiftLangSupport::printUSR(ReceiverTy, OS))
955+
ReceiverUSRsStream.startPiece();
956+
ReceiverUSRsStream.endPiece();
957+
}
958+
952959
ASTContext &Ctx = VD->getASTContext();
953960

954961
ClangImporter *Importer = static_cast<ClangImporter*>(
@@ -1026,6 +1033,9 @@ static bool passCursorInfoForDecl(SourceFile* SF,
10261033
SmallVector<StringRef, 4> AnnotatedRelatedDecls;
10271034
RelDeclsStream.retrieve([&](StringRef S) { AnnotatedRelatedDecls.push_back(S); });
10281035

1036+
SmallVector<StringRef, 4> ReceiverUSRs;
1037+
ReceiverUSRsStream.retrieve([&](StringRef S) { ReceiverUSRs.push_back(S); });
1038+
10291039
SmallVector<RefactoringInfo, 4> RefactoringInfoBuffer;
10301040
for (unsigned I = 0, N = RefactoringIds.size(); I < N; I ++) {
10311041
RefactoringInfoBuffer.push_back({RefactoringIds[I], RefactoringNameOS[I],
@@ -1065,6 +1075,8 @@ static bool passCursorInfoForDecl(SourceFile* SF,
10651075
Info.ParentNameOffset = getParamParentNameOffset(VD, CursorLoc);
10661076
Info.SymbolGraph = SymbolGraphJSON;
10671077
Info.ParentContexts = llvm::makeArrayRef(Parents);
1078+
Info.IsDynamic = TheTok.IsDynamic;
1079+
Info.ReceiverUSRs = ReceiverUSRs;
10681080
Receiver(RequestResult<CursorInfoData>::fromResult(Info));
10691081
return true;
10701082
}

0 commit comments

Comments
 (0)