Swift: flow through writeable keypaths

This commit is contained in:
Robert Marsh
2023-09-07 15:56:44 +00:00
parent 12a717e3af
commit 5fe942e642
4 changed files with 185 additions and 2 deletions

View File

@@ -57,6 +57,22 @@ private class KeyPathComponentNodeImpl extends TKeyPathComponentNode, NodeImpl {
KeyPathComponent getComponent() { result = component }
}
private class KeyPathComponentPostUpdateNode extends TKeyPathComponentPostUpdateNode, NodeImpl {
KeyPathComponent component;
KeyPathComponentPostUpdateNode() { this = TKeyPathComponentPostUpdateNode(component) }
override Location getLocationImpl() { result = component.getLocation() }
override string toStringImpl() { result = "[post] " + component.toString() }
override DataFlowCallable getEnclosingCallable() {
result.asSourceCallable() = component.getKeyPathExpr()
}
KeyPathComponent getComponent() { result = component }
}
private class PatternNodeImpl extends PatternNode, NodeImpl {
override Location getLocationImpl() { result = pattern.getLocation() }
@@ -96,6 +112,9 @@ private module Cached {
TKeyPathParameterNode(EntryNode entry) { entry.getScope() instanceof KeyPathExpr } or
TKeyPathReturnNode(ExitNode exit) { exit.getScope() instanceof KeyPathExpr } or
TKeyPathComponentNode(KeyPathComponent component) or
TKeyPathParameterPostUpdateNode(EntryNode entry) { entry.getScope() instanceof KeyPathExpr } or
TKeyPathReturnPostUpdateNode(ExitNode exit) { exit.getScope() instanceof KeyPathExpr } or
TKeyPathComponentPostUpdateNode(KeyPathComponent component) or
TExprPostUpdateNode(CfgNode n) {
// Obviously, the base of setters needs a post-update node
n = any(PropertySetterCfgNode setter).getBase()
@@ -105,6 +124,8 @@ private module Cached {
or
n = any(PropertyObserverCfgNode getter).getBase()
or
n = any(KeyPathApplicationExprCfgNode expr).getBase()
or
// Arguments that are `inout` expressions needs a post-update node,
// as well as any class-like argument (since a field can be modified).
// Finally, qualifiers and bases of member reference need post-update nodes to support reverse reads.
@@ -227,6 +248,17 @@ private module Cached {
nodeTo.(KeyPathComponentNodeImpl).getComponent() =
nodeFrom.(KeyPathParameterNode).getComponent(0)
or
nodeFrom.(KeyPathComponentPostUpdateNode).getComponent() =
nodeTo.(KeyPathParameterPostUpdateNode).getComponent(0)
or
// Flow to the result of a keypath assignment
// TODO: is there a cleaner way to do this?
exists(KeyPathApplicationExpr apply, AssignExpr assign |
apply = assign.getDest() and
nodeTo.asExpr() = apply and
nodeFrom.asExpr() = assign.getSource()
)
or
// flow through a flow summary (extension of `SummaryModelCsv`)
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
nodeTo.(FlowSummaryNode).getSummaryNode(), true)
@@ -383,6 +415,56 @@ class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
override string toStringImpl() { result = this.getSummaryNode().toString() }
}
class KeyPathParameterPostUpdateNode extends NodeImpl, ReturnNode, PostUpdateNodeImpl,
TKeyPathParameterPostUpdateNode
{
private EntryNode entry;
KeyPathParameterPostUpdateNode() { this = TKeyPathParameterPostUpdateNode(entry) }
override KeyPathParameterNode getPreUpdateNode() {
result.getKeyPathExpr() = this.getKeyPathExpr()
}
override Location getLocationImpl() { result = entry.getLocation() }
override string toStringImpl() { result = "[post] " + entry.toString() }
override DataFlowCallable getEnclosingCallable() { result.asSourceCallable() = entry.getScope() }
KeyPathComponent getComponent(int i) { result = entry.getScope().(KeyPathExpr).getComponent(i) }
KeyPathComponent getAComponent() { result = this.getComponent(_) }
KeyPathExpr getKeyPathExpr() { result = entry.getScope() }
override ReturnKind getKind() { result.(ParamReturnKind).getIndex() = -1 }
}
class KeyPathReturnPostUpdateNode extends NodeImpl, ParameterNodeImpl, PostUpdateNodeImpl,
TKeyPathReturnPostUpdateNode
{
private ExitNode exit;
KeyPathReturnPostUpdateNode() { this = TKeyPathReturnPostUpdateNode(exit) }
override KeyPathParameterNode getPreUpdateNode() {
result.getKeyPathExpr() = this.getKeyPathExpr()
}
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
c.asSourceCallable() = this.getKeyPathExpr() and pos = TPositionalParameter(0) // TODO: new parameter type?
}
override Location getLocationImpl() { result = exit.getLocation() }
override string toStringImpl() { result = "[post] " + exit.toString() }
override DataFlowCallable getEnclosingCallable() { result.asSourceCallable() = exit.getScope() }
KeyPathExpr getKeyPathExpr() { result = exit.getScope() }
}
/** A data-flow node that represents a call argument. */
abstract class ArgumentNode extends Node {
/** Holds if this argument occurs at the given position in the given call. */
@@ -476,6 +558,20 @@ private module ArgumentNodes {
pos = TThisArgument()
}
}
class KeyPathAssignmentArgumentNode extends ArgumentNode {
private KeyPathApplicationExprCfgNode keyPath;
KeyPathAssignmentArgumentNode() {
keyPath = this.getCfgNode() and
exists(AssignExpr assign | assign.getDest() = keyPath.getNode().asAstNode())
}
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
call.asKeyPath() = keyPath and
pos = TPositionalArgument(0) // TODO: new parameter type?
}
}
}
import ArgumentNodes
@@ -735,6 +831,24 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
c instanceof OptionalSomeContentSet
)
or
// store through a component in a key-path expression chain
exists(KeyPathComponent component |
component = node2.(KeyPathComponentPostUpdateNode).getComponent() and
(
c.isSingleton(any(Content::FieldContent ct | ct.getField() = component.getDeclRef()))
or
c.isSingleton(any(Content::ArrayContent ac)) and
component.isSubscript()
)
|
// the previous node is either the next element in the chain
node1.(KeyPathComponentPostUpdateNode).getComponent() = component.getNextComponent()
or
// or the return node, if this is the last component in the chain
not exists(component.getNextComponent()) and
node1.(KeyPathReturnPostUpdateNode).getKeyPathExpr() = component.getKeyPathExpr()
)
or
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
}

View File

@@ -1,12 +1,14 @@
edges
| file://:0:0:0:0 | KeyPathComponent | test.swift:663:13:663:29 | exit #keyPath(...) [some:0] |
| file://:0:0:0:0 | self [a, x] | file://:0:0:0:0 | .a [x] |
| file://:0:0:0:0 | self [s, x] | file://:0:0:0:0 | .s [x] |
| file://:0:0:0:0 | self [str] | file://:0:0:0:0 | .str |
| file://:0:0:0:0 | self [v2, some:0] | file://:0:0:0:0 | .v2 [some:0] |
| file://:0:0:0:0 | self [v2] | file://:0:0:0:0 | .v2 |
| file://:0:0:0:0 | self [v3] | file://:0:0:0:0 | .v3 |
| file://:0:0:0:0 | self [x, some:0] | file://:0:0:0:0 | .x [some:0] |
| file://:0:0:0:0 | self [x] | file://:0:0:0:0 | .x |
| file://:0:0:0:0 | self [x] | file://:0:0:0:0 | .x |
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [v2] |
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [v3] |
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [x] |
@@ -299,6 +301,7 @@ edges
| test.swift:599:24:599:32 | call to source3() | test.swift:599:13:599:33 | call to MyClass.init(s:) [str] |
| test.swift:600:13:600:41 | call to MyClass.init(contentsOfFile:) [str] | test.swift:585:9:585:9 | self [str] |
| test.swift:600:13:600:41 | call to MyClass.init(contentsOfFile:) [str] | test.swift:600:13:600:43 | .str |
| test.swift:615:7:615:7 | self [x] | file://:0:0:0:0 | self [x] |
| test.swift:617:8:617:11 | x | test.swift:618:14:618:14 | x |
| test.swift:618:5:618:5 | [post] self [x] | test.swift:617:3:619:3 | self[return] [x] |
| test.swift:618:14:618:14 | x | test.swift:618:5:618:5 | [post] self [x] |
@@ -314,6 +317,7 @@ edges
| test.swift:627:38:627:38 | KeyPathComponent [x] | test.swift:627:36:627:38 | exit #keyPath(...) |
| test.swift:628:13:628:13 | s [x] | test.swift:627:36:627:38 | enter #keyPath(...) [x] |
| test.swift:628:13:628:13 | s [x] | test.swift:628:13:628:32 | \\...[...] |
| test.swift:632:7:632:7 | self [s, x] | file://:0:0:0:0 | self [s, x] |
| test.swift:634:8:634:11 | s [x] | test.swift:635:14:635:14 | s [x] |
| test.swift:635:5:635:5 | [post] self [s, x] | test.swift:634:3:636:3 | self[return] [s, x] |
| test.swift:635:14:635:14 | s [x] | test.swift:635:5:635:5 | [post] self [s, x] |
@@ -438,13 +442,26 @@ edges
| test.swift:766:29:766:29 | KeyPathComponent [x] | test.swift:766:13:766:29 | exit #keyPath(...) |
| test.swift:767:15:767:15 | s2 [s, some:0, x] | test.swift:766:13:766:29 | enter #keyPath(...) [s, some:0, x] |
| test.swift:767:15:767:15 | s2 [s, some:0, x] | test.swift:767:15:767:28 | \\...[...] |
| test.swift:773:11:773:17 | [post] exit #keyPath(...) | test.swift:773:17:773:17 | [post] KeyPathComponent [x] |
| test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] | test.swift:773:11:773:17 | [post] enter #keyPath(...) [s, x] |
| test.swift:773:17:773:17 | [post] KeyPathComponent [x] | test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] |
| test.swift:774:3:774:3 | [post] s2 [s, x] | test.swift:775:13:775:13 | s2 [s, x] |
| test.swift:774:3:774:16 | \\...[...] | test.swift:773:11:773:17 | [post] exit #keyPath(...) |
| test.swift:774:3:774:16 | \\...[...] | test.swift:774:3:774:3 | [post] s2 [s, x] |
| test.swift:774:20:774:27 | call to source() | test.swift:774:3:774:16 | \\...[...] |
| test.swift:775:13:775:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] |
| test.swift:775:13:775:13 | s2 [s, x] | test.swift:775:13:775:16 | .s [x] |
| test.swift:775:13:775:16 | .s [x] | test.swift:615:7:615:7 | self [x] |
| test.swift:775:13:775:16 | .s [x] | test.swift:775:13:775:18 | .x |
nodes
| file://:0:0:0:0 | .a [x] | semmle.label | .a [x] |
| file://:0:0:0:0 | .s [x] | semmle.label | .s [x] |
| file://:0:0:0:0 | .str | semmle.label | .str |
| file://:0:0:0:0 | .v2 | semmle.label | .v2 |
| file://:0:0:0:0 | .v2 [some:0] | semmle.label | .v2 [some:0] |
| file://:0:0:0:0 | .v3 | semmle.label | .v3 |
| file://:0:0:0:0 | .x | semmle.label | .x |
| file://:0:0:0:0 | .x | semmle.label | .x |
| file://:0:0:0:0 | .x [some:0] | semmle.label | .x [some:0] |
| file://:0:0:0:0 | KeyPathComponent | semmle.label | KeyPathComponent |
| file://:0:0:0:0 | [post] self [v2, some:0] | semmle.label | [post] self [v2, some:0] |
@@ -453,12 +470,14 @@ nodes
| file://:0:0:0:0 | [post] self [x, some:0] | semmle.label | [post] self [x, some:0] |
| file://:0:0:0:0 | [post] self [x] | semmle.label | [post] self [x] |
| file://:0:0:0:0 | self [a, x] | semmle.label | self [a, x] |
| file://:0:0:0:0 | self [s, x] | semmle.label | self [s, x] |
| file://:0:0:0:0 | self [str] | semmle.label | self [str] |
| file://:0:0:0:0 | self [v2, some:0] | semmle.label | self [v2, some:0] |
| file://:0:0:0:0 | self [v2] | semmle.label | self [v2] |
| file://:0:0:0:0 | self [v3] | semmle.label | self [v3] |
| file://:0:0:0:0 | self [x, some:0] | semmle.label | self [x, some:0] |
| file://:0:0:0:0 | self [x] | semmle.label | self [x] |
| file://:0:0:0:0 | self [x] | semmle.label | self [x] |
| file://:0:0:0:0 | value | semmle.label | value |
| file://:0:0:0:0 | value | semmle.label | value |
| file://:0:0:0:0 | value | semmle.label | value |
@@ -769,6 +788,7 @@ nodes
| test.swift:599:24:599:32 | call to source3() | semmle.label | call to source3() |
| test.swift:600:13:600:41 | call to MyClass.init(contentsOfFile:) [str] | semmle.label | call to MyClass.init(contentsOfFile:) [str] |
| test.swift:600:13:600:43 | .str | semmle.label | .str |
| test.swift:615:7:615:7 | self [x] | semmle.label | self [x] |
| test.swift:617:3:619:3 | self[return] [x] | semmle.label | self[return] [x] |
| test.swift:617:8:617:11 | x | semmle.label | x |
| test.swift:618:5:618:5 | [post] self [x] | semmle.label | [post] self [x] |
@@ -785,6 +805,7 @@ nodes
| test.swift:627:38:627:38 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] |
| test.swift:628:13:628:13 | s [x] | semmle.label | s [x] |
| test.swift:628:13:628:32 | \\...[...] | semmle.label | \\...[...] |
| test.swift:632:7:632:7 | self [s, x] | semmle.label | self [s, x] |
| test.swift:634:3:636:3 | self[return] [s, x] | semmle.label | self[return] [s, x] |
| test.swift:634:8:634:11 | s [x] | semmle.label | s [x] |
| test.swift:635:5:635:5 | [post] self [s, x] | semmle.label | [post] self [s, x] |
@@ -915,6 +936,16 @@ nodes
| test.swift:766:29:766:29 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] |
| test.swift:767:15:767:15 | s2 [s, some:0, x] | semmle.label | s2 [s, some:0, x] |
| test.swift:767:15:767:28 | \\...[...] | semmle.label | \\...[...] |
| test.swift:773:11:773:17 | [post] enter #keyPath(...) [s, x] | semmle.label | [post] enter #keyPath(...) [s, x] |
| test.swift:773:11:773:17 | [post] exit #keyPath(...) | semmle.label | [post] exit #keyPath(...) |
| test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] | semmle.label | [post] KeyPathComponent [s, x] |
| test.swift:773:17:773:17 | [post] KeyPathComponent [x] | semmle.label | [post] KeyPathComponent [x] |
| test.swift:774:3:774:3 | [post] s2 [s, x] | semmle.label | [post] s2 [s, x] |
| test.swift:774:3:774:16 | \\...[...] | semmle.label | \\...[...] |
| test.swift:774:20:774:27 | call to source() | semmle.label | call to source() |
| test.swift:775:13:775:13 | s2 [s, x] | semmle.label | s2 [s, x] |
| test.swift:775:13:775:16 | .s [x] | semmle.label | .s [x] |
| test.swift:775:13:775:18 | .x | semmle.label | .x |
subpaths
| 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 |
| 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 ... |
@@ -969,6 +1000,9 @@ subpaths
| test.swift:764:18:764:25 | call to source() | test.swift:617:8:617:11 | x | test.swift:617:3:619:3 | self[return] [x] | test.swift:764:13:764:26 | call to S.init(x:) [x] |
| test.swift:765:29:765:29 | s [some:0, x] | test.swift:655:8:655:12 | s [some:0, x] | test.swift:655:3:657:3 | self[return] [s, some:0, x] | test.swift:765:14:765:30 | call to S2_Optional.init(s:) [s, some:0, x] |
| test.swift:767:15:767:15 | s2 [s, some:0, x] | test.swift:766:13:766:29 | enter #keyPath(...) [s, some:0, x] | test.swift:766:13:766:29 | exit #keyPath(...) | test.swift:767:15:767:28 | \\...[...] |
| test.swift:774:3:774:16 | \\...[...] | test.swift:773:11:773:17 | [post] exit #keyPath(...) | test.swift:773:11:773:17 | [post] enter #keyPath(...) [s, x] | test.swift:774:3:774:3 | [post] s2 [s, x] |
| test.swift:775:13:775:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] | file://:0:0:0:0 | .s [x] | test.swift:775:13:775:16 | .s [x] |
| test.swift:775:13:775:16 | .s [x] | test.swift:615:7:615:7 | self [x] | file://:0:0:0:0 | .x | test.swift:775:13:775:18 | .x |
#select
| test.swift:7:15:7:15 | t1 | test.swift:6:19:6:26 | call to source() | test.swift:7:15:7:15 | t1 | result |
| test.swift:9:15:9:15 | t1 | test.swift:6:19:6:26 | call to source() | test.swift:9:15:9:15 | t1 | result |
@@ -1070,3 +1104,4 @@ subpaths
| test.swift:756:15:756:21 | ...! | test.swift:746:14:746:21 | call to source() | test.swift:756:15:756:21 | ...! | result |
| test.swift:757:15:757:19 | .v3 | test.swift:747:14:747:21 | call to source() | test.swift:757:15:757:19 | .v3 | result |
| test.swift:767:15:767:28 | \\...[...] | test.swift:764:18:764:25 | call to source() | test.swift:767:15:767:28 | \\...[...] | result |
| test.swift:775:13:775:18 | .x | test.swift:774:20:774:27 | call to source() | test.swift:775:13:775:18 | .x | result |

View File

@@ -686,6 +686,9 @@
| test.swift:611:36:611:36 | [post] n | test.swift:611:35:611:36 | &... |
| test.swift:611:36:611:36 | n | test.swift:611:35:611:36 | &... |
| test.swift:615:7:615:7 | self | test.swift:615:7:615:7 | SSA def(self) |
| test.swift:615:7:615:7 | self | test.swift:615:7:615:7 | SSA def(self) |
| test.swift:615:7:615:7 | self | test.swift:615:7:615:7 | SSA def(self) |
| test.swift:615:7:615:7 | value | test.swift:615:7:615:7 | SSA def(value) |
| test.swift:617:3:617:3 | SSA def(self) | test.swift:618:5:618:5 | self |
| test.swift:617:3:617:3 | self | test.swift:617:3:617:3 | SSA def(self) |
| test.swift:617:8:617:11 | SSA def(x) | test.swift:618:14:618:14 | x |
@@ -699,13 +702,19 @@
| test.swift:624:7:624:7 | f | test.swift:624:7:624:7 | SSA def(f) |
| test.swift:624:11:624:14 | #keyPath(...) | test.swift:624:7:624:7 | f |
| test.swift:624:11:624:14 | enter #keyPath(...) | test.swift:624:14:624:14 | KeyPathComponent |
| test.swift:624:14:624:14 | [post] KeyPathComponent | test.swift:624:11:624:14 | [post] enter #keyPath(...) |
| test.swift:625:13:625:13 | [post] s | test.swift:628:13:628:13 | s |
| test.swift:625:13:625:13 | s | test.swift:628:13:628:13 | s |
| test.swift:627:7:627:7 | SSA def(inferred) | test.swift:628:24:628:24 | inferred |
| test.swift:627:7:627:7 | inferred | test.swift:627:7:627:7 | SSA def(inferred) |
| test.swift:627:7:627:32 | ... as ... | test.swift:627:7:627:7 | inferred |
| test.swift:627:36:627:38 | #keyPath(...) | test.swift:627:7:627:32 | ... as ... |
| test.swift:627:36:627:38 | enter #keyPath(...) | test.swift:627:38:627:38 | KeyPathComponent |
| test.swift:627:38:627:38 | [post] KeyPathComponent | test.swift:627:36:627:38 | [post] enter #keyPath(...) |
| test.swift:632:7:632:7 | self | test.swift:632:7:632:7 | SSA def(self) |
| test.swift:632:7:632:7 | self | test.swift:632:7:632:7 | SSA def(self) |
| test.swift:632:7:632:7 | self | test.swift:632:7:632:7 | SSA def(self) |
| test.swift:632:7:632:7 | value | test.swift:632:7:632:7 | SSA def(value) |
| test.swift:634:3:634:3 | SSA def(self) | test.swift:635:5:635:5 | self |
| test.swift:634:3:634:3 | self | test.swift:634:3:634:3 | SSA def(self) |
| test.swift:634:8:634:11 | SSA def(s) | test.swift:635:14:635:14 | s |
@@ -722,6 +731,7 @@
| test.swift:642:7:642:7 | f | test.swift:642:7:642:7 | SSA def(f) |
| test.swift:642:11:642:17 | #keyPath(...) | test.swift:642:7:642:7 | f |
| test.swift:642:11:642:17 | enter #keyPath(...) | test.swift:642:15:642:15 | KeyPathComponent |
| test.swift:642:15:642:15 | [post] KeyPathComponent | test.swift:642:11:642:17 | [post] enter #keyPath(...) |
| test.swift:647:9:647:9 | SSA def(array) | test.swift:649:15:649:15 | array |
| test.swift:647:9:647:9 | array | test.swift:647:9:647:9 | SSA def(array) |
| test.swift:647:17:647:26 | [...] | test.swift:647:9:647:9 | array |
@@ -729,6 +739,7 @@
| test.swift:648:9:648:9 | f | test.swift:648:9:648:9 | SSA def(f) |
| test.swift:648:13:648:22 | #keyPath(...) | test.swift:648:9:648:9 | f |
| test.swift:648:13:648:22 | enter #keyPath(...) | test.swift:648:20:648:22 | KeyPathComponent |
| test.swift:648:20:648:22 | [post] KeyPathComponent | test.swift:648:13:648:22 | [post] enter #keyPath(...) |
| test.swift:653:7:653:7 | self | test.swift:653:7:653:7 | SSA def(self) |
| test.swift:655:3:655:3 | SSA def(self) | test.swift:656:5:656:5 | self |
| test.swift:655:3:655:3 | self | test.swift:655:3:655:3 | SSA def(self) |
@@ -746,6 +757,7 @@
| test.swift:663:9:663:9 | f | test.swift:663:9:663:9 | SSA def(f) |
| test.swift:663:13:663:29 | #keyPath(...) | test.swift:663:9:663:9 | f |
| test.swift:663:13:663:29 | enter #keyPath(...) | test.swift:663:26:663:26 | KeyPathComponent |
| test.swift:663:26:663:26 | [post] KeyPathComponent | test.swift:663:13:663:29 | [post] enter #keyPath(...) |
| test.swift:664:15:664:28 | \\...[...] | test.swift:664:15:664:29 | ...! |
| test.swift:668:9:668:9 | SSA def(x) | test.swift:672:9:672:9 | x |
| test.swift:668:9:668:9 | x | test.swift:668:9:668:9 | SSA def(x) |
@@ -924,3 +936,17 @@
| test.swift:766:9:766:9 | f | test.swift:766:9:766:9 | SSA def(f) |
| test.swift:766:13:766:29 | #keyPath(...) | test.swift:766:9:766:9 | f |
| test.swift:766:13:766:29 | enter #keyPath(...) | test.swift:766:26:766:26 | KeyPathComponent |
| test.swift:766:26:766:26 | [post] KeyPathComponent | test.swift:766:13:766:29 | [post] enter #keyPath(...) |
| test.swift:771:7:771:7 | SSA def(s2) | test.swift:772:13:772:13 | s2 |
| test.swift:771:7:771:7 | s2 | test.swift:771:7:771:7 | SSA def(s2) |
| test.swift:771:12:771:25 | call to S2.init(s:) | test.swift:771:7:771:7 | s2 |
| test.swift:772:13:772:13 | [post] s2 | test.swift:774:3:774:3 | s2 |
| test.swift:772:13:772:13 | s2 | test.swift:774:3:774:3 | s2 |
| test.swift:773:7:773:7 | SSA def(f) | test.swift:774:15:774:15 | f |
| test.swift:773:7:773:7 | f | test.swift:773:7:773:7 | SSA def(f) |
| test.swift:773:11:773:17 | #keyPath(...) | test.swift:773:7:773:7 | f |
| test.swift:773:11:773:17 | enter #keyPath(...) | test.swift:773:15:773:15 | KeyPathComponent |
| test.swift:773:15:773:15 | [post] KeyPathComponent | test.swift:773:11:773:17 | [post] enter #keyPath(...) |
| test.swift:774:3:774:3 | [post] s2 | test.swift:775:13:775:13 | s2 |
| test.swift:774:3:774:3 | s2 | test.swift:775:13:775:13 | s2 |
| test.swift:774:20:774:27 | call to source() | test.swift:774:3:774:16 | \\...[...] |

View File

@@ -612,7 +612,7 @@ func inoutConstructor() {
}
struct S {
let x: Int
var x: Int
init(x: Int) {
self.x = x
@@ -629,7 +629,7 @@ func testKeyPath() {
}
struct S2 {
let s: S
var s: S
init(s: S) {
self.s = s
@@ -766,3 +766,11 @@ func testOptionalKeyPathForce() {
let f = \S2_Optional.s!.x
sink(arg: s2[keyPath: f]) // $ flow=764
}
func testNestedKeyPathWrite() {
var s2 = S2(s: S(x: 1))
sink(arg: s2.s.x)
var f = \S2.s.x
s2[keyPath: f] = source()
sink(arg: s2.s.x) // $ flow=774
}