Skip to content

Commit 35ae1fc

Browse files
committed
Method call resolution in QL
1 parent 1ca1bc3 commit 35ae1fc

6 files changed

Lines changed: 292 additions & 55 deletions

File tree

swift/ql/lib/codeql/swift/typeinference/TypeInference.qll

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ import M2
123123
private module Input3 implements InputSig3 {
124124
private import swift as Swift
125125

126-
predicate cachedStageRevRef() { none() }
126+
predicate cachedStageRevRef() { MethodCallResolution::resolveMethodCall(_, _) implies any() }
127127

128128
predicate inferType = M3::inferType/2;
129129

@@ -135,6 +135,8 @@ private module Input3 implements InputSig3 {
135135

136136
TypeMention getTypeAnnotation(AstNode n) {
137137
result.(ParamDeclTypeMention).getDecl() = n
138+
or
139+
result.(TypedPatternTypeMention).getPattern() = n
138140
// todo: more cases, like local variables with explicit type annotations and casts
139141
}
140142

@@ -264,9 +266,8 @@ private module Input3 implements InputSig3 {
264266
Callable getTargetCertain() { none() }
265267

266268
Callable getTarget(CallResolutionContext ctx) {
267-
// todo: implement in QL
268-
result = this.getStaticTarget() and
269-
exists(ctx)
269+
exists(ctx) and
270+
result = getCallTarget(this)
270271
}
271272
}
272273

@@ -278,8 +279,13 @@ private module Input3 implements InputSig3 {
278279
result = M3::inferCallArgumentTypeTopDown(_, _, _, n, path)
279280
}
280281

281-
predicate inferStepSymmetric(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) {
282-
none()
282+
predicate inferStepSymmetric(AstNode n1, TypePath path1, AstNode n2, TypePath path2) {
283+
path1.isEmpty() and
284+
path2.isEmpty() and
285+
exists(Variable v |
286+
n1 = v.getParentPattern() and
287+
n2 = v.getDefiningNode()
288+
)
283289
}
284290

285291
predicate inferStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) {
@@ -318,6 +324,68 @@ predicate inferType = M3::inferType/2;
318324

319325
predicate inferTypeCertain = M3::inferTypeCertain/2;
320326

327+
private module MethodCallResolution {
328+
private class MethodCall extends MethodCallExpr {
329+
pragma[nomagic]
330+
predicate hasInfo(string name, int arity) {
331+
name =
332+
this.getFunction()
333+
.(MethodLookupExpr)
334+
.getMethodRef()
335+
.(DeclRefExpr)
336+
.getDecl()
337+
.(Method)
338+
.getName() and
339+
arity = this.getNumberOfArguments()
340+
}
341+
342+
Type getTypeAt(TypePath path) { result = inferType(this.getQualifier(), path) }
343+
}
344+
345+
pragma[nomagic]
346+
private predicate methodInfo(Method m, string name, int arity, ParamDeclTypeMention selfType) {
347+
name = m.getName() and
348+
arity = m.getNumberOfParams() and
349+
selfType.getDecl() = m.getSelfParam()
350+
}
351+
352+
private module MethodCallQualifierSatisfiesSelfTypeInput implements
353+
SatisfiesConstraintInputSig<MethodCall, TypeMention>
354+
{
355+
pragma[nomagic]
356+
predicate relevantConstraint(MethodCall call, TypeMention constraint) {
357+
exists(string name, int arity |
358+
call.hasInfo(name, arity) and
359+
methodInfo(_, name, arity, constraint)
360+
)
361+
}
362+
}
363+
364+
private module MethodCallQualifierSatisfiesSelfType =
365+
SatisfiesConstraint<MethodCall, TypeMention, MethodCallQualifierSatisfiesSelfTypeInput>;
366+
367+
cached
368+
predicate resolveMethodCall(MethodCallExpr call, Method m) {
369+
M3::CachedStage::ref() and
370+
exists(ParamDeclTypeMention selfType |
371+
MethodCallQualifierSatisfiesSelfType::satisfiesConstraint(call, selfType, _, _) and
372+
methodInfo(m, _, _, selfType)
373+
)
374+
}
375+
}
376+
377+
Callable getCallTarget(CallExpr call) {
378+
MethodCallResolution::resolveMethodCall(call, result)
379+
or
380+
// todo: implement in QL
381+
result = call.getStaticTarget() and
382+
(
383+
not call instanceof MethodCallExpr
384+
or
385+
result instanceof Initializer
386+
)
387+
}
388+
321389
module Consistency {
322390
import M2::Consistency
323391
}

swift/ql/lib/codeql/swift/typeinference/TypeMention.qll

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ newtype TTypeMention =
4545
TTypeDeclBaseTypeMention(TypeDecl decl, int i) { exists(decl.getInheritedType(i)) } or
4646
TExtensionDeclBaseTypeMention(ExtensionDecl decl, int i) { exists(decl.getProtocol(i)) } or
4747
TParamDeclTypeMention(ParamDecl decl) or
48-
TCallableReturnTypeMention(Callable c)
48+
TCallableReturnTypeMention(Callable c) or
49+
TTypedPatternTypeMention(TypedPattern p)
4950

5051
/** An AST node that may mention a type. */
5152
abstract private class TypeMentionImpl extends TTypeMention {
@@ -164,3 +165,17 @@ class CallableReturnTypeMention extends TypeMentionImpl, TCallableReturnTypeMent
164165

165166
override Location getLocation() { result = c.getLocation() }
166167
}
168+
169+
class TypedPatternTypeMention extends TypeMentionImpl, TTypedPatternTypeMention {
170+
TypedPattern p;
171+
172+
TypedPatternTypeMention() { this = TTypedPatternTypeMention(p) }
173+
174+
TypedPattern getPattern() { result = p }
175+
176+
override Type getTypeAt(TypePath path) { result = getTypeAt(p.getType(), path) }
177+
178+
override string toString() { result = p.toString() + " [pattern type]" }
179+
180+
override Location getLocation() { result = p.getLocation() }
181+
}

swift/ql/test/library-tests/type-inference/Common.qll

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,12 @@ private SingleLineComment getPrecedingComment(Decl d) {
2020
)
2121
}
2222

23-
module ResolveTest implements TestSig {
24-
string getARelevantTag() { result = "target" }
25-
26-
private predicate declHasName(Decl c, string value) {
27-
exists(string s |
28-
s = getPrecedingComment(c).getText() and
29-
value = s.substring(3, s.length() - 1)
30-
)
31-
or
32-
not exists(getPrecedingComment(c)) and
33-
value = [c.(EnumElementDecl).getName(), c.(Function).getName()]
34-
}
35-
36-
predicate hasActualResult(Location location, string element, string tag, string value) {
37-
exists(AstNode source, Decl target |
38-
location = source.getLocation() and
39-
element = source.toString() and
40-
target =
41-
[
42-
source.(CallExpr).getStaticTarget().(Decl), source.(MethodLookupExpr).getMember(),
43-
source.(EnumElementPattern).getElement()
44-
] and
45-
declHasName(target, value) and
46-
tag = "target" and
47-
toBeTested(source) and
48-
toBeTested(target)
49-
)
50-
}
23+
predicate declHasName(Decl c, string value) {
24+
exists(string s |
25+
s = getPrecedingComment(c).getText() and
26+
value = s.substring(3, s.length() - 1)
27+
)
28+
or
29+
not exists(getPrecedingComment(c)) and
30+
value = [c.(EnumElementDecl).getName(), c.(Function).getName()]
5131
}

0 commit comments

Comments
 (0)