@@ -14,7 +14,7 @@ private class ArrayIndex extends int {
14
14
* Provides flow summaries for the `Array` class.
15
15
*
16
16
* The summaries are ordered (and implemented) based on
17
- * https://ruby-doc .org/core- 3.1.0 /Array.html, however for methods that have the
17
+ * https://docs. ruby-lang .org/en/ 3.1/Array.html, however for methods that have the
18
18
* more general `Enumerable` scope, they are implemented in the `Enumerable`
19
19
* module instead.
20
20
*/
@@ -368,6 +368,10 @@ module Array {
368
368
}
369
369
370
370
override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
371
+ // We model this imprecisely, saying that there's flow from any element of
372
+ // the argument or the receiver to any element of the receiver. This could
373
+ // be made more precise when the range is known, similar to the way it's
374
+ // done in `ElementReferenceRangeReadKnownSummary`.
371
375
exists ( string arg |
372
376
arg = "Argument[" + ( mc .getNumberOfArguments ( ) - 1 ) + "]" and
373
377
input = [ "ArrayElement of " + arg , arg , "ArrayElement of Receiver" ] and
@@ -526,18 +530,71 @@ module Array {
526
530
DeleteSummary ( ) { this = "delete" }
527
531
528
532
override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
529
- input = [ "ArrayElement of Receiver" , "ReturnValue of BlockArgument" ] and
530
- output = "ReturnValue" and
533
+ (
534
+ input = "ArrayElement of Receiver" and
535
+ output = [ "ArrayElement[?] of Receiver" , "ReturnValue" ]
536
+ or
537
+ input = "ReturnValue of BlockArgument" and
538
+ output = "ReturnValue"
539
+ ) and
531
540
preservesValue = true
532
541
}
542
+
543
+ override predicate clearsContent ( ParameterPosition pos , DataFlow:: Content content ) {
544
+ pos .isSelf ( ) and
545
+ content instanceof DataFlow:: Content:: ArrayElementContent
546
+ }
533
547
}
534
548
535
- private class DeleteAtSummary extends SimpleSummarizedCallable {
536
- DeleteAtSummary ( ) { this = "delete_at" }
549
+ abstract private class DeleteAtSummary extends SummarizedCallable {
550
+ MethodCall mc ;
551
+
552
+ bindingset [ this ]
553
+ DeleteAtSummary ( ) { mc .getMethodName ( ) = "delete_at" }
554
+
555
+ override predicate clearsContent ( ParameterPosition pos , DataFlow:: Content content ) {
556
+ pos .isSelf ( ) and
557
+ content instanceof DataFlow:: Content:: ArrayElementContent
558
+ }
559
+
560
+ override MethodCall getACall ( ) { result = mc }
561
+ }
562
+
563
+ private class DeleteAtKnownSummary extends DeleteAtSummary {
564
+ int i ;
565
+
566
+ DeleteAtKnownSummary ( ) {
567
+ this = "delete_at(" + i + ")" and
568
+ i = mc .getArgument ( 0 ) .getConstantValue ( ) .getInt ( ) and
569
+ i >= 0
570
+ }
571
+
572
+ override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
573
+ (
574
+ input = "ArrayElement[?] of Receiver" and
575
+ output = [ "ReturnValue" , "ArrayElement[?] of Receiver" ]
576
+ or
577
+ exists ( ArrayIndex j | input = "ArrayElement[" + j + "] of Receiver" |
578
+ j < i and output = "ArrayElement[" + j + "] of Receiver"
579
+ or
580
+ j = i and output = "ReturnValue"
581
+ or
582
+ j > i and output = "ArrayElement[" + ( j - 1 ) + "] of Receiver"
583
+ )
584
+ ) and
585
+ preservesValue = true
586
+ }
587
+ }
588
+
589
+ private class DeleteAtUnknownSummary extends DeleteAtSummary {
590
+ DeleteAtUnknownSummary ( ) {
591
+ this = "delete_at(index)" and
592
+ not exists ( int i | i = mc .getArgument ( 0 ) .getConstantValue ( ) .getInt ( ) and i >= 0 )
593
+ }
537
594
538
595
override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
539
596
input = "ArrayElement of Receiver" and
540
- output = "ReturnValue" and
597
+ output = [ "ReturnValue" , "ArrayElement[?] of Receiver" ] and
541
598
preservesValue = true
542
599
}
543
600
}
@@ -547,15 +604,26 @@ module Array {
547
604
548
605
override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
549
606
input = "ArrayElement of Receiver" and
550
- output = [ "Parameter[0] of BlockArgument" , "ArrayElement[?] of ReturnValue" ] and
607
+ output =
608
+ [
609
+ "Parameter[0] of BlockArgument" , "ArrayElement[?] of ReturnValue" ,
610
+ "ArrayElement[?] of Receiver"
611
+ ] and
551
612
preservesValue = true
552
613
}
614
+
615
+ override predicate clearsContent ( ParameterPosition pos , DataFlow:: Content content ) {
616
+ pos .isSelf ( ) and
617
+ content instanceof DataFlow:: Content:: ArrayElementContent
618
+ }
553
619
}
554
620
555
621
private class DifferenceSummary extends SimpleSummarizedCallable {
556
622
DifferenceSummary ( ) { this = "difference" }
557
623
558
624
override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
625
+ // `Array#difference` and `Array#-` do not behave exactly the same way,
626
+ // but we model their flow the same way.
559
627
any ( SetDifferenceSummary s ) .propagatesFlowExt ( input , output , preservesValue )
560
628
}
561
629
}
@@ -653,12 +721,53 @@ module Array {
653
721
}
654
722
}
655
723
656
- private class FetchSummary extends SimpleSummarizedCallable {
657
- FetchSummary ( ) { this = "fetch" }
724
+ abstract private class FetchSummary extends SummarizedCallable {
725
+ MethodCall mc ;
726
+
727
+ bindingset [ this ]
728
+ FetchSummary ( ) { mc .getMethodName ( ) = "fetch" }
729
+
730
+ override MethodCall getACall ( ) { result = mc }
731
+ }
732
+
733
+ private class FetchKnownSummary extends FetchSummary {
734
+ int i ;
735
+
736
+ FetchKnownSummary ( ) {
737
+ this = "fetch(" + i + ")" and
738
+ i = mc .getArgument ( 0 ) .getConstantValue ( ) .getInt ( ) and
739
+ i >= 0
740
+ }
658
741
659
742
override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
660
743
(
661
- input = "ArrayElement of Receiver" and
744
+ input = "ArrayElement[?] of Receiver" and
745
+ output = [ "ReturnValue" , "ArrayElement[?] of Receiver" ]
746
+ or
747
+ exists ( ArrayIndex j | input = "ArrayElement[" + j + "] of Receiver" |
748
+ j = i and output = "ReturnValue"
749
+ or
750
+ j != i and output = "ArrayElement[" + j + "] of Receiver"
751
+ )
752
+ or
753
+ input = "Argument[0]" and
754
+ output = "Parameter[0] of BlockArgument"
755
+ or
756
+ input = "Argument[1]" and output = "ReturnValue"
757
+ ) and
758
+ preservesValue = true
759
+ }
760
+ }
761
+
762
+ private class FetchUnknownSummary extends FetchSummary {
763
+ FetchUnknownSummary ( ) {
764
+ this = "fetch(index)" and
765
+ not exists ( int i | i = mc .getArgument ( 0 ) .getConstantValue ( ) .getInt ( ) and i >= 0 )
766
+ }
767
+
768
+ override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
769
+ (
770
+ input = [ "ArrayElement of Receiver" , "Argument[1]" ] and
662
771
output = "ReturnValue"
663
772
or
664
773
input = "Argument[0]" and
@@ -702,6 +811,12 @@ module Array {
702
811
}
703
812
}
704
813
814
+ /**
815
+ * A call to `flatten`.
816
+ *
817
+ * Note that we model flow from elements up to 3 levels of nesting
818
+ * (`[[[1],[2]]]`), but not beyond that.
819
+ */
705
820
private class FlattenSummary extends SimpleSummarizedCallable {
706
821
FlattenSummary ( ) { this = "flatten" }
707
822
@@ -1494,7 +1609,7 @@ module Array {
1494
1609
* Provides flow summaries for the `Enumerable` class.
1495
1610
*
1496
1611
* The summaries are ordered (and implemented) based on
1497
- * https://ruby-doc .org/core- 3.1.0 /Enumerable.html
1612
+ * https://docs. ruby-lang .org/en/ 3.1/Enumerable.html
1498
1613
*/
1499
1614
module Enumerable {
1500
1615
private class ChunkSummary extends SimpleSummarizedCallable {
0 commit comments