Skip to content

Commit ecf1d98

Browse files
authored
Merge pull request github#14165 from rdmarsh2/rdmarsh2/swift/keypath-write-flow
Swift: flow through writeable keypaths
2 parents 0d7769f + c2868fe commit ecf1d98

File tree

5 files changed

+178
-2
lines changed

5 files changed

+178
-2
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
5+
* Flow through writes via keypaths is now supported by the data flow library.

swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,28 @@ private class KeyPathComponentNodeImpl extends TKeyPathComponentNode, NodeImpl {
5858
KeyPathComponent getComponent() { result = component }
5959
}
6060

61+
private class KeyPathComponentPostUpdateNode extends TKeyPathComponentPostUpdateNode, NodeImpl,
62+
PostUpdateNodeImpl
63+
{
64+
KeyPathComponent component;
65+
66+
KeyPathComponentPostUpdateNode() { this = TKeyPathComponentPostUpdateNode(component) }
67+
68+
override Location getLocationImpl() { result = component.getLocation() }
69+
70+
override string toStringImpl() { result = "[post] " + component.toString() }
71+
72+
override DataFlowCallable getEnclosingCallable() {
73+
result.asSourceCallable() = component.getKeyPathExpr()
74+
}
75+
76+
override KeyPathComponentNodeImpl getPreUpdateNode() {
77+
result.getComponent() = this.getComponent()
78+
}
79+
80+
KeyPathComponent getComponent() { result = component }
81+
}
82+
6183
private class PatternNodeImpl extends PatternNode, NodeImpl {
6284
override Location getLocationImpl() { result = pattern.getLocation() }
6385

@@ -97,6 +119,9 @@ private module Cached {
97119
TKeyPathParameterNode(EntryNode entry) { entry.getScope() instanceof KeyPathExpr } or
98120
TKeyPathReturnNode(ExitNode exit) { exit.getScope() instanceof KeyPathExpr } or
99121
TKeyPathComponentNode(KeyPathComponent component) or
122+
TKeyPathParameterPostUpdateNode(EntryNode entry) { entry.getScope() instanceof KeyPathExpr } or
123+
TKeyPathReturnPostUpdateNode(ExitNode exit) { exit.getScope() instanceof KeyPathExpr } or
124+
TKeyPathComponentPostUpdateNode(KeyPathComponent component) or
100125
TExprPostUpdateNode(CfgNode n) {
101126
// Obviously, the base of setters needs a post-update node
102127
n = any(PropertySetterCfgNode setter).getBase()
@@ -106,6 +131,8 @@ private module Cached {
106131
or
107132
n = any(PropertyObserverCfgNode getter).getBase()
108133
or
134+
n = any(KeyPathApplicationExprCfgNode expr).getBase()
135+
or
109136
// Arguments that are `inout` expressions needs a post-update node,
110137
// as well as any class-like argument (since a field can be modified).
111138
// Finally, qualifiers and bases of member reference need post-update nodes to support reverse reads.
@@ -231,6 +258,16 @@ private module Cached {
231258
nodeTo.(KeyPathComponentNodeImpl).getComponent() =
232259
nodeFrom.(KeyPathParameterNode).getComponent(0)
233260
or
261+
nodeFrom.(KeyPathComponentPostUpdateNode).getComponent() =
262+
nodeTo.(KeyPathParameterPostUpdateNode).getComponent(0)
263+
or
264+
// Flow to the result of a keypath assignment
265+
exists(KeyPathApplicationExpr apply, AssignExpr assign |
266+
apply = assign.getDest() and
267+
nodeTo.asExpr() = apply and
268+
nodeFrom.asExpr() = assign.getSource()
269+
)
270+
or
234271
// flow through a flow summary (extension of `SummaryModelCsv`)
235272
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
236273
nodeTo.(FlowSummaryNode).getSummaryNode(), true)
@@ -409,6 +446,56 @@ class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
409446
override string toStringImpl() { result = this.getSummaryNode().toString() }
410447
}
411448

449+
class KeyPathParameterPostUpdateNode extends NodeImpl, ReturnNode, PostUpdateNodeImpl,
450+
TKeyPathParameterPostUpdateNode
451+
{
452+
private EntryNode entry;
453+
454+
KeyPathParameterPostUpdateNode() { this = TKeyPathParameterPostUpdateNode(entry) }
455+
456+
override KeyPathParameterNode getPreUpdateNode() {
457+
result.getKeyPathExpr() = this.getKeyPathExpr()
458+
}
459+
460+
override Location getLocationImpl() { result = entry.getLocation() }
461+
462+
override string toStringImpl() { result = "[post] " + entry.toString() }
463+
464+
override DataFlowCallable getEnclosingCallable() { result.asSourceCallable() = entry.getScope() }
465+
466+
KeyPathComponent getComponent(int i) { result = entry.getScope().(KeyPathExpr).getComponent(i) }
467+
468+
KeyPathComponent getAComponent() { result = this.getComponent(_) }
469+
470+
KeyPathExpr getKeyPathExpr() { result = entry.getScope() }
471+
472+
override ReturnKind getKind() { result.(ParamReturnKind).getIndex() = -1 }
473+
}
474+
475+
class KeyPathReturnPostUpdateNode extends NodeImpl, ParameterNodeImpl, PostUpdateNodeImpl,
476+
TKeyPathReturnPostUpdateNode
477+
{
478+
private ExitNode exit;
479+
480+
KeyPathReturnPostUpdateNode() { this = TKeyPathReturnPostUpdateNode(exit) }
481+
482+
override KeyPathReturnNodeImpl getPreUpdateNode() {
483+
result.getKeyPathExpr() = this.getKeyPathExpr()
484+
}
485+
486+
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
487+
c.asSourceCallable() = this.getKeyPathExpr() and pos = TPositionalParameter(0)
488+
}
489+
490+
override Location getLocationImpl() { result = exit.getLocation() }
491+
492+
override string toStringImpl() { result = "[post] " + exit.toString() }
493+
494+
override DataFlowCallable getEnclosingCallable() { result.asSourceCallable() = exit.getScope() }
495+
496+
KeyPathExpr getKeyPathExpr() { result = exit.getScope() }
497+
}
498+
412499
/** A data-flow node that represents a call argument. */
413500
abstract class ArgumentNode extends Node {
414501
/** Holds if this argument occurs at the given position in the given call. */
@@ -502,6 +589,20 @@ private module ArgumentNodes {
502589
pos = TThisArgument()
503590
}
504591
}
592+
593+
class KeyPathAssignmentArgumentNode extends ArgumentNode {
594+
private KeyPathApplicationExprCfgNode keyPath;
595+
596+
KeyPathAssignmentArgumentNode() {
597+
keyPath = this.getCfgNode() and
598+
exists(AssignExpr assign | assign.getDest() = keyPath.getNode().asAstNode())
599+
}
600+
601+
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
602+
call.asKeyPath() = keyPath and
603+
pos = TPositionalArgument(0)
604+
}
605+
}
505606
}
506607

507608
import ArgumentNodes

swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
edges
22
| file://:0:0:0:0 | KeyPathComponent | test.swift:663:13:663:29 | exit #keyPath(...) [some:0] |
33
| file://:0:0:0:0 | self [a, x] | file://:0:0:0:0 | .a [x] |
4+
| file://:0:0:0:0 | self [s, x] | file://:0:0:0:0 | .s [x] |
45
| file://:0:0:0:0 | self [str] | file://:0:0:0:0 | .str |
56
| file://:0:0:0:0 | self [v2, some:0] | file://:0:0:0:0 | .v2 [some:0] |
67
| file://:0:0:0:0 | self [v2] | file://:0:0:0:0 | .v2 |
78
| file://:0:0:0:0 | self [v3] | file://:0:0:0:0 | .v3 |
89
| file://:0:0:0:0 | self [v] | file://:0:0:0:0 | .v |
910
| file://:0:0:0:0 | self [x, some:0] | file://:0:0:0:0 | .x [some:0] |
1011
| file://:0:0:0:0 | self [x] | file://:0:0:0:0 | .x |
12+
| file://:0:0:0:0 | self [x] | file://:0:0:0:0 | .x |
1113
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [v2] |
1214
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [v3] |
1315
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [v] |
@@ -301,6 +303,7 @@ edges
301303
| test.swift:599:24:599:32 | call to source3() | test.swift:599:13:599:33 | call to MyClass.init(s:) [str] |
302304
| test.swift:600:13:600:41 | call to MyClass.init(contentsOfFile:) [str] | test.swift:585:9:585:9 | self [str] |
303305
| test.swift:600:13:600:41 | call to MyClass.init(contentsOfFile:) [str] | test.swift:600:13:600:43 | .str |
306+
| test.swift:615:7:615:7 | self [x] | file://:0:0:0:0 | self [x] |
304307
| test.swift:617:8:617:11 | x | test.swift:618:14:618:14 | x |
305308
| test.swift:618:5:618:5 | [post] self [x] | test.swift:617:3:619:3 | self[return] [x] |
306309
| test.swift:618:14:618:14 | x | test.swift:618:5:618:5 | [post] self [x] |
@@ -316,6 +319,7 @@ edges
316319
| test.swift:627:38:627:38 | KeyPathComponent [x] | test.swift:627:36:627:38 | exit #keyPath(...) |
317320
| test.swift:628:13:628:13 | s [x] | test.swift:627:36:627:38 | enter #keyPath(...) [x] |
318321
| test.swift:628:13:628:13 | s [x] | test.swift:628:13:628:32 | \\...[...] |
322+
| test.swift:632:7:632:7 | self [s, x] | file://:0:0:0:0 | self [s, x] |
319323
| test.swift:634:8:634:11 | s [x] | test.swift:635:14:635:14 | s [x] |
320324
| test.swift:635:5:635:5 | [post] self [s, x] | test.swift:634:3:636:3 | self[return] [s, x] |
321325
| test.swift:635:14:635:14 | s [x] | test.swift:635:5:635:5 | [post] self [s, x] |
@@ -507,14 +511,27 @@ edges
507511
| test.swift:831:15:831:15 | s2 [v] | test.swift:831:15:831:18 | .v |
508512
| test.swift:833:15:833:15 | s2 [v] | test.swift:813:8:813:8 | self [v] |
509513
| test.swift:833:15:833:15 | s2 [v] | test.swift:833:15:833:23 | call to getv() |
514+
| test.swift:839:11:839:17 | [post] exit #keyPath(...) | test.swift:839:17:839:17 | [post] KeyPathComponent [x] |
515+
| test.swift:839:15:839:15 | [post] KeyPathComponent [s, x] | test.swift:839:11:839:17 | [post] enter #keyPath(...) [s, x] |
516+
| test.swift:839:17:839:17 | [post] KeyPathComponent [x] | test.swift:839:15:839:15 | [post] KeyPathComponent [s, x] |
517+
| test.swift:840:3:840:3 | [post] s2 [s, x] | test.swift:841:13:841:13 | s2 [s, x] |
518+
| test.swift:840:3:840:16 | \\...[...] | test.swift:839:11:839:17 | [post] exit #keyPath(...) |
519+
| test.swift:840:3:840:16 | \\...[...] | test.swift:840:3:840:3 | [post] s2 [s, x] |
520+
| test.swift:840:20:840:27 | call to source() | test.swift:840:3:840:16 | \\...[...] |
521+
| test.swift:841:13:841:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] |
522+
| test.swift:841:13:841:13 | s2 [s, x] | test.swift:841:13:841:16 | .s [x] |
523+
| test.swift:841:13:841:16 | .s [x] | test.swift:615:7:615:7 | self [x] |
524+
| test.swift:841:13:841:16 | .s [x] | test.swift:841:13:841:18 | .x |
510525
nodes
511526
| file://:0:0:0:0 | .a [x] | semmle.label | .a [x] |
527+
| file://:0:0:0:0 | .s [x] | semmle.label | .s [x] |
512528
| file://:0:0:0:0 | .str | semmle.label | .str |
513529
| file://:0:0:0:0 | .v | semmle.label | .v |
514530
| file://:0:0:0:0 | .v2 | semmle.label | .v2 |
515531
| file://:0:0:0:0 | .v2 [some:0] | semmle.label | .v2 [some:0] |
516532
| file://:0:0:0:0 | .v3 | semmle.label | .v3 |
517533
| file://:0:0:0:0 | .x | semmle.label | .x |
534+
| file://:0:0:0:0 | .x | semmle.label | .x |
518535
| file://:0:0:0:0 | .x [some:0] | semmle.label | .x [some:0] |
519536
| file://:0:0:0:0 | KeyPathComponent | semmle.label | KeyPathComponent |
520537
| file://:0:0:0:0 | [post] self [v2, some:0] | semmle.label | [post] self [v2, some:0] |
@@ -524,13 +541,15 @@ nodes
524541
| file://:0:0:0:0 | [post] self [x, some:0] | semmle.label | [post] self [x, some:0] |
525542
| file://:0:0:0:0 | [post] self [x] | semmle.label | [post] self [x] |
526543
| file://:0:0:0:0 | self [a, x] | semmle.label | self [a, x] |
544+
| file://:0:0:0:0 | self [s, x] | semmle.label | self [s, x] |
527545
| file://:0:0:0:0 | self [str] | semmle.label | self [str] |
528546
| file://:0:0:0:0 | self [v2, some:0] | semmle.label | self [v2, some:0] |
529547
| file://:0:0:0:0 | self [v2] | semmle.label | self [v2] |
530548
| file://:0:0:0:0 | self [v3] | semmle.label | self [v3] |
531549
| file://:0:0:0:0 | self [v] | semmle.label | self [v] |
532550
| file://:0:0:0:0 | self [x, some:0] | semmle.label | self [x, some:0] |
533551
| file://:0:0:0:0 | self [x] | semmle.label | self [x] |
552+
| file://:0:0:0:0 | self [x] | semmle.label | self [x] |
534553
| file://:0:0:0:0 | value | semmle.label | value |
535554
| file://:0:0:0:0 | value | semmle.label | value |
536555
| file://:0:0:0:0 | value | semmle.label | value |
@@ -842,6 +861,7 @@ nodes
842861
| test.swift:599:24:599:32 | call to source3() | semmle.label | call to source3() |
843862
| test.swift:600:13:600:41 | call to MyClass.init(contentsOfFile:) [str] | semmle.label | call to MyClass.init(contentsOfFile:) [str] |
844863
| test.swift:600:13:600:43 | .str | semmle.label | .str |
864+
| test.swift:615:7:615:7 | self [x] | semmle.label | self [x] |
845865
| test.swift:617:3:619:3 | self[return] [x] | semmle.label | self[return] [x] |
846866
| test.swift:617:8:617:11 | x | semmle.label | x |
847867
| test.swift:618:5:618:5 | [post] self [x] | semmle.label | [post] self [x] |
@@ -858,6 +878,7 @@ nodes
858878
| test.swift:627:38:627:38 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] |
859879
| test.swift:628:13:628:13 | s [x] | semmle.label | s [x] |
860880
| test.swift:628:13:628:32 | \\...[...] | semmle.label | \\...[...] |
881+
| test.swift:632:7:632:7 | self [s, x] | semmle.label | self [s, x] |
861882
| test.swift:634:3:636:3 | self[return] [s, x] | semmle.label | self[return] [s, x] |
862883
| test.swift:634:8:634:11 | s [x] | semmle.label | s [x] |
863884
| test.swift:635:5:635:5 | [post] self [s, x] | semmle.label | [post] self [s, x] |
@@ -1056,6 +1077,16 @@ nodes
10561077
| test.swift:831:15:831:18 | .v | semmle.label | .v |
10571078
| test.swift:833:15:833:15 | s2 [v] | semmle.label | s2 [v] |
10581079
| test.swift:833:15:833:23 | call to getv() | semmle.label | call to getv() |
1080+
| test.swift:839:11:839:17 | [post] enter #keyPath(...) [s, x] | semmle.label | [post] enter #keyPath(...) [s, x] |
1081+
| test.swift:839:11:839:17 | [post] exit #keyPath(...) | semmle.label | [post] exit #keyPath(...) |
1082+
| test.swift:839:15:839:15 | [post] KeyPathComponent [s, x] | semmle.label | [post] KeyPathComponent [s, x] |
1083+
| test.swift:839:17:839:17 | [post] KeyPathComponent [x] | semmle.label | [post] KeyPathComponent [x] |
1084+
| test.swift:840:3:840:3 | [post] s2 [s, x] | semmle.label | [post] s2 [s, x] |
1085+
| test.swift:840:3:840:16 | \\...[...] | semmle.label | \\...[...] |
1086+
| test.swift:840:20:840:27 | call to source() | semmle.label | call to source() |
1087+
| test.swift:841:13:841:13 | s2 [s, x] | semmle.label | s2 [s, x] |
1088+
| test.swift:841:13:841:16 | .s [x] | semmle.label | .s [x] |
1089+
| test.swift:841:13:841:18 | .x | semmle.label | .x |
10591090
subpaths
10601091
| test.swift:75:22:75:22 | x | test.swift:65:16:65:28 | arg1 | test.swift:65:1:70:1 | arg2[return] | test.swift:75:32:75:32 | [post] y |
10611092
| test.swift:114:19:114:19 | arg | test.swift:109:9:109:14 | arg | test.swift:110:12:110:12 | arg | test.swift:114:12:114:22 | call to ... |
@@ -1117,6 +1148,10 @@ subpaths
11171148
| test.swift:828:12:828:19 | call to source() | test.swift:815:7:815:7 | value | file://:0:0:0:0 | [post] self [v] | test.swift:828:5:828:5 | [post] s2 [v] |
11181149
| test.swift:831:15:831:15 | s2 [v] | test.swift:815:7:815:7 | self [v] | file://:0:0:0:0 | .v | test.swift:831:15:831:18 | .v |
11191150
| test.swift:833:15:833:15 | s2 [v] | test.swift:813:8:813:8 | self [v] | test.swift:813:31:813:31 | .v | test.swift:833:15:833:23 | call to getv() |
1151+
| test.swift:840:3:840:16 | \\...[...] | test.swift:839:11:839:17 | [post] exit #keyPath(...) | test.swift:839:11:839:17 | [post] enter #keyPath(...) [s, x] | test.swift:840:3:840:3 | [post] s2 [s, x] |
1152+
| test.swift:840:3:840:16 | \\...[...] | test.swift:839:11:839:17 | [post] exit #keyPath(...) | test.swift:839:15:839:15 | [post] KeyPathComponent [s, x] | test.swift:840:3:840:3 | [post] s2 [s, x] |
1153+
| test.swift:841:13:841:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] | file://:0:0:0:0 | .s [x] | test.swift:841:13:841:16 | .s [x] |
1154+
| test.swift:841:13:841:16 | .s [x] | test.swift:615:7:615:7 | self [x] | file://:0:0:0:0 | .x | test.swift:841:13:841:18 | .x |
11201155
#select
11211156
| test.swift:7:15:7:15 | t1 | test.swift:6:19:6:26 | call to source() | test.swift:7:15:7:15 | t1 | result |
11221157
| test.swift:9:15:9:15 | t1 | test.swift:6:19:6:26 | call to source() | test.swift:9:15:9:15 | t1 | result |
@@ -1231,3 +1266,4 @@ subpaths
12311266
| test.swift:824:15:824:23 | call to getv() | test.swift:819:17:819:24 | call to source() | test.swift:824:15:824:23 | call to getv() | result |
12321267
| test.swift:831:15:831:18 | .v | test.swift:828:12:828:19 | call to source() | test.swift:831:15:831:18 | .v | result |
12331268
| test.swift:833:15:833:23 | call to getv() | test.swift:828:12:828:19 | call to source() | test.swift:833:15:833:23 | call to getv() | result |
1269+
| test.swift:841:13:841:18 | .x | test.swift:840:20:840:27 | call to source() | test.swift:841:13:841:18 | .x | result |

0 commit comments

Comments
 (0)