Ruby: add flow summaries for all remaining Enumerable methods

This commit is contained in:
Nick Rolfe
2022-01-07 14:38:45 +00:00
parent 588e60e230
commit 030cfa36da
3 changed files with 2426 additions and 941 deletions

View File

@@ -624,8 +624,9 @@ module Array {
}
}
private class AppendSummary extends SummarizedCallable {
AppendSummary() { this = "<<" }
/** Flow summary for `Array#<<`. For `Array#append`, see `PushSummary`. */
private class AppendOperatorSummary extends SummarizedCallable {
AppendOperatorSummary() { this = "<<" }
override LShiftExpr getACall() { any() }
@@ -980,7 +981,8 @@ module Array {
}
private class EachSummary extends SimpleSummarizedCallable {
EachSummary() { this = "each" }
// `each` and `reverse_each` are the same in terms of flow inputs/outputs.
EachSummary() { this = ["each", "reverse_each"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -1175,39 +1177,34 @@ module Array {
* Provides flow summaries for the `Enumerable` class.
*
* The summaries are ordered (and implemented) based on
* https://ruby-doc.org/core-2.7.0/Enumerable.html.
* https://ruby-doc.org/core-3.1.0/Enumerable.html
*/
module Enumerable {
private class AllSummary extends SimpleSummarizedCallable {
AllSummary() { this = "all?" }
private class ChunkSummary extends SimpleSummarizedCallable {
ChunkSummary() { this = "chunk" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = "Parameter[0] of BlockArgument" and
preservesValue = true
or
input = "ReturnValue of BlockArgument" and
output = "ReturnValue" and
preservesValue = false
}
}
private class AnySummary extends SimpleSummarizedCallable {
AnySummary() { this = "any?" }
private class ChunkWhileSummary extends SimpleSummarizedCallable {
ChunkWhileSummary() { this = "chunk_while" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = "Parameter[0] of BlockArgument" and
output = ["Parameter[0] of BlockArgument", "Parameter[1] of BlockArgument"] and
preservesValue = true
or
input = "ReturnValue of BlockArgument" and
output = "ReturnValue" and
preservesValue = false
}
}
private class CollectSummary extends SimpleSummarizedCallable {
CollectSummary() { this = ["collect", "collect!"] }
// `map` is an alias of `collect`.
// TODO: handle `map!` and `collect!` in the Array module. They were
// previously handled here, but they are not Enumerable methods.
CollectSummary() { this = ["collect", "map"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
@@ -1221,7 +1218,8 @@ module Enumerable {
}
private class CollectConcatSummary extends SimpleSummarizedCallable {
CollectConcatSummary() { this = "collect_concat" }
// `flat_map` is an alias of `collect_concat`.
CollectConcatSummary() { this = ["collect_concat", "flat_map"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
@@ -1255,7 +1253,8 @@ module Enumerable {
}
private class DetectSummary extends SimpleSummarizedCallable {
DetectSummary() { this = "detect" }
// `find` is an alias of `detect`.
DetectSummary() { this = ["detect", "find"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -1408,8 +1407,8 @@ module Enumerable {
}
}
private class FilterSummary extends SimpleSummarizedCallable {
FilterSummary() { this = ["filter", "filter_map"] }
private class FilterMapSummary extends SimpleSummarizedCallable {
FilterMapSummary() { this = "filter_map" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
@@ -1418,29 +1417,6 @@ module Enumerable {
}
}
private class FindSummary extends SimpleSummarizedCallable {
FindSummary() { this = "find" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
input = "ArrayElement of Receiver" and
output = ["Parameter[0] of BlockArgument", "ReturnValue"]
or
input = "ReturnValue of Argument[0]" and
output = "ReturnValue"
) and
preservesValue = true
}
}
private class FindAllSummary extends SimpleSummarizedCallable {
FindAllSummary() { this = "find_all" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
any(FilterSummary f).propagatesFlowExt(input, output, preservesValue)
}
}
private class FindIndexSummary extends SimpleSummarizedCallable {
FindIndexSummary() { this = "find_index" }
@@ -1513,21 +1489,6 @@ module Enumerable {
}
}
private class FlatMapSummary extends SimpleSummarizedCallable {
FlatMapSummary() { this = "flat_map" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
input = "ArrayElement of Receiver" and
output = "Parameter[0] of BlockArgument"
or
input = "ArrayElement of ReturnValue of BlockArgument" and
output = "ArrayElement[?] of ReturnValue"
) and
preservesValue = true
}
}
abstract private class GrepSummary extends SummarizedCallable {
MethodCall mc;
@@ -1561,5 +1522,451 @@ module Enumerable {
preservesValue = true
}
}
// TODO: Implement `group_by` when we have flow through hashes
private class GroupBySummary extends SimpleSummarizedCallable {
GroupBySummary() { this = "group_by" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
// TODO: Add flow to return value once we have flow through hashes
input = "ArrayElement of Receiver" and
output = "Parameter[0] of BlockArgument" and
preservesValue = true
}
}
abstract private class InjectSummary extends SummarizedCallable {
MethodCall mc;
// `reduce` is an alias for `inject`.
bindingset[this]
InjectSummary() { mc.getMethodName() = ["inject", "reduce"] }
override MethodCall getACall() { result = mc }
}
private class InjectNoArgSummary extends InjectSummary {
InjectNoArgSummary() { this = "inject(no_arg)" and mc.getNumberOfArguments() = 0 }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
// The no-argument variant of inject passes element 0 to the first block
// parameter (first iteration only). All other elements are passed to the
// second block parameter.
(
input = "ArrayElement[0] of Receiver" and
output = "Parameter[0] of BlockArgument"
or
exists(ArrayIndex i | i > 0 | input = "ArrayElement[" + i + "] of Receiver") and
output = "Parameter[1] of BlockArgument"
) and
preservesValue = true
}
}
private class InjectArgSummary extends InjectSummary {
InjectArgSummary() { this = "inject(arg)" and mc.getNumberOfArguments() > 0 }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
// The first argument of the call is passed to the first block parameter.
input = "Argument[0]" and
output = "Parameter[0] of BlockArgument"
or
// Each element in the receiver is passed to the second block parameter.
exists(ArrayIndex i | input = "ArrayElement[" + i + "] of Receiver") and
output = "Parameter[1] of BlockArgument"
) and
preservesValue = true
}
}
abstract private class MinOrMaxBySummary extends SummarizedCallable {
MethodCall mc;
bindingset[this]
MinOrMaxBySummary() { mc.getMethodName() = ["min_by", "max_by"] }
override MethodCall getACall() { result = mc }
}
private class MinOrMaxByNoArgSummary extends MinOrMaxBySummary {
MinOrMaxByNoArgSummary() {
this = "min_or_max_by_no_arg" and
mc.getNumberOfArguments() = 0
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = ["Parameter[0] of BlockArgument", "ReturnValue"] and
preservesValue = true
}
}
private class MinOrMaxByArgSummary extends MinOrMaxBySummary {
MinOrMaxByArgSummary() {
this = "min_or_max_by_arg" and
mc.getNumberOfArguments() > 0
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = ["Parameter[0] of BlockArgument", "ArrayElement[?] of ReturnValue"] and
preservesValue = true
}
}
abstract private class MinOrMaxSummary extends SummarizedCallable {
MethodCall mc;
bindingset[this]
MinOrMaxSummary() { mc.getMethodName() = ["min", "max"] }
override MethodCall getACall() { result = mc }
}
private class MinOrMaxNoArgNoBlockSummary extends MinOrMaxSummary {
MinOrMaxNoArgNoBlockSummary() {
this = "min_or_max_no_arg_no_block" and
mc.getNumberOfArguments() = 0 and
not exists(mc.getBlock())
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = "ReturnValue" and
preservesValue = true
}
}
private class MinOrMaxArgNoBlockSummary extends MinOrMaxSummary {
MinOrMaxArgNoBlockSummary() {
this = "min_or_max_arg_no_block" and
mc.getNumberOfArguments() > 0 and
not exists(mc.getBlock())
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = "ArrayElement[?] of ReturnValue" and
preservesValue = true
}
}
private class MinOrMaxNoArgBlockSummary extends MinOrMaxSummary {
MinOrMaxNoArgBlockSummary() {
this = "min_or_max_no_arg_block" and
mc.getNumberOfArguments() = 0 and
exists(mc.getBlock())
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = ["Parameter[0] of BlockArgument", "Parameter[1] of BlockArgument", "ReturnValue"] and
preservesValue = true
}
}
private class MinOrMaxArgBlockSummary extends MinOrMaxSummary {
MinOrMaxArgBlockSummary() {
this = "min_or_max_arg_block" and
mc.getNumberOfArguments() > 0 and
exists(mc.getBlock())
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output =
[
"Parameter[0] of BlockArgument", "Parameter[1] of BlockArgument",
"ArrayElement[?] of ReturnValue"
] and
preservesValue = true
}
}
abstract private class MinmaxSummary extends SummarizedCallable {
MethodCall mc;
bindingset[this]
MinmaxSummary() { mc.getMethodName() = "minmax" }
override MethodCall getACall() { result = mc }
}
private class MinmaxNoArgNoBlockSummary extends MinmaxSummary {
MinmaxNoArgNoBlockSummary() {
this = "minmax_no_block" and
not exists(mc.getBlock())
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = "ArrayElement[?] of ReturnValue" and
preservesValue = true
}
}
private class MinmaxBlockSummary extends MinmaxSummary {
MinmaxBlockSummary() {
this = "minmax_block" and
exists(mc.getBlock())
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output =
[
"Parameter[0] of BlockArgument", "Parameter[1] of BlockArgument",
"ArrayElement[?] of ReturnValue"
] and
preservesValue = true
}
}
private class MinmaxBySummary extends SimpleSummarizedCallable {
MinmaxBySummary() { this = "minmax_by" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = ["Parameter[0] of BlockArgument", "ArrayElement[?] of ReturnValue"] and
preservesValue = true
}
}
private class PartitionSummary extends SimpleSummarizedCallable {
PartitionSummary() { this = "partition" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output =
["Parameter[0] of BlockArgument", "ArrayElement[?] of ArrayElement[?] of ReturnValue"] and
preservesValue = true
}
}
private class QuerySummary extends SimpleSummarizedCallable {
QuerySummary() { this = ["all?", "any?", "none?", "one?"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = "Parameter[0] of BlockArgument" and
preservesValue = true
}
}
private class RejectSummary extends SimpleSummarizedCallable {
RejectSummary() { this = "reject" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = ["Parameter[0] of BlockArgument", "ArrayElement[?] of ReturnValue"] and
preservesValue = true
}
}
private class SelectSummary extends SimpleSummarizedCallable {
// `find_all` and `filter` are aliases of `select`.
SelectSummary() { this = ["select", "find_all", "filter"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = ["Parameter[0] of BlockArgument", "ArrayElement[?] of ReturnValue"] and
preservesValue = true
}
}
private class SliceBeforeAfterSummary extends SimpleSummarizedCallable {
SliceBeforeAfterSummary() { this = ["slice_before", "slice_after"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = "Parameter[0] of BlockArgument" and
preservesValue = true
}
}
private class SliceWhenSummary extends SimpleSummarizedCallable {
SliceWhenSummary() { this = "slice_when" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = ["Parameter[0] of BlockArgument", "Parameter[1] of BlockArgument"] and
preservesValue = true
}
}
private class SortSummary extends SimpleSummarizedCallable {
SortSummary() { this = "sort" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output =
[
"Parameter[0] of BlockArgument", "Parameter[1] of BlockArgument",
"ArrayElement[?] of ReturnValue"
] and
preservesValue = true
}
}
private class SortBySummary extends SimpleSummarizedCallable {
SortBySummary() { this = "sort_by" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = ["Parameter[0] of BlockArgument", "ArrayElement[?] of ReturnValue"] and
preservesValue = true
}
}
private class SumSummary extends SimpleSummarizedCallable {
SumSummary() { this = "sum" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = "Parameter[0] of BlockArgument" and
preservesValue = true
}
}
abstract private class TakeSummary extends SummarizedCallable {
MethodCall mc;
bindingset[this]
TakeSummary() { mc.getMethodName() = "take" }
override MethodCall getACall() { result = mc }
}
private class TakeKnownSummary extends TakeSummary {
private int i;
TakeKnownSummary() {
this = "take(" + i + ")" and
i = mc.getArgument(0).getValueText().toInt()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
input = "ArrayElement[?] of Receiver" and
output = "ArrayElement[?] of ReturnValue"
or
exists(ArrayIndex j | j < i |
input = "ArrayElement[" + j + "] of Receiver" and
output = "ArrayElement[" + j + "] of ReturnValue"
)
) and
preservesValue = true
}
}
private class TakeUnknownSummary extends TakeSummary {
TakeUnknownSummary() {
this = "take(index)" and
not exists(mc.getArgument(0).getValueText().toInt())
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
// When the index is unknown, we can't know the size of the result, but we
// know that indices are preserved, so, as an approximation, we just treat
// it like the array is copied.
input = "Receiver" and
output = "ReturnValue" and
preservesValue = true
}
}
private class TakeWhileSummary extends SimpleSummarizedCallable {
TakeWhileSummary() { this = "take_while" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = ["Parameter[0] of BlockArgument"] and
preservesValue = true
or
// We can't know the size of the return value, but we know that indices
// are preserved, so, as an approximation, we just treat it like the array
// is copied.
input = "Receiver" and
output = "ReturnValue" and
preservesValue = true
}
}
private class ToASummary extends SimpleSummarizedCallable {
// `entries` is an alias of `to_a`.
ToASummary() { this = ["to_a", "entries"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Receiver" and
output = "ReturnValue" and
preservesValue = true
}
}
private class UniqSummary extends SimpleSummarizedCallable {
UniqSummary() { this = "uniq" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "ArrayElement of Receiver" and
output = ["ArrayElement[?] of ReturnValue", "Parameter[0] of BlockArgument"] and
preservesValue = true
}
}
abstract private class ZipSummary extends SummarizedCallable {
MethodCall mc;
bindingset[this]
ZipSummary() { mc.getMethodName() = "zip" }
override MethodCall getACall() { result = mc }
}
private class ZipBlockSummary extends ZipSummary {
ZipBlockSummary() { this = "zip(block)" and exists(mc.getBlock()) }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
input = "ArrayElement of Receiver" and
output = "ArrayElement[0] of Parameter[0] of BlockArgument"
or
exists(int j | j in [0 .. 5] |
input = "ArrayElement of Argument[" + j + "]" and
output = "ArrayElement[" + (j + 1) + "] of Parameter[0] of BlockArgument"
)
) and
preservesValue = true
}
}
private class ZipNoBlockSummary extends ZipSummary {
ZipNoBlockSummary() { this = "zip(no_block)" and not exists(mc.getBlock()) }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
// receiver[i] -> return_value[i][0]
exists(ArrayIndex i |
input = "ArrayElement[" + i + "] of Receiver" and
output = "ArrayElement[0] of ArrayElement[" + i + "] of ReturnValue"
)
or
// receiver[?] -> return_value[0][?]
input = "ArrayElement[?] of Receiver" and
output = "ArrayElement[0] of ArrayElement[?] of ReturnValue"
or
// arg_j[i] -> return_value[i][j+1]
exists(ArrayIndex i, int j | j in [0 .. 5] |
input = "ArrayElement[" + i + "] of Argument[" + j + "]" and
output = "ArrayElement[" + (j + 1) + "] of ArrayElement[" + i + "] of ReturnValue"
)
or
// arg_j[?] -> return_value[?][j+1]
exists(int j | j in [0 .. 5] |
input = "ArrayElement[?] of Argument[" + j + "]" and
output = "ArrayElement[" + (j + 1) + "] of ArrayElement[?] of ReturnValue"
)
) and
preservesValue = true
}
}
}

View File

@@ -179,254 +179,251 @@ end
def m22
a = [0, 1, source(22)]
a.clear()
sink(a[2])
a.chunk do |x|
sink x # $ hasValueFlow=22
end
end
def m23
a = [0, 1, source(23)]
b = a.collect do |x|
sink x # $ hasValueFlow=23
x
a = [0, 1, source(23.1), source(23.2)]
b = a.chunk_while do |x, y|
sink x # $ hasValueFlow=23.1 $ hasValueFlow=23.2
sink y # $ hasValueFlow=23.1 $ hasValueFlow=23.2
x > y
end
sink(b[0]) # $ hasValueFlow=23
end
def m24
a = [0, 1, source(24)]
b = a.collect_concat do |x|
sink x # $ hasValueFlow=24
[x, x]
end
sink(b[0]) # $ hasValueFlow=24
a.clear()
sink(a[2])
end
def m25
a = [0, 1, source(25)]
a.combination(1) do |x|
sink(x[0]) # $ hasValueFlow=25
b = a.collect do |x|
sink x # $ hasValueFlow=25
x
end
sink(b[0]) # $ hasValueFlow=25
end
def m26
a = [0, 1, source(26)]
b = a.compact
b = a.collect_concat do |x|
sink x # $ hasValueFlow=26
[x, x]
end
sink(b[0]) # $ hasValueFlow=26
end
def m27
a = [0, 1, source(27.1)]
b = [0, 1, source(27.2)]
a.concat(b)
sink(a[0]) # $ hasValueFlow=27.2
sink(a[2]) # $ hasValueFlow=27.1 $ hasValueFlow=27.2
a = [0, 1, source(27)]
a.combination(1) do |x|
sink(x[0]) # $ hasValueFlow=27
end
end
def m28
a = [0, 1, source(28)]
a.count do |x|
sink x # $ hasValueFlow=28
end
b = a.compact
sink(b[0]) # $ hasValueFlow=28
end
def m29
a = [0, 1, source(29)]
a.cycle(2) do |x|
sink x # $ hasValueFlow=29
end
a = [0, 1, source(29.1)]
b = [0, 1, source(29.2)]
a.concat(b)
sink(a[0]) # $ hasValueFlow=29.2
sink(a[2]) # $ hasValueFlow=29.1 $ hasValueFlow=29.2
end
def m30
a = [0, 1, source(30.1)]
b = a.delete(2) { source(30.2) }
sink b # $ hasValueFlow=30.1 $ hasValueFlow=30.2
a = [0, 1, source(30)]
a.count do |x|
sink x # $ hasValueFlow=30
end
end
def m31
a = [0, 1, source(31)]
b = a.delete_at(2)
sink b # $ hasValueFlow=31
a.cycle(2) do |x|
sink x # $ hasValueFlow=31
end
end
def m32
a = [0, 1, source(32)]
b = a.delete_if do |x|
sink x # $ hasValueFlow=32
end
sink(b[0]) # $ hasValueFlow=32
a = [0, 1, source(32.1)]
b = a.delete(2) { source(32.2) }
sink b # $ hasValueFlow=32.1 $ hasValueFlow=32.2
end
def m33
a = [0, 1, source(33)]
b = a.difference([1])
sink(b[0]) # $ hasValueFlow=33
b = a.delete_at(2)
sink b # $ hasValueFlow=33
end
def m34(i)
a = [0, 1, source(34.1), [0, source(34.2)]]
sink(a.dig(0))
sink(a.dig(2)) # $ hasValueFlow=34.1
sink(a.dig(i)) # $ hasValueFlow=34.1
sink(a.dig(3,0))
sink(a.dig(3,1)) # $ hasValueFlow=34.2
def m34
a = [0, 1, source(34)]
b = a.delete_if do |x|
sink x # $ hasValueFlow=34
end
sink(b[0]) # $ hasValueFlow=34
end
def m35
a = [0, 1, source(35.1)]
b = a.detect(-> { source(35.2) }) do |x|
sink x # $ hasValueFlow=35.1
end
sink b # $ hasValueFlow=35.1 $ hasValueFlow=35.2
a = [0, 1, source(35)]
b = a.difference([1])
sink(b[0]) # $ hasValueFlow=35
end
def m36(i)
a = [0, 1, source(36.1), source(36.2)]
b = a.drop(i)
sink(b[0]) # $ hasValueFlow=36.1 # $ hasValueFlow=36.2
b = a.drop(1)
sink(b[0])
sink(b[1]) # $ hasValueFlow=36.1
sink(b[i]) # $ hasValueFlow=36.1 # $ hasValueFlow=36.2
a[i] = source(36.3)
b = a.drop(1)
sink(b[1]) # $ hasValueFlow=36.1 # $ hasValueFlow=36.3
c = b.drop(100)
sink(c[1]) # $ hasValueFlow=36.3
a = [0, 1, source(36.1), [0, source(36.2)]]
sink(a.dig(0))
sink(a.dig(2)) # $ hasValueFlow=36.1
sink(a.dig(i)) # $ hasValueFlow=36.1
sink(a.dig(3,0))
sink(a.dig(3,1)) # $ hasValueFlow=36.2
end
def m37
a = [0, 1, source(37.1), source(37.2)]
b = a.drop_while do |x|
sink x # $ hasValueFlow=37.1 # $ hasValueFlow=37.2
a = [0, 1, source(37.1)]
b = a.detect(-> { source(37.2) }) do |x|
sink x # $ hasValueFlow=37.1
end
sink(b[0]) # $ hasValueFlow=37.1 # $ hasValueFlow=37.2
sink b # $ hasValueFlow=37.1 $ hasValueFlow=37.2
end
def m38
a = [0, 1, source(38)]
b = a.each do |x|
sink x # $ hasValueFlow=38
end
sink(b[2]) # $ hasValueFlow=38
def m38(i)
a = [0, 1, source(38.1), source(38.2)]
b = a.drop(i)
sink(b[0]) # $ hasValueFlow=38.1 # $ hasValueFlow=38.2
b = a.drop(1)
sink(b[0])
sink(b[1]) # $ hasValueFlow=38.1
sink(b[i]) # $ hasValueFlow=38.1 # $ hasValueFlow=38.2
a[i] = source(38.3)
b = a.drop(1)
sink(b[1]) # $ hasValueFlow=38.1 # $ hasValueFlow=38.3
c = b.drop(100)
sink(c[1]) # $ hasValueFlow=38.3
end
def m39
a = [0, 1, source(39)]
b = for x in a # desugars to an `each` call
sink x # $ hasValueFlow=39
a = [0, 1, source(39.1), source(39.2)]
b = a.drop_while do |x|
sink x # $ hasValueFlow=39.1 # $ hasValueFlow=39.2
end
sink x # $ hasValueFlow=39
sink(b[2]) # $ hasValueFlow=39
sink(b[0]) # $ hasValueFlow=39.1 # $ hasValueFlow=39.2
end
def m40
a = [0, 1, source(40)]
a.each_cons(2) do |x|
sink (x[0]) # $ hasValueFlow=40
b = a.each do |x|
sink x # $ hasValueFlow=40
end
sink(b[2]) # $ hasValueFlow=40
end
def m41
a = [0, 1, source(41)]
b = a.each_entry do |x|
b = for x in a # desugars to an `each` call
sink x # $ hasValueFlow=41
end
sink x # $ hasValueFlow=41
sink(b[2]) # $ hasValueFlow=41
end
def m42
a = [0, 1, source(42)]
b = a.each_index do |x|
sink x
a.each_cons(2) do |x|
sink (x[0]) # $ hasValueFlow=42
end
sink(b[2]) # $ hasValueFlow=42
end
def m43
a = [0, 1, 2, source(43)]
a.each_slice(1) do |x|
sink(x[0]) # $ hasValueFlow=43
a = [0, 1, source(43)]
b = a.each_entry do |x|
sink x # $ hasValueFlow=43
end
sink(b[2]) # $ hasValueFlow=43
end
def m44
a = [0, 1, 2, source(44)]
b = a.each_with_index do |x,i|
sink(x) # $ hasValueFlow=44
sink(i)
a = [0, 1, source(44)]
b = a.each_index do |x|
sink x
end
sink(b[3]) # $ hasValueFlow=44
sink(b[2]) # $ hasValueFlow=44
end
def m45
a = [0, 1, 2, source(45.1)]
b = a.each_with_object(source(45.2)) do |x,a|
sink(x) # $ hasValueFlow=45.1
sink(a) # $ hasValueFlow=45.2
a = [0, 1, 2, source(45)]
a.each_slice(1) do |x|
sink(x[0]) # $ hasValueFlow=45
end
sink(b) # $ hasValueFlow=45.2
end
def m46(i)
a = [0, 1, 2, source(46.1)]
b = a.fetch(source(46.2)) do |x|
sink(x) # $ hasValueFlow=46.2
def m46
a = [0, 1, 2, source(46)]
b = a.each_with_index do |x,i|
sink(x) # $ hasValueFlow=46
sink(i)
end
sink(b) # $ hasValueFlow=46.1
sink(b[3]) # $ hasValueFlow=46
end
def m47
a = [0, 1, 2, source(47.1)]
a.fill(source(47.2), 1, 1)
sink(a[3]) # $ hasValueFlow=47.1 $ hasValueFlow=47.2
a.fill(source(47.3))
sink(a[0]) # $ hasValueFlow=47.3
a.fill do |i|
source(47.4)
b = a.each_with_object(source(47.2)) do |x,a|
sink(x) # $ hasValueFlow=47.1
sink(a) # $ hasValueFlow=47.2
end
sink(a[0]) # $ hasValueFlow=47.4
a.fill(2) do |i|
source(47.5)
end
sink(a[0]) # $ hasValueFlow=47.4 $ hasValueFlow=47.5
sink(b) # $ hasValueFlow=47.2
end
def m48
a = [0, 1, 2, source(48)]
b = a.filter do |x|
sink(x) # $ hasValueFlow=48
end
sink(b[0]) # $ hasValueFlow=48
b = a.entries
sink(b[3]) # $ hasValueFlow=48
end
def m49
a = [0, 1, 2, source(49)]
b = a.filter_map do |x|
sink(x) # $ hasValueFlow=49
def m49(i)
a = [0, 1, 2, source(49.1)]
b = a.fetch(source(49.2)) do |x|
sink(x) # $ hasValueFlow=49.2
end
sink(b[0]) # $ hasValueFlow=49
sink(b) # $ hasValueFlow=49.1
end
def m50
a = [0, 1, 2, source(50)]
b = a.filter! do |x|
sink(x) # $ hasValueFlow=50
x > 2
a = [0, 1, 2, source(50.1)]
a.fill(source(50.2), 1, 1)
sink(a[3]) # $ hasValueFlow=50.1 $ hasValueFlow=50.2
a.fill(source(50.3))
sink(a[0]) # $ hasValueFlow=50.3
a.fill do |i|
source(50.4)
end
sink(b[0]) # $ hasValueFlow=50
sink(a[0]) # $ hasValueFlow=50.4
a.fill(2) do |i|
source(50.5)
end
sink(a[0]) # $ hasValueFlow=50.4 $ hasValueFlow=50.5
end
def m51
a = [0, 1, 2, source(51.1)]
b = a.find(-> { source(51.2) }) do |x|
sink(x) # $ hasValueFlow=51.1
a = [0, 1, 2, source(51)]
b = a.filter do |x|
sink(x) # $ hasValueFlow=51
end
sink(b) # $ hasValueFlow=51.1 $ hasValueFlow=51.2
sink(b[0]) # $ hasValueFlow=51
end
def m52
a = [0, 1, 2, source(52)]
b = a.find_all do |x|
b = a.filter_map do |x|
sink(x) # $ hasValueFlow=52
end
sink(b[0]) # $ hasValueFlow=52
@@ -434,90 +431,452 @@ end
def m53
a = [0, 1, 2, source(53)]
a.find_index do |x|
b = a.filter! do |x|
sink(x) # $ hasValueFlow=53
x > 2
end
sink(b[0]) # $ hasValueFlow=53
end
def m54(i)
a = [source(54.1), 1, 2, source(54.2)]
a[i] = source(54.3)
sink(a.first) # $ hasValueFlow=54.1 $ hasValueFlow=54.3
b = a.first(2)
sink(b[0]) # $ hasValueFlow=54.1 $ hasValueFlow=54.3
sink(b[4]) # $ hasValueFlow=54.3
c = a.first(i)
sink(c[0]) # $ hasValueFlow=54.1 $ hasValueFlow=54.3
sink(c[3]) # $ hasValueFlow=54.2 $ hasValueFlow=54.3
def m54
a = [0, 1, 2, source(54.1)]
b = a.find(-> { source(54.2) }) do |x|
sink(x) # $ hasValueFlow=54.1
end
sink(b) # $ hasValueFlow=54.1 $ hasValueFlow=54.2
end
def m55
a = [0, 1, 2, source(55.1)]
b = a.flat_map do |x|
sink(x) # $ hasValueFlow=55.1
[x, source(55.2)]
a = [0, 1, 2, source(55)]
b = a.find_all do |x|
sink(x) # $ hasValueFlow=55
end
sink(b[0]) # $ hasValueFlow=55.1 $ hasValueFlow=55.2
sink(b[0]) # $ hasValueFlow=55
end
def m56
a = [0, 1, [2, source(56)]]
b = a.flatten
sink(b[0]) # $ hasValueFlow=56
a = [0, 1, 2, source(56)]
a.find_index do |x|
sink(x) # $ hasValueFlow=56
end
end
def m57
a = [0, 1, [2, source(57)]]
sink(a[2][1]) # $ hasValueFlow=57
a.flatten!
sink(a[0]) # $ hasValueFlow=57
sink(a[2][1]) # $ SPURIOUS: hasValueFlow=57
def m57(i)
a = [source(57.1), 1, 2, source(57.2)]
a[i] = source(57.3)
sink(a.first) # $ hasValueFlow=57.1 $ hasValueFlow=57.3
b = a.first(2)
sink(b[0]) # $ hasValueFlow=57.1 $ hasValueFlow=57.3
sink(b[4]) # $ hasValueFlow=57.3
c = a.first(i)
sink(c[0]) # $ hasValueFlow=57.1 $ hasValueFlow=57.3
sink(c[3]) # $ hasValueFlow=57.2 $ hasValueFlow=57.3
end
def m58
a = [0, 1, 2, source(58.1)]
b = a.grep(/.*/)
sink(b[0]) # $ hasValueFlow=58.1
b = a.grep(/.*/) do |x|
sink x # $ hasValueFlow=58.1
source(58.2)
b = a.flat_map do |x|
sink(x) # $ hasValueFlow=58.1
[x, source(58.2)]
end
sink(b[0]) # $ hasValueFlow=58.2
sink(b[0]) # $ hasValueFlow=58.1 $ hasValueFlow=58.2
end
def m59
a = [0, 1, 2, source(59.1)]
b = a.grep_v(/A/)
sink(b[0]) # $ hasValueFlow=59.1
b = a.grep_v(/A/) do |x|
sink x # $ hasValueFlow=59.1
source(59.2)
end
sink(b[0]) # $ hasValueFlow=59.2
a = [0, 1, [2, source(59)]]
b = a.flatten
sink(b[0]) # $ hasValueFlow=59
end
def m60
a = [0, 1, 2, source(60)]
a.index do |x|
sink x # $ hasValueFlow=60
end
a = [0, 1, [2, source(60)]]
sink(a[2][1]) # $ hasValueFlow=60
a.flatten!
sink(a[0]) # $ hasValueFlow=60
sink(a[2][1]) # $ SPURIOUS: hasValueFlow=60
end
def m61
a = [0, 1, 2, source(61.1)]
a.replace([source(61.2)])
sink(a[0]) # $ hasValueFlow=61.2
b = a.grep(/.*/)
sink(b[0]) # $ hasValueFlow=61.1
b = a.grep(/.*/) do |x|
sink x # $ hasValueFlow=61.1
source(61.2)
end
sink(b[0]) # $ hasValueFlow=61.2
end
def m62
a = [0, 1, 2, source(62.1)]
b = a.grep_v(/A/)
sink(b[0]) # $ hasValueFlow=62.1
b = a.grep_v(/A/) do |x|
sink x # $ hasValueFlow=62.1
source(62.2)
end
sink(b[0]) # $ hasValueFlow=62.2
end
# TODO: assign appropriate number when reached in the alphabetical ordering
def m2600
a = [0, 1, source(2600.1)]
a.prepend(2, 3, source(2600.2))
def m63
a = [0, 1, 2, source(63.1)]
b = a.group_by do |x|
sink x # $ hasValueFlow=63.1
source 63.2
end
sink b
end
def m64
a = [0, 1, 2, source(64)]
a.index do |x|
sink x # $ hasValueFlow=64
end
end
def m65
a = [source(65.1), 1, source(65.2)]
b = a.inject do |x, y|
sink x # $ hasValueFlow=65.1
sink y # $ hasValueFlow=65.2
x + y
end
c = a.inject(0) do |x, y|
sink x
sink y # $ hasValueFlow=65.1 $ hasValueFlow=65.2
x + y
end
end
def m66
a = [0, 1, source(66)]
b = a.map do |x|
sink x # $ hasValueFlow=66
x
end
sink b[0] # $ hasValueFlow=66
end
def m67
a = [0, 1, source(67)]
# No argument or block
b = a.max
sink(b) # $ hasValueFlow=67
# Argument, no block
c = a.max(3)
sink(c[0]) # $ hasValueFlow=67
# Block, no argument
d = a.max do |x, y|
sink x # $ hasValueFlow=67
sink y # $ hasValueFlow=67
x <=> y
end
sink(d) # $ hasValueFlow=67
# Block & argument
e = a.max(3) do |x, y|
sink x # $ hasValueFlow=67
sink y # $ hasValueFlow=67
x <=> y
end
sink(e[0]) # $ hasValueFlow=67
end
def m68
a = [0, 1, source(68)]
# No argument
b = a.max_by do |x|
sink x # $ hasValueFlow=68
x
end
sink(b) # $ hasValueFlow=68
# Argument
c = a.max_by(3) do |x|
sink x # $ hasValueFlow=68
x
end
sink(c[0]) # $ hasValueFlow=68
end
def m69
a = [0, 1, source(69)]
# No argument or block
b = a.min
sink(b) # $ hasValueFlow=69
# Argument, no block
c = a.min(3)
sink(c[0]) # $ hasValueFlow=69
# Block, no argument
d = a.min do |x, y|
sink x # $ hasValueFlow=69
sink y # $ hasValueFlow=69
x <=> y
end
sink(d) # $ hasValueFlow=69
# Block & argument
e = a.min(3) do |x, y|
sink x # $ hasValueFlow=69
sink y # $ hasValueFlow=69
x <=> y
end
sink(e[0]) # $ hasValueFlow=69
end
def m70
a = [0, 1, source(70)]
# No argument
b = a.min_by do |x|
sink x # $ hasValueFlow=70
x
end
sink(b) # $ hasValueFlow=70
# Argument
c = a.min_by(3) do |x|
sink x # $ hasValueFlow=70
x
end
sink(c[0]) # $ hasValueFlow=70
end
def m71
a = [0, 1, source(71)]
b = a.minmax
sink b[0] # $ hasValueFlow=71
sink b[1] # $ hasValueFlow=71
c = a.minmax do |x, y|
sink x # $ hasValueFlow=71
sink y # $ hasValueFlow=71
x <=> y
end
sink c[0] # $ hasValueFlow=71
sink c[1] # $ hasValueFlow=71
end
def m72
a = [0, 1, source(72)]
b = a.minmax_by do |x|
sink x # $ hasValueFlow=72
x
end
sink b[0] # $ hasValueFlow=72
sink b[1] # $ hasValueFlow=72
end
def m73
a = [0, 1, source(73)]
a.none? do |x|
sink x # $ hasValueFlow=73
end
end
def m74
a = [0, 1, source(74)]
a.one? do |x|
sink x # $ hasValueFlow=74
end
end
def m75
a = [0, 1, source(75)]
b = a.partition do |x|
sink x # $ hasValueFlow=75
x > 11
end
sink b[0][0] # $ hasValueFlow=75
sink b[1][0] # $ hasValueFlow=75
end
def m76
a = [0, 1, source(76.1)]
a.prepend(2, 3, source(76.2))
sink(a[0])
sink(a[1])
sink(a[2]) # $ hasValueFlow=2600.2
sink(a[2]) # $ hasValueFlow=76.2
sink(a[3])
sink(a[4])
sink(a[5]) # $ hasValueFlow=2600.1
sink(a[5]) # $ hasValueFlow=76.1
end
def m77
a = [source(77.1), 1, source(77.2)]
b = a.reduce do |x, y|
sink x # $ hasValueFlow=77.1
sink y # $ hasValueFlow=77.2
x + y
end
c = a.reduce(0) do |x, y|
sink x
sink y # $ hasValueFlow=77.1 $ hasValueFlow=77.2
x + y
end
end
def m78
a = [0, 1, source(78)]
b = a.reject do |x|
sink x # $ hasValueFlow=78
x > 10
end
sink b[0] # $ hasValueFlow=78
end
def m79
a = [0, 1, 2, source(79.1)]
a.replace([source(79.2)])
sink(a[0]) # $ hasValueFlow=79.2
end
def m80
a = [0, 1, source(80)]
b = a.reverse_each do |x|
sink x # $ hasValueFlow=80
end
sink(b[2]) # $ hasValueFlow=80
end
def m81
a = [0, 1, 2, source(81)]
b = a.find_all do |x|
sink(x) # $ hasValueFlow=81
end
sink(b[0]) # $ hasValueFlow=81
end
def m82
a = [0, 1, source(82)]
b = a.slice_after do |x|
sink x # $ hasValueFlow=82
x > 10
end
end
def m83
a = [0, 1, source(83)]
b = a.slice_before do |x|
sink x # $ hasValueFlow=83
x > 10
end
end
def m84
a = [0, 1, source(84)]
b = a.slice_when do |x, y|
sink x # $ hasValueFlow=84
sink y # $ hasValueFlow=84
end
end
def m85
a = [0, 1, source(85)]
b = a.sort
sink b[0] # $ hasValueFlow=85
sink b[1] # $ hasValueFlow=85
c = a.sort do |x, y|
sink x # $ hasValueFlow=85
sink y # $ hasValueFlow=85
y <=> x
end
sink c[0] # $ hasValueFlow=85
sink c[1] # $ hasValueFlow=85
end
def m86
a = [0, 1, source(86)]
b = a.sort_by do |x|
sink x # $ hasValueFlow=86
-x
end
sink b[1] # $ hasValueFlow=86
sink b[1] # $ hasValueFlow=86
end
def m87
a = [0, 1, source(86)]
b = a.sum do |x|
sink x # $ hasValueFlow=86
x * x
end
end
def m88(i)
a = [0, 1, source(88.1), source(88.2)]
b = a.take(i)
sink(b[0])
sink(b[1])
sink(b[2]) # $ hasValueFlow=88.1
sink(b[3]) # $ hasValueFlow=88.2
b = a.take(3)
sink(b[0])
sink(b[1])
sink(b[2]) # $ hasValueFlow=88.1
sink(b[3])
sink(b[i]) # $ hasValueFlow=88.1
b = a.take(100)
sink(b[0])
sink(b[1])
sink(b[2]) # $ hasValueFlow=88.1
sink(b[3]) # $ hasValueFlow=88.2
sink(b[i]) # $ hasValueFlow=88.1 $ hasValueFlow=88.2
a[i] = source(88.3)
b = a.take(3)
sink(b[2]) # $ hasValueFlow=88.1 # $ hasValueFlow=88.3
end
def m89
a = [0, 1, source(89)]
b = a.take_while do |x|
sink x # $ hasValueFlow=89
x < 11
end
sink b[0]
sink b[1]
sink b[2] # $ hasValueFlow=89
end
# TODO: test method (m90) for `tally`, once we have flow through hashes
def m91
a = [0, 1, 2, source(91)]
b = a.to_a
sink(b[3]) # $ hasValueFlow=91
end
def m92
a = [0, 1, 2, source(92.1), source(92.2)]
b = a.uniq
sink b[0] # $ hasValueFlow=92.1 $ hasValueFlow=92.2
c = a.uniq do |x|
sink x # $ hasValueFlow=92.1 $ hasValueFlow=92.2
x % 7
end
sink c[0] # $ hasValueFlow=92.1 $ hasValueFlow=92.2
end
def m93
a = [0, 1, source(93.1)]
b = [2, source(93.2), 3]
c = [source(93.3), 4, 5]
d = a.zip(b, c)
sink d[0][0]
sink d[0][2] # $ hasValueFlow=93.3
sink d[1][1] # $ hasValueFlow=93.2
sink d[2][0] # $ hasValueFlow=93.1
a.zip(b, c) do |x|
sink x[0] # $ hasValueFlow=93.1
sink x[1] # $ hasValueFlow=93.2
sink x[2] # $ hasValueFlow=93.3
end
end