Skip to content

Commit 36bdadf

Browse files
authored
Merge pull request github#13933 from geoffw0/madtuples
Swift: Models-as-data support for tuple content
2 parents 3e2c6d6 + c20a17e commit 36bdadf

File tree

7 files changed

+1161
-1074
lines changed

7 files changed

+1161
-1074
lines changed

swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -476,18 +476,25 @@ private predicate parseField(AccessPathToken c, Content::FieldContent f) {
476476
)
477477
}
478478

479-
private predicate parseEnum(AccessPathToken c, Content::EnumContent f) {
479+
private predicate parseTuple(AccessPathToken c, Content::TupleContent t) {
480+
c.getName() = "TupleElement" and
481+
t.getIndex() = c.getAnArgument().toInt()
482+
}
483+
484+
private predicate parseEnum(AccessPathToken c, Content::EnumContent e) {
480485
c.getName() = "EnumElement" and
481-
c.getAnArgument() = f.getSignature()
486+
c.getAnArgument() = e.getSignature()
482487
or
483488
c.getName() = "OptionalSome" and
484-
f.getSignature() = "some:0"
489+
e.getSignature() = "some:0"
485490
}
486491

487492
/** Holds if the specification component parses as a `Content`. */
488493
predicate parseContent(AccessPathToken component, Content content) {
489494
parseField(component, content)
490495
or
496+
parseTuple(component, content)
497+
or
491498
parseEnum(component, content)
492499
or
493500
component.getName() = "ArrayElement" and

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ private string getContentSpecific(ContentSet cs) {
110110
result = "Field[" + c.getField().getName() + "]"
111111
)
112112
or
113+
exists(Content::TupleContent c |
114+
cs.isSingleton(c) and
115+
result = "TupleElement[" + c.getIndex().toString() + "]"
116+
)
117+
or
113118
exists(Content::EnumContent c |
114119
cs.isSingleton(c) and
115120
result = "EnumElement[" + c.getSignature() + "]"
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
multipleSuccessors
2-
| test.swift:519:8:519:12 | let ...? | no-match | test.swift:519:27:519:27 | y |
3-
| test.swift:519:8:519:12 | let ...? | no-match | test.swift:524:9:524:9 | tuple1 |
2+
| test.swift:538:8:538:12 | let ...? | no-match | test.swift:538:27:538:27 | y |
3+
| test.swift:538:8:538:12 | let ...? | no-match | test.swift:543:9:543:9 | tuple1 |

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

Lines changed: 542 additions & 507 deletions
Large diffs are not rendered by default.

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ private class TestSummaries extends SummaryModelCsv {
2626
[
2727
// model to allow data flow through `signum()` as though it were an identity function, for the benefit of testing flow through optional chaining (`x?.`).
2828
";Int;true;signum();;;Argument[-1];ReturnValue;value",
29+
// test Tuple content in MAD
30+
";;false;tupleShiftLeft2(_:);;;Argument[0].TupleElement[1];ReturnValue.TupleElement[0];value",
2931
// test Enum content in MAD
3032
";;false;mkMyEnum2(_:);;;Argument[0];ReturnValue.EnumElement[mySingle:0];value",
3133
";;false;mkOptional2(_:);;;Argument[0];ReturnValue.OptionalSome;value"

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

Lines changed: 534 additions & 515 deletions
Large diffs are not rendered by default.

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

Lines changed: 66 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,25 @@ func testTuples2() {
365365
sink(arg: c)
366366
}
367367

368+
func tupleShiftLeft1(_ t: (Int, Int)) -> (Int, Int) {
369+
return (t.1, 0)
370+
}
371+
372+
func tupleShiftLeft2(_ t: (Int, Int)) -> (Int, Int) { return (0, 0) } // modelled flow
373+
374+
func testTuples3() {
375+
let t1 = (1, source())
376+
let t2 = tupleShiftLeft1(t1)
377+
let t3 = tupleShiftLeft2(t1)
378+
379+
sink(arg: t1.0)
380+
sink(arg: t1.1) // $ flow=375
381+
sink(arg: t2.0) // $ flow=375
382+
sink(arg: t2.1)
383+
sink(arg: t3.0) // $ flow=375
384+
sink(arg: t3.1)
385+
}
386+
368387
indirect enum MyEnum {
369388
case myNone
370389
case mySingle(Int)
@@ -406,7 +425,7 @@ func testEnums() {
406425
case .myNone:
407426
()
408427
case .mySingle(let a):
409-
sink(arg: a) // $ flow=403
428+
sink(arg: a) // $ flow=422
410429
case .myPair(let a, let b):
411430
sink(arg: a)
412431
sink(arg: b)
@@ -415,7 +434,7 @@ func testEnums() {
415434
}
416435

417436
if case .mySingle(let x) = a {
418-
sink(arg: x) // $ flow=403
437+
sink(arg: x) // $ flow=422
419438
}
420439
if case .myPair(let x, let y) = a {
421440
sink(arg: x)
@@ -431,7 +450,7 @@ func testEnums() {
431450
sink(arg: a)
432451
case .myPair(let a, let b):
433452
sink(arg: a)
434-
sink(arg: b) // $ flow=425
453+
sink(arg: b) // $ flow=444
435454
case let .myCons(a, _):
436455
sink(arg: a)
437456
}
@@ -441,7 +460,7 @@ func testEnums() {
441460
}
442461
if case .myPair(let x, let y) = a {
443462
sink(arg: x)
444-
sink(arg: y) // $ flow=425
463+
sink(arg: y) // $ flow=444
445464
}
446465

447466
let b: MyEnum = .myCons(42, a)
@@ -457,7 +476,7 @@ func testEnums() {
457476
case let .myCons(a, .myPair(b, c)):
458477
sink(arg: a)
459478
sink(arg: b)
460-
sink(arg: c) // $ flow=425
479+
sink(arg: c) // $ flow=444
461480
case let .myCons(a, _):
462481
sink(arg: a)
463482
}
@@ -466,20 +485,20 @@ func testEnums() {
466485
sink(arg: x)
467486
}
468487
if case MyEnum.myPair(let x, let y) = .myPair(source(), 0) {
469-
sink(arg: x) // $ flow=468
488+
sink(arg: x) // $ flow=487
470489
sink(arg: y)
471490
}
472491
if case let .myCons(_, .myPair(_, c)) = b {
473-
sink(arg: c) // $ flow=425
492+
sink(arg: c) // $ flow=444
474493
}
475494

476495
switch (a, b) {
477496
case let (.myPair(a, b), .myCons(c, .myPair(d, e))):
478497
sink(arg: a)
479-
sink(arg: b) // $ flow=425
498+
sink(arg: b) // $ flow=444
480499
sink(arg: c)
481500
sink(arg: d)
482-
sink(arg: e) // $ flow=425
501+
sink(arg: e) // $ flow=444
483502
default:
484503
()
485504
}
@@ -491,11 +510,11 @@ func testEnums() {
491510
let c5 = mkMyEnum2(0)
492511
let c6 = mkMyEnum2(source())
493512
if case MyEnum.mySingle(let d1) = c1 { sink(arg: d1) }
494-
if case MyEnum.mySingle(let d2) = c2 { sink(arg: d2) } // $ flow=488
513+
if case MyEnum.mySingle(let d2) = c2 { sink(arg: d2) } // $ flow=507
495514
if case MyEnum.mySingle(let d3) = c3 { sink(arg: d3) }
496-
if case MyEnum.mySingle(let d4) = c4 { sink(arg: d4) } // $ flow=490
515+
if case MyEnum.mySingle(let d4) = c4 { sink(arg: d4) } // $ flow=509
497516
if case MyEnum.mySingle(let d5) = c5 { sink(arg: d5) }
498-
if case MyEnum.mySingle(let d6) = c6 { sink(arg: d6) } // $ flow=492
517+
if case MyEnum.mySingle(let d6) = c6 { sink(arg: d6) } // $ flow=511
499518

500519
let e1 = Optional.some(0)
501520
let e2 = Optional.some(source())
@@ -504,11 +523,11 @@ func testEnums() {
504523
let e5 = mkOptional2(0)
505524
let e6 = mkOptional2(source())
506525
sink(arg: e1!)
507-
sink(arg: e2!) // $ flow=501
526+
sink(arg: e2!) // $ flow=520
508527
sink(arg: e3!)
509-
sink(arg: e4!) // $ flow=503
528+
sink(arg: e4!) // $ flow=522
510529
sink(arg: e5!)
511-
sink(arg: e6!) // $ flow=505
530+
sink(arg: e6!) // $ flow=524
512531
}
513532

514533
func source2() -> (Int, Int)? { return nil }
@@ -554,8 +573,8 @@ func testOptionalPropertyAccess(y: Int?) {
554573
}
555574

556575
func testIdentityArithmetic() {
557-
sink(arg: +source()) // $ flow=557
558-
sink(arg: (source())) // $ flow=558
576+
sink(arg: +source()) // $ flow=576
577+
sink(arg: (source())) // $ flow=577
559578
}
560579

561580
func sink(str: String) {}
@@ -572,13 +591,13 @@ class MyClass {
572591
extension MyClass {
573592
convenience init(contentsOfFile: String) {
574593
self.init(s: source3())
575-
sink(str: str) // $ flow=574
594+
sink(str: str) // $ flow=593
576595
}
577596
}
578597

579598
func extensionInits(path: String) {
580-
sink(str: MyClass(s: source3()).str) // $ flow=580
581-
sink(str: MyClass(contentsOfFile: path).str) // $ flow=574
599+
sink(str: MyClass(s: source3()).str) // $ flow=599
600+
sink(str: MyClass(contentsOfFile: path).str) // $ flow=593
582601
}
583602

584603
class InoutConstructorClass {
@@ -603,10 +622,10 @@ struct S {
603622
func testKeyPath() {
604623
let s = S(x: source())
605624
let f = \S.x
606-
sink(arg: s[keyPath: f]) // $ flow=604
625+
sink(arg: s[keyPath: f]) // $ flow=623
607626

608627
let inferred : KeyPath<S, Int> = \.x
609-
sink(arg: s[keyPath: inferred]) // $ flow=604
628+
sink(arg: s[keyPath: inferred]) // $ flow=623
610629
}
611630

612631
struct S2 {
@@ -621,13 +640,13 @@ func testNestedKeyPath() {
621640
let s = S(x: source())
622641
let s2 = S2(s: s)
623642
let f = \S2.s.x
624-
sink(arg: s2[keyPath: f]) // $ flow=621
643+
sink(arg: s2[keyPath: f]) // $ flow=640
625644
}
626645

627646
func testArrayKeyPath() {
628647
let array = [source()]
629648
let f = \[Int].[0]
630-
sink(arg: array[keyPath: f]) // $ flow=628
649+
sink(arg: array[keyPath: f]) // $ flow=647
631650
}
632651

633652
struct S2_Optional {
@@ -642,7 +661,7 @@ func testOptionalKeyPath() {
642661
let s = S(x: source())
643662
let s2 = S2_Optional(s: s)
644663
let f = \S2_Optional.s?.x
645-
sink(opt: s2[keyPath: f]) // $ MISSING: flow=642
664+
sink(opt: s2[keyPath: f]) // $ MISSING: flow=661
646665
}
647666

648667
func testSwap() {
@@ -654,57 +673,57 @@ func testSwap() {
654673
x = y
655674
y = t
656675
sink(arg: x)
657-
sink(arg: y) // $ flow=649
676+
sink(arg: y) // $ flow=668
658677

659678
x = source()
660679
y = 0
661680
swap(&x, &y)
662-
sink(arg: x) // $ SPURIOUS: flow=659
663-
sink(arg: y) // $ flow=659
681+
sink(arg: x) // $ SPURIOUS: flow=678
682+
sink(arg: y) // $ flow=678
664683
}
665684

666685
func testArray() {
667686
var arr1 = [1,2,3]
668687
sink(arg: arr1[0])
669688
arr1[1] = source()
670-
sink(arg: arr1[0]) // $ flow=669
689+
sink(arg: arr1[0]) // $ flow=688
671690
sink(arg: arr1)
672691

673692
var arr2 = [source()]
674-
sink(arg: arr2[0]) // $ flow=673
693+
sink(arg: arr2[0]) // $ flow=692
675694

676695
var matrix = [[source()]]
677696
sink(arg: matrix[0])
678-
sink(arg: matrix[0][0]) // $ flow=676
697+
sink(arg: matrix[0][0]) // $ flow=695
679698

680699
var matrix2 = [[1]]
681700
matrix2[0][0] = source()
682-
sink(arg: matrix2[0][0]) // $ flow=681
701+
sink(arg: matrix2[0][0]) // $ flow=700
683702

684703
var arr3 = [1]
685704
var arr4 = arr2 + arr3
686705
sink(arg: arr3[0])
687-
sink(arg: arr4[0]) // $ MISSING: flow=673
706+
sink(arg: arr4[0]) // $ MISSING: flow=692
688707

689708
var arr5 = Array(repeating: source(), count: 2)
690-
sink(arg: arr5[0]) // $ MISSING: flow=689
709+
sink(arg: arr5[0]) // $ MISSING: flow=708
691710

692711
var arr6 = [1,2,3]
693712
arr6.insert(source(), at: 2)
694-
sink(arg: arr6[0]) // $ flow=693
713+
sink(arg: arr6[0]) // $ flow=712
695714

696715
var arr7 = [source()]
697-
sink(arg: arr7.randomElement()!) // $ flow=696
716+
sink(arg: arr7.randomElement()!) // $ flow=715
698717
}
699718

700719
func testSetCollections() {
701720
var set1: Set = [1,2,3]
702721
sink(arg: set1.randomElement()!)
703722
set1.insert(source())
704-
sink(arg: set1.randomElement()!) // $flow=703
723+
sink(arg: set1.randomElement()!) // $ flow=722
705724

706725
let set2 = Set([source()])
707-
sink(arg: set2.randomElement()!) // $ flow=706
726+
sink(arg: set2.randomElement()!) // $ flow=725
708727
}
709728

710729
struct MyOptionals {
@@ -730,13 +749,13 @@ func testWriteOptional() {
730749
mo2!.v2 = source()
731750
mo2!.v3 = source()
732751

733-
sink(arg: v1!) // $ flow=723
734-
sink(arg: v2!) // $ flow=724
735-
sink(arg: v3) // $ flow=725
736-
sink(arg: mo1.v1!) // $ MISSING:flow=726
737-
sink(arg: mo1.v2!) // $ flow=727
738-
sink(arg: mo1.v3) // $ flow=728
739-
sink(arg: mo2!.v1!) // $ MISSING:flow=729
740-
sink(arg: mo2!.v2!) // $ MISSING:flow=730
741-
sink(arg: mo2!.v3) // $ MISSING:flow=731
752+
sink(arg: v1!) // $ flow=742
753+
sink(arg: v2!) // $ flow=743
754+
sink(arg: v3) // $ flow=744
755+
sink(arg: mo1.v1!) // $ MISSING:flow=745
756+
sink(arg: mo1.v2!) // $ flow=746
757+
sink(arg: mo1.v3) // $ flow=747
758+
sink(arg: mo2!.v1!) // $ MISSING:flow=748
759+
sink(arg: mo2!.v2!) // $ MISSING:flow=749
760+
sink(arg: mo2!.v3) // $ MISSING:flow=750
742761
}

0 commit comments

Comments
 (0)