Skip to content

Commit 1634fa2

Browse files
committed
Swift: support for optional chaining in keypaths
1 parent 81bf415 commit 1634fa2

File tree

3 files changed

+48
-8
lines changed

3 files changed

+48
-8
lines changed

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,14 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
727727
c.isSingleton(any(Content::ArrayContent ac))
728728
)
729729
or
730+
// Store throug
731+
exists(KeyPathComponent component |
732+
component.isOptionalWrapping() and
733+
node1.(KeyPathComponentNodeImpl).getComponent() = component and
734+
node2.(KeyPathReturnNodeImpl).getKeyPathExpr() = component.getKeyPathExpr() and
735+
c instanceof OptionalSomeContentSet
736+
)
737+
or
730738
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
731739
node2.(FlowSummaryNode).getSummaryNode())
732740
}
@@ -790,13 +798,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
790798
c.isSingleton(any(Content::ArrayContent ac)) and
791799
component.isSubscript()
792800
or
793-
c.isSingleton(any(Content::EnumContent ec, EnumElementDecl eed |
794-
ec.getParam() = eed.getParam(0) and
795-
eed.getEnclosingDecl().asNominalTypeDecl().getName() = "Optional"
796-
|
797-
ec
798-
)) and
799-
component.isOptionalForcing()
801+
c instanceof OptionalSomeContentSet and
802+
(
803+
component.isOptionalForcing()
804+
or
805+
component.isOptionalChaining()
806+
)
800807
)
801808
|
802809
// the next node is either the next element in the chain

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
edges
2+
| file://:0:0:0:0 | KeyPathComponent | test.swift:663:13:663:29 | exit #keyPath(...) [some:0] |
23
| file://:0:0:0:0 | self [a, x] | file://:0:0:0:0 | .a [x] |
34
| file://:0:0:0:0 | self [str] | file://:0:0:0:0 | .str |
45
| file://:0:0:0:0 | self [v2, some:0] | file://:0:0:0:0 | .v2 [some:0] |
@@ -336,6 +337,20 @@ edges
336337
| test.swift:655:8:655:12 | s [some:0, x] | test.swift:656:14:656:14 | s [some:0, x] |
337338
| test.swift:656:5:656:5 | [post] self [s, some:0, x] | test.swift:655:3:657:3 | self[return] [s, some:0, x] |
338339
| test.swift:656:14:656:14 | s [some:0, x] | test.swift:656:5:656:5 | [post] self [s, some:0, x] |
340+
| test.swift:661:13:661:26 | call to S.init(x:) [x] | test.swift:662:29:662:29 | s [x] |
341+
| test.swift:661:18:661:25 | call to source() | test.swift:617:8:617:11 | x |
342+
| test.swift:661:18:661:25 | call to source() | test.swift:661:13:661:26 | call to S.init(x:) [x] |
343+
| test.swift:662:14:662:30 | call to S2_Optional.init(s:) [s, some:0, x] | test.swift:664:15:664:15 | s2 [s, some:0, x] |
344+
| test.swift:662:29:662:29 | s [some:0, x] | test.swift:655:8:655:12 | s [some:0, x] |
345+
| test.swift:662:29:662:29 | s [some:0, x] | test.swift:662:14:662:30 | call to S2_Optional.init(s:) [s, some:0, x] |
346+
| test.swift:662:29:662:29 | s [x] | test.swift:662:29:662:29 | s [some:0, x] |
347+
| test.swift:663:13:663:29 | enter #keyPath(...) [s, some:0, x] | test.swift:663:26:663:26 | KeyPathComponent [s, some:0, x] |
348+
| test.swift:663:26:663:26 | KeyPathComponent [s, some:0, x] | test.swift:663:27:663:27 | KeyPathComponent [some:0, x] |
349+
| test.swift:663:27:663:27 | KeyPathComponent [some:0, x] | test.swift:663:29:663:29 | KeyPathComponent [x] |
350+
| test.swift:663:29:663:29 | KeyPathComponent [x] | file://:0:0:0:0 | KeyPathComponent |
351+
| test.swift:664:15:664:15 | s2 [s, some:0, x] | test.swift:663:13:663:29 | enter #keyPath(...) [s, some:0, x] |
352+
| test.swift:664:15:664:15 | s2 [s, some:0, x] | test.swift:664:15:664:28 | \\...[...] [some:0] |
353+
| test.swift:664:15:664:28 | \\...[...] [some:0] | test.swift:664:15:664:29 | ...! |
339354
| test.swift:668:13:668:20 | call to source() | test.swift:676:15:676:15 | y |
340355
| test.swift:678:9:678:16 | call to source() | test.swift:680:11:680:11 | x |
341356
| test.swift:678:9:678:16 | call to source() | test.swift:681:15:681:15 | x |
@@ -428,6 +443,7 @@ nodes
428443
| file://:0:0:0:0 | .v3 | semmle.label | .v3 |
429444
| file://:0:0:0:0 | .x | semmle.label | .x |
430445
| file://:0:0:0:0 | .x [some:0] | semmle.label | .x [some:0] |
446+
| file://:0:0:0:0 | KeyPathComponent | semmle.label | KeyPathComponent |
431447
| file://:0:0:0:0 | [post] self [v2, some:0] | semmle.label | [post] self [v2, some:0] |
432448
| file://:0:0:0:0 | [post] self [v2] | semmle.label | [post] self [v2] |
433449
| file://:0:0:0:0 | [post] self [v3] | semmle.label | [post] self [v3] |
@@ -791,6 +807,19 @@ nodes
791807
| test.swift:655:8:655:12 | s [some:0, x] | semmle.label | s [some:0, x] |
792808
| test.swift:656:5:656:5 | [post] self [s, some:0, x] | semmle.label | [post] self [s, some:0, x] |
793809
| test.swift:656:14:656:14 | s [some:0, x] | semmle.label | s [some:0, x] |
810+
| test.swift:661:13:661:26 | call to S.init(x:) [x] | semmle.label | call to S.init(x:) [x] |
811+
| test.swift:661:18:661:25 | call to source() | semmle.label | call to source() |
812+
| test.swift:662:14:662:30 | call to S2_Optional.init(s:) [s, some:0, x] | semmle.label | call to S2_Optional.init(s:) [s, some:0, x] |
813+
| test.swift:662:29:662:29 | s [some:0, x] | semmle.label | s [some:0, x] |
814+
| test.swift:662:29:662:29 | s [x] | semmle.label | s [x] |
815+
| test.swift:663:13:663:29 | enter #keyPath(...) [s, some:0, x] | semmle.label | enter #keyPath(...) [s, some:0, x] |
816+
| test.swift:663:13:663:29 | exit #keyPath(...) [some:0] | semmle.label | exit #keyPath(...) [some:0] |
817+
| test.swift:663:26:663:26 | KeyPathComponent [s, some:0, x] | semmle.label | KeyPathComponent [s, some:0, x] |
818+
| test.swift:663:27:663:27 | KeyPathComponent [some:0, x] | semmle.label | KeyPathComponent [some:0, x] |
819+
| test.swift:663:29:663:29 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] |
820+
| test.swift:664:15:664:15 | s2 [s, some:0, x] | semmle.label | s2 [s, some:0, x] |
821+
| test.swift:664:15:664:28 | \\...[...] [some:0] | semmle.label | \\...[...] [some:0] |
822+
| test.swift:664:15:664:29 | ...! | semmle.label | ...! |
794823
| test.swift:668:13:668:20 | call to source() | semmle.label | call to source() |
795824
| test.swift:676:15:676:15 | y | semmle.label | y |
796825
| test.swift:678:9:678:16 | call to source() | semmle.label | call to source() |
@@ -923,6 +952,9 @@ subpaths
923952
| test.swift:641:18:641:18 | s [x] | test.swift:634:8:634:11 | s [x] | test.swift:634:3:636:3 | self[return] [s, x] | test.swift:641:12:641:19 | call to S2.init(s:) [s, x] |
924953
| test.swift:643:13:643:13 | s2 [s, x] | test.swift:642:11:642:17 | enter #keyPath(...) [s, x] | test.swift:642:11:642:17 | exit #keyPath(...) | test.swift:643:13:643:26 | \\...[...] |
925954
| test.swift:649:15:649:15 | array [Array element] | test.swift:648:13:648:22 | enter #keyPath(...) [Array element] | test.swift:648:13:648:22 | exit #keyPath(...) | test.swift:649:15:649:31 | \\...[...] |
955+
| test.swift:661:18:661:25 | call to source() | test.swift:617:8:617:11 | x | test.swift:617:3:619:3 | self[return] [x] | test.swift:661:13:661:26 | call to S.init(x:) [x] |
956+
| test.swift:662:29:662: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:662:14:662:30 | call to S2_Optional.init(s:) [s, some:0, x] |
957+
| test.swift:664:15:664:15 | s2 [s, some:0, x] | test.swift:663:13:663:29 | enter #keyPath(...) [s, some:0, x] | test.swift:663:13:663:29 | exit #keyPath(...) [some:0] | test.swift:664:15:664:28 | \\...[...] [some:0] |
926958
| test.swift:746:14:746:21 | call to source() | test.swift:731:9:731:9 | value | file://:0:0:0:0 | [post] self [v2] | test.swift:746:5:746:5 | [post] mo1 [v2] |
927959
| test.swift:746:14:746:21 | call to source() [some:0] | test.swift:731:9:731:9 | value [some:0] | file://:0:0:0:0 | [post] self [v2, some:0] | test.swift:746:5:746:5 | [post] mo1 [v2, some:0] |
928960
| test.swift:747:14:747:21 | call to source() | test.swift:732:9:732:9 | value | file://:0:0:0:0 | [post] self [v3] | test.swift:747:5:747:5 | [post] mo1 [v3] |
@@ -1015,6 +1047,7 @@ subpaths
10151047
| test.swift:628:13:628:32 | \\...[...] | test.swift:623:16:623:23 | call to source() | test.swift:628:13:628:32 | \\...[...] | result |
10161048
| test.swift:643:13:643:26 | \\...[...] | test.swift:640:16:640:23 | call to source() | test.swift:643:13:643:26 | \\...[...] | result |
10171049
| test.swift:649:15:649:31 | \\...[...] | test.swift:647:18:647:25 | call to source() | test.swift:649:15:649:31 | \\...[...] | result |
1050+
| test.swift:664:15:664:29 | ...! | test.swift:661:18:661:25 | call to source() | test.swift:664:15:664:29 | ...! | result |
10181051
| test.swift:676:15:676:15 | y | test.swift:668:13:668:20 | call to source() | test.swift:676:15:676:15 | y | result |
10191052
| test.swift:681:15:681:15 | x | test.swift:678:9:678:16 | call to source() | test.swift:681:15:681:15 | x | result |
10201053
| test.swift:682:15:682:15 | y | test.swift:678:9:678:16 | call to source() | test.swift:682:15:682:15 | y | result |

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ func testOptionalKeyPath() {
661661
let s = S(x: source())
662662
let s2 = S2_Optional(s: s)
663663
let f = \S2_Optional.s?.x
664-
sink(arg: s2[keyPath: f]!) // $ MISSING: flow=661
664+
sink(arg: s2[keyPath: f]!) // $ flow=661
665665
}
666666

667667
func testSwap() {

0 commit comments

Comments
 (0)