Merge pull request #10650 from asgerf/rb/summarize-more

Ruby: more type-tracking steps
This commit is contained in:
Asger F
2022-10-05 19:16:56 +02:00
committed by GitHub
22 changed files with 1454 additions and 668 deletions

View File

@@ -14,6 +14,11 @@ private module Cached {
ReturnStep() or
StoreStep(TypeTrackerContent content) { basicStoreStep(_, _, content) } or
LoadStep(TypeTrackerContent content) { basicLoadStep(_, _, content) } or
LoadStoreStep(TypeTrackerContent load, TypeTrackerContent store) {
basicLoadStoreStep(_, _, load, store)
} or
WithContent(ContentFilter filter) { basicWithContentStep(_, _, filter) } or
WithoutContent(ContentFilter filter) { basicWithoutContentStep(_, _, filter) } or
JumpStep()
cached
@@ -25,7 +30,11 @@ private module Cached {
// We can't rely on `basicStoreStep` since `startInContent` might be used with
// a content that has no corresponding store.
exists(TypeTrackerContent loadContents |
basicLoadStep(_, _, loadContents) and
(
basicLoadStep(_, _, loadContents)
or
basicLoadStoreStep(_, _, loadContents, _)
) and
compatibleContents(content, loadContents)
)
}
@@ -37,7 +46,11 @@ private module Cached {
or
// As in MkTypeTracker, restrict `content` to those that might eventually match a store.
exists(TypeTrackerContent storeContent |
basicStoreStep(_, _, storeContent) and
(
basicStoreStep(_, _, storeContent)
or
basicLoadStoreStep(_, _, _, storeContent)
) and
compatibleContents(storeContent, content)
)
}
@@ -61,6 +74,14 @@ private module Cached {
or
step = JumpStep() and
result = MkTypeTracker(false, currentContents)
or
exists(ContentFilter filter | result = tt |
step = WithContent(filter) and
currentContents = filter.getAMatchingContent()
or
step = WithoutContent(filter) and
not currentContents = filter.getAMatchingContent()
)
)
or
exists(TypeTrackerContent storeContents, boolean hasCall |
@@ -75,6 +96,16 @@ private module Cached {
tt = noContentTypeTracker(hasCall) and
result = MkTypeTracker(hasCall, storeContents)
)
or
exists(
TypeTrackerContent currentContent, TypeTrackerContent store, TypeTrackerContent load,
boolean hasCall
|
step = LoadStoreStep(pragma[only_bind_into](load), pragma[only_bind_into](store)) and
compatibleContents(pragma[only_bind_into](currentContent), load) and
tt = MkTypeTracker(pragma[only_bind_into](hasCall), currentContent) and
result = MkTypeTracker(pragma[only_bind_out](hasCall), store)
)
}
pragma[nomagic]
@@ -96,6 +127,14 @@ private module Cached {
or
step = JumpStep() and
result = MkTypeBackTracker(false, content)
or
exists(ContentFilter filter | result = tbt |
step = WithContent(filter) and
content = filter.getAMatchingContent()
or
step = WithoutContent(filter) and
not content = filter.getAMatchingContent()
)
)
or
exists(TypeTrackerContent loadContents, boolean hasReturn |
@@ -110,6 +149,16 @@ private module Cached {
tbt = noContentTypeBackTracker(hasReturn) and
result = MkTypeBackTracker(hasReturn, loadContents)
)
or
exists(
TypeTrackerContent currentContent, TypeTrackerContent store, TypeTrackerContent load,
boolean hasCall
|
step = LoadStoreStep(pragma[only_bind_into](load), pragma[only_bind_into](store)) and
compatibleContents(store, pragma[only_bind_into](currentContent)) and
tbt = MkTypeBackTracker(pragma[only_bind_into](hasCall), currentContent) and
result = MkTypeBackTracker(pragma[only_bind_out](hasCall), load)
)
}
/**
@@ -146,6 +195,19 @@ private module Cached {
or
basicLoadStep(nodeFrom, nodeTo, content) and summary = LoadStep(content)
)
or
exists(TypeTrackerContent loadContent, TypeTrackerContent storeContent |
flowsToLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent) and
summary = LoadStoreStep(loadContent, storeContent)
)
or
exists(ContentFilter filter |
basicWithContentStep(nodeFrom, nodeTo, filter) and
summary = WithContent(filter)
or
basicWithoutContentStep(nodeFrom, nodeTo, filter) and
summary = WithoutContent(filter)
)
}
cached
@@ -190,6 +252,18 @@ private predicate flowsToStoreStep(
exists(Node obj | nodeTo.flowsTo(obj) and basicStoreStep(nodeFrom, obj, content))
}
/**
* Holds if `loadContent` is loaded from `nodeFrom` and written to `storeContent` of `nodeTo`.
*/
private predicate flowsToLoadStoreStep(
Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContent loadContent,
TypeTrackerContent storeContent
) {
exists(Node obj |
nodeTo.flowsTo(obj) and basicLoadStoreStep(nodeFrom, obj, loadContent, storeContent)
)
}
/**
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
*
@@ -208,6 +282,11 @@ class StepSummary extends TStepSummary {
or
exists(TypeTrackerContent content | this = LoadStep(content) | result = "load " + content)
or
exists(TypeTrackerContent load, TypeTrackerContent store |
this = LoadStoreStep(load, store) and
result = "load-store " + load + " -> " + store
)
or
this instanceof JumpStep and result = "jump"
}
}

View File

@@ -28,6 +28,14 @@ class TypeTrackerContent extends OptionalTypeTrackerContent {
/** Gets the content string representing no value. */
OptionalTypeTrackerContent noContent() { result = "" }
/**
* A label to use for `WithContent` and `WithoutContent` steps, restricting
* which `ContentSet` may pass through. Not currently used in Python.
*/
class ContentFilter extends Unit {
TypeTrackerContent getAMatchingContent() { none() }
}
pragma[inline]
predicate compatibleContents(TypeTrackerContent storeContent, TypeTrackerContent loadContent) {
storeContent = loadContent
@@ -110,6 +118,23 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, string content) {
)
}
/**
* Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
*/
predicate basicLoadStoreStep(Node nodeFrom, Node nodeTo, string loadContent, string storeContent) {
none()
}
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here.
*/
predicate basicWithoutContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) { none() }
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`.
*/
predicate basicWithContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) { none() }
/**
* A utility class that is equivalent to `boolean` but does not require type joining.
*/

View File

@@ -620,6 +620,11 @@ private predicate localFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo,
summary.toString() = "level"
}
pragma[nomagic]
private predicate hasAdjacentTypeCheckedReads(DataFlow::Node node) {
hasAdjacentTypeCheckedReads(_, _, node.asExpr(), _)
}
/**
* We exclude steps into `self` parameters and type checked variables. For those,
* we instead rely on the type of the enclosing module resp. the type being checked
@@ -629,12 +634,12 @@ private predicate localFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo,
pragma[nomagic]
private DataFlow::Node trackInstanceRec(Module tp, TypeTracker t, boolean exact, StepSummary summary) {
exists(DataFlow::Node mid | mid = trackInstance(tp, exact, t) |
StepSummary::smallstep(mid, result, summary)
StepSummary::smallstep(mid, result, summary) and
not result instanceof SelfParameterNode
or
localFlowStep(mid, result, summary)
) and
not result instanceof SelfParameterNode and
not hasAdjacentTypeCheckedReads(_, _, result.asExpr(), _)
localFlowStep(mid, result, summary) and
not hasAdjacentTypeCheckedReads(result)
)
}
pragma[nomagic]

View File

@@ -366,8 +366,7 @@ private module Cached {
cached
predicate isLocalSourceNode(Node n) {
n instanceof ParameterNode and
not n instanceof SynthHashSplatParameterNode
n instanceof ParameterNode
or
// Expressions that can't be reached from another entry definition or expression
n instanceof ExprNode and
@@ -381,7 +380,7 @@ private module Cached {
n instanceof SynthReturnNode
or
// Needed for stores in type tracking
TypeTrackerSpecific::postUpdateStoreStep(_, n, _)
TypeTrackerSpecific::storeStepIntoSourceNode(_, n, _)
}
cached
@@ -1010,6 +1009,31 @@ private ContentSet getKeywordContent(string name) {
)
}
/**
* Subset of `storeStep` that should be shared with type-tracking.
*/
predicate storeStepCommon(Node node1, ContentSet c, Node node2) {
// Wrap all key-value arguments in a synthesized hash-splat argument node
exists(CfgNodes::ExprNodes::CallCfgNode call | node2 = TSynthHashSplatArgumentNode(call) |
// symbol key
exists(ArgumentPosition keywordPos, string name |
node1.asExpr().(Argument).isArgumentOf(call, keywordPos) and
keywordPos.isKeyword(name) and
c = getKeywordContent(name)
)
or
// non-symbol key
exists(CfgNodes::ExprNodes::PairCfgNode pair, CfgNodes::ExprCfgNode key, ConstantValue cv |
node1.asExpr() = pair.getValue() and
pair = call.getAnArgument() and
key = pair.getKey() and
cv = key.getConstantValue() and
not cv.isSymbol(_) and
c.isSingleton(TKnownElementContent(cv))
)
)
}
/**
* Holds if data can flow from `node1` to `node2` via an assignment to
* content `c`.
@@ -1040,25 +1064,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
or
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2)
or
// Wrap all key-value arguments in a synthesized hash-splat argument node
exists(CfgNodes::ExprNodes::CallCfgNode call | node2 = TSynthHashSplatArgumentNode(call) |
// symbol key
exists(ArgumentPosition keywordPos, string name |
node1.asExpr().(Argument).isArgumentOf(call, keywordPos) and
keywordPos.isKeyword(name) and
c = getKeywordContent(name)
)
or
// non-symbol key
exists(CfgNodes::ExprNodes::PairCfgNode pair, CfgNodes::ExprCfgNode key, ConstantValue cv |
node1.asExpr() = pair.getValue() and
pair = call.getAnArgument() and
key = pair.getKey() and
cv = key.getConstantValue() and
not cv.isSymbol(_) and
c.isSingleton(TKnownElementContent(cv))
)
)
storeStepCommon(node1, c, node2)
}
/**

View File

@@ -77,7 +77,7 @@ private class SplatSummary extends SummarizedCallable {
private class HashSplatSummary extends SummarizedCallable {
HashSplatSummary() { this = "**(hash-splat)" }
override HashSplatExpr getACall() { any() }
override HashSplatExpr getACallSimple() { any() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[self].WithElement[any]" and

View File

@@ -394,7 +394,7 @@ module Array {
bindingset[this]
AtSummary() { mc.getMethodName() = "at" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class AtKnownSummary extends AtSummary {
@@ -525,7 +525,7 @@ module Array {
bindingset[this]
DeleteSummary() { mc.getMethodName() = "delete" }
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -612,7 +612,7 @@ module Array {
preservesValue = true
}
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class DeleteAtKnownSummary extends DeleteAtSummary {
@@ -669,7 +669,7 @@ module Array {
DeleteIfSummary() { this = lastBlockParam(mc, "delete_if", lastBlockParam) }
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -743,7 +743,7 @@ module Array {
) + ")"
}
override MethodCall getACall() { result = dig }
override MethodCall getACallSimple() { result = dig }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[self]" + buildDigInputSpec(dig) and
@@ -764,7 +764,7 @@ module Array {
)
}
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -794,7 +794,7 @@ module Array {
bindingset[this]
FetchSummary() { mc.getMethodName() = "fetch" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class FetchKnownSummary extends FetchSummary {
@@ -847,7 +847,7 @@ module Array {
bindingset[this]
FillSummary() { mc.getMethodName() = "fill" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = ["Argument[0]", "Argument[block].ReturnValue"] and
@@ -935,7 +935,7 @@ module Array {
bindingset[this]
InsertSummary() { mc.getMethodName() = "insert" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class InsertKnownSummary extends InsertSummary {
@@ -1014,7 +1014,7 @@ module Array {
preservesValue = true
}
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class KeepIfSummary extends SummarizedCallable {
@@ -1023,7 +1023,7 @@ module Array {
KeepIfSummary() { this = lastBlockParam(mc, "keep_if", lastBlockParam) }
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -1050,7 +1050,7 @@ module Array {
bindingset[this]
LastSummary() { mc.getMethodName() = "last" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class LastNoArgSummary extends LastSummary {
@@ -1104,7 +1104,7 @@ module Array {
bindingset[this]
PopSummary() { mc.getMethodName() = "pop" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class PopNoArgSummary extends PopSummary {
@@ -1142,7 +1142,12 @@ module Array {
this = mc.getMethodName() + "(" + mc.getNumberOfArguments() + ")"
}
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() {
result = mc and
// Filter out obvious 'prepend' calls in a module scope
// Including such calls is mostly harmless but also easy to filter out
not result.getReceiver().(SelfVariableAccess).getCfgScope() instanceof ModuleBase
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
exists(int num | num = mc.getNumberOfArguments() and preservesValue = true |
@@ -1206,7 +1211,7 @@ module Array {
RejectBangSummary() { this = lastBlockParam(mc, "reject!", lastBlockParam) }
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -1264,7 +1269,7 @@ module Array {
bindingset[this]
RotateSummary() { mc.getMethodName() = "rotate" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class RotateKnownSummary extends RotateSummary {
@@ -1315,7 +1320,7 @@ module Array {
bindingset[this]
RotateBangSummary() { mc.getMethodName() = "rotate!" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[self].WithoutElement[any]" and
@@ -1380,7 +1385,7 @@ module Array {
)
}
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -1407,7 +1412,7 @@ module Array {
bindingset[this]
ShiftSummary() { mc.getMethodName() = "shift" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[self].WithoutElement[any]" and
@@ -1521,7 +1526,7 @@ module Array {
preservesValue = true
}
override Call getACall() { result = mc }
override Call getACallSimple() { result = mc }
}
/** A call to `slice!` with a known integer index. */
@@ -1766,7 +1771,7 @@ module Array {
bindingset[this]
ValuesAtSummary() { mc.getMethodName() = "values_at" }
override Call getACall() { result = mc }
override Call getACallSimple() { result = mc }
}
private string getValuesAtComponent(MethodCall mc, int i) {
@@ -1929,7 +1934,7 @@ module Enumerable {
bindingset[this]
DropSummary() { mc.getMethodName() = "drop" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class DropKnownSummary extends DropSummary {
@@ -2073,7 +2078,7 @@ module Enumerable {
bindingset[this]
FirstSummary() { mc.getMethodName() = "first" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class FirstNoArgSummary extends FirstSummary {
@@ -2133,7 +2138,7 @@ module Enumerable {
bindingset[this]
GrepSummary() { mc.getMethodName() = methodName }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class GrepBlockSummary extends GrepSummary {
@@ -2184,7 +2189,7 @@ module Enumerable {
bindingset[this]
InjectSummary() { mc.getMethodName() = methodName }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class InjectNoArgSummary extends InjectSummary {
@@ -2198,7 +2203,7 @@ module Enumerable {
input = "Argument[self].Element[0]" and
output = "Argument[block].Parameter[0]"
or
exists(ArrayIndex i | i > 0 | input = "Argument[self].Element[" + i + "]") and
input = "Argument[self].Element[1..]" and
output = "Argument[block].Parameter[1]"
or
input = "Argument[block].ReturnValue" and output = "ReturnValue"
@@ -2217,7 +2222,7 @@ module Enumerable {
output = "Argument[block].Parameter[0]"
or
// Each element in the receiver is passed to the second block parameter.
exists(ArrayIndex i | input = "Argument[self].Element[" + i + "]") and
input = "Argument[self].Element[0..]" and
output = "Argument[block].Parameter[1]"
or
input = "Argument[block].ReturnValue" and output = "ReturnValue"
@@ -2237,7 +2242,7 @@ module Enumerable {
bindingset[this]
MinOrMaxBySummary() { mc.getMethodName() = methodName }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class MinOrMaxByNoArgSummary extends MinOrMaxBySummary {
@@ -2277,7 +2282,7 @@ module Enumerable {
bindingset[this]
MinOrMaxSummary() { mc.getMethodName() = methodName }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class MinOrMaxNoArgNoBlockSummary extends MinOrMaxSummary {
@@ -2343,7 +2348,7 @@ module Enumerable {
bindingset[this]
MinmaxSummary() { mc.getMethodName() = "minmax" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class MinmaxNoArgNoBlockSummary extends MinmaxSummary {
@@ -2404,7 +2409,7 @@ module Enumerable {
)
}
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any]" and
@@ -2419,7 +2424,7 @@ module Enumerable {
RejectSummary() { this = lastBlockParam(mc, "reject", lastBlockParam) }
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -2448,7 +2453,7 @@ module Enumerable {
)
}
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -2523,7 +2528,7 @@ module Enumerable {
bindingset[this]
TakeSummary() { mc.getMethodName() = "take" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class TakeKnownSummary extends TakeSummary {
@@ -2609,7 +2614,7 @@ module Enumerable {
bindingset[this]
ZipSummary() { mc.getMethodName() = "zip" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class ZipBlockSummary extends ZipSummary {

View File

@@ -179,7 +179,7 @@ module Hash {
bindingset[this]
AssocSummary() { mc.getMethodName() = "assoc" }
override MethodCall getACall() { result = mc }
override MethodCall getACallSimple() { result = mc }
}
private class AssocKnownSummary extends AssocSummary {
@@ -258,7 +258,7 @@ module Hash {
")"
}
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input =
@@ -280,7 +280,7 @@ abstract private class FetchValuesSummary extends SummarizedCallable {
bindingset[this]
FetchValuesSummary() { mc.getMethodName() = "fetch_values" }
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
(
@@ -376,7 +376,7 @@ abstract private class SliceSummary extends SummarizedCallable {
bindingset[this]
SliceSummary() { mc.getMethodName() = "slice" }
final override MethodCall getACall() { result = mc }
final override MethodCall getACallSimple() { result = mc }
}
private class SliceKnownSummary extends SliceSummary {

View File

@@ -14,6 +14,11 @@ private module Cached {
ReturnStep() or
StoreStep(TypeTrackerContent content) { basicStoreStep(_, _, content) } or
LoadStep(TypeTrackerContent content) { basicLoadStep(_, _, content) } or
LoadStoreStep(TypeTrackerContent load, TypeTrackerContent store) {
basicLoadStoreStep(_, _, load, store)
} or
WithContent(ContentFilter filter) { basicWithContentStep(_, _, filter) } or
WithoutContent(ContentFilter filter) { basicWithoutContentStep(_, _, filter) } or
JumpStep()
cached
@@ -25,7 +30,11 @@ private module Cached {
// We can't rely on `basicStoreStep` since `startInContent` might be used with
// a content that has no corresponding store.
exists(TypeTrackerContent loadContents |
basicLoadStep(_, _, loadContents) and
(
basicLoadStep(_, _, loadContents)
or
basicLoadStoreStep(_, _, loadContents, _)
) and
compatibleContents(content, loadContents)
)
}
@@ -37,7 +46,11 @@ private module Cached {
or
// As in MkTypeTracker, restrict `content` to those that might eventually match a store.
exists(TypeTrackerContent storeContent |
basicStoreStep(_, _, storeContent) and
(
basicStoreStep(_, _, storeContent)
or
basicLoadStoreStep(_, _, _, storeContent)
) and
compatibleContents(storeContent, content)
)
}
@@ -61,6 +74,14 @@ private module Cached {
or
step = JumpStep() and
result = MkTypeTracker(false, currentContents)
or
exists(ContentFilter filter | result = tt |
step = WithContent(filter) and
currentContents = filter.getAMatchingContent()
or
step = WithoutContent(filter) and
not currentContents = filter.getAMatchingContent()
)
)
or
exists(TypeTrackerContent storeContents, boolean hasCall |
@@ -75,6 +96,16 @@ private module Cached {
tt = noContentTypeTracker(hasCall) and
result = MkTypeTracker(hasCall, storeContents)
)
or
exists(
TypeTrackerContent currentContent, TypeTrackerContent store, TypeTrackerContent load,
boolean hasCall
|
step = LoadStoreStep(pragma[only_bind_into](load), pragma[only_bind_into](store)) and
compatibleContents(pragma[only_bind_into](currentContent), load) and
tt = MkTypeTracker(pragma[only_bind_into](hasCall), currentContent) and
result = MkTypeTracker(pragma[only_bind_out](hasCall), store)
)
}
pragma[nomagic]
@@ -96,6 +127,14 @@ private module Cached {
or
step = JumpStep() and
result = MkTypeBackTracker(false, content)
or
exists(ContentFilter filter | result = tbt |
step = WithContent(filter) and
content = filter.getAMatchingContent()
or
step = WithoutContent(filter) and
not content = filter.getAMatchingContent()
)
)
or
exists(TypeTrackerContent loadContents, boolean hasReturn |
@@ -110,6 +149,16 @@ private module Cached {
tbt = noContentTypeBackTracker(hasReturn) and
result = MkTypeBackTracker(hasReturn, loadContents)
)
or
exists(
TypeTrackerContent currentContent, TypeTrackerContent store, TypeTrackerContent load,
boolean hasCall
|
step = LoadStoreStep(pragma[only_bind_into](load), pragma[only_bind_into](store)) and
compatibleContents(store, pragma[only_bind_into](currentContent)) and
tbt = MkTypeBackTracker(pragma[only_bind_into](hasCall), currentContent) and
result = MkTypeBackTracker(pragma[only_bind_out](hasCall), load)
)
}
/**
@@ -146,6 +195,19 @@ private module Cached {
or
basicLoadStep(nodeFrom, nodeTo, content) and summary = LoadStep(content)
)
or
exists(TypeTrackerContent loadContent, TypeTrackerContent storeContent |
flowsToLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent) and
summary = LoadStoreStep(loadContent, storeContent)
)
or
exists(ContentFilter filter |
basicWithContentStep(nodeFrom, nodeTo, filter) and
summary = WithContent(filter)
or
basicWithoutContentStep(nodeFrom, nodeTo, filter) and
summary = WithoutContent(filter)
)
}
cached
@@ -190,6 +252,18 @@ private predicate flowsToStoreStep(
exists(Node obj | nodeTo.flowsTo(obj) and basicStoreStep(nodeFrom, obj, content))
}
/**
* Holds if `loadContent` is loaded from `nodeFrom` and written to `storeContent` of `nodeTo`.
*/
private predicate flowsToLoadStoreStep(
Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContent loadContent,
TypeTrackerContent storeContent
) {
exists(Node obj |
nodeTo.flowsTo(obj) and basicLoadStoreStep(nodeFrom, obj, loadContent, storeContent)
)
}
/**
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
*
@@ -208,6 +282,11 @@ class StepSummary extends TStepSummary {
or
exists(TypeTrackerContent content | this = LoadStep(content) | result = "load " + content)
or
exists(TypeTrackerContent load, TypeTrackerContent store |
this = LoadStoreStep(load, store) and
result = "load-store " + load + " -> " + store
)
or
this instanceof JumpStep and result = "jump"
}
}

View File

@@ -17,6 +17,10 @@ class TypeTrackingNode = DataFlowPublic::LocalSourceNode;
class TypeTrackerContent = DataFlowPublic::ContentSet;
private module SCS = SummaryComponentStack;
private module SC = SummaryComponent;
/**
* An optional content set, that is, a `ContentSet` or the special "no content set" value.
*/
@@ -30,6 +34,29 @@ class OptionalTypeTrackerContent extends DataFlowPrivate::TOptionalContentSet {
}
}
private newtype TContentFilter = MkElementFilter()
/**
* A label to use for `WithContent` and `WithoutContent` steps, restricting
* which `ContentSet` may pass through.
*/
class ContentFilter extends TContentFilter {
/** Gets a string representation of this content filter. */
string toString() { this = MkElementFilter() and result = "elements" }
/** Gets the content of a type-tracker that matches this filter. */
TypeTrackerContent getAMatchingContent() {
this = MkElementFilter() and
result.getAReadContent() instanceof DataFlow::Content::ElementContent
}
}
/** Module for getting `ContentFilter` values. */
module ContentFilter {
/** Gets the filter that only allow element contents. */
ContentFilter hasElements() { result = MkElementFilter() }
}
/**
* Holds if a value stored with `storeContents` can be read back with `loadContents`.
*/
@@ -62,6 +89,16 @@ private predicate summarizedLocalStep(Node nodeFrom, Node nodeTo) {
.flowsTo(returnNode) and
callStep(nodeTo.asExpr(), nodeFrom, param)
)
or
exists(
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
SummaryComponentStack output
|
callable.propagatesFlow(input, output, true) and
call.asExpr().getExpr() = callable.getACallSimple() and
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
)
}
/** Holds if there is a level step from `nodeFrom` to `nodeTo`. */
@@ -168,16 +205,17 @@ predicate returnStep(Node nodeFrom, Node nodeTo) {
* called.
*/
predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
postUpdateStoreStep(nodeFrom, nodeTo, contents)
storeStepIntoSourceNode(nodeFrom, nodeTo, contents)
or
exists(
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponent input,
SummaryComponent output
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
SummaryComponentStack output
|
hasStoreSummary(callable, contents, input, output) and
hasStoreSummary(callable, contents, pragma[only_bind_into](input),
pragma[only_bind_into](output)) and
call.asExpr().getExpr() = callable.getACallSimple() and
nodeFrom = evaluateSummaryComponentLocal(call, input) and
nodeTo = evaluateSummaryComponentLocal(call, output)
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
)
}
@@ -185,7 +223,7 @@ predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet conten
* Holds if a store step `nodeFrom -> nodeTo` with `contents` exists, where the destination node
* is a post-update node that should be treated as a local source node.
*/
predicate postUpdateStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
predicate storeStepIntoSourceNode(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
// TODO: support SetterMethodCall inside TuplePattern
exists(ExprNodes::MethodCallCfgNode call |
contents
@@ -197,6 +235,8 @@ predicate postUpdateStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet c
call.getArgument(call.getNumberOfArguments() - 1) =
nodeFrom.(DataFlowPublic::ExprNode).getExprNode()
)
or
DataFlowPrivate::storeStepCommon(nodeFrom, contents, nodeTo)
}
/**
@@ -211,13 +251,63 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet content
)
or
exists(
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponent input,
SummaryComponent output
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
SummaryComponentStack output
|
hasLoadSummary(callable, contents, input, output) and
hasLoadSummary(callable, contents, pragma[only_bind_into](input), pragma[only_bind_into](output)) and
call.asExpr().getExpr() = callable.getACallSimple() and
nodeFrom = evaluateSummaryComponentLocal(call, input) and
nodeTo = evaluateSummaryComponentLocal(call, output)
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
)
}
/**
* Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
*/
predicate basicLoadStoreStep(
Node nodeFrom, Node nodeTo, DataFlow::ContentSet loadContent, DataFlow::ContentSet storeContent
) {
exists(
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
SummaryComponentStack output
|
hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input),
pragma[only_bind_into](output)) and
call.asExpr().getExpr() = callable.getACallSimple() and
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
)
}
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here.
*/
predicate basicWithoutContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) {
exists(
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
SummaryComponentStack output
|
hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input),
pragma[only_bind_into](output)) and
call.asExpr().getExpr() = callable.getACallSimple() and
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
)
}
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`.
*/
predicate basicWithContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) {
exists(
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
SummaryComponentStack output
|
hasWithContentSummary(callable, filter, pragma[only_bind_into](input),
pragma[only_bind_into](output)) and
call.asExpr().getExpr() = callable.getACallSimple() and
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
)
}
@@ -230,22 +320,114 @@ class Boolean extends boolean {
private import SummaryComponentStack
pragma[nomagic]
private predicate hasStoreSummary(
SummarizedCallable callable, DataFlow::ContentSet contents, SummaryComponent input,
SummaryComponent output
SummarizedCallable callable, DataFlow::ContentSet contents, SummaryComponentStack input,
SummaryComponentStack output
) {
callable
.propagatesFlow(singleton(input),
push(SummaryComponent::content(contents), singleton(output)), true)
callable.propagatesFlow(input, push(SummaryComponent::content(contents), output), true) and
not isNonLocal(input.head()) and
not isNonLocal(output.head())
}
pragma[nomagic]
private predicate hasLoadSummary(
SummarizedCallable callable, DataFlow::ContentSet contents, SummaryComponent input,
SummaryComponent output
SummarizedCallable callable, DataFlow::ContentSet contents, SummaryComponentStack input,
SummaryComponentStack output
) {
callable.propagatesFlow(push(SummaryComponent::content(contents), input), output, true) and
not isNonLocal(input.head()) and
not isNonLocal(output.head())
}
pragma[nomagic]
private predicate hasLoadStoreSummary(
SummarizedCallable callable, DataFlow::ContentSet loadContents,
DataFlow::ContentSet storeContents, SummaryComponentStack input, SummaryComponentStack output
) {
callable
.propagatesFlow(push(SummaryComponent::content(contents), singleton(input)),
singleton(output), true)
.propagatesFlow(push(SummaryComponent::content(loadContents), input),
push(SummaryComponent::content(storeContents), output), true) and
not isNonLocal(input.head()) and
not isNonLocal(output.head())
}
/**
* Gets a content filter to use for a `WithoutContent[content]` step, or has no result if
* the step should be treated as ordinary flow.
*
* `WithoutContent` is often used to perform strong updates on individual collection elements, but for
* type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful
* for restricting the type of an object, and in these cases we translate it to a filter.
*/
private ContentFilter getFilterFromWithoutContentStep(DataFlow::ContentSet content) {
(
content.isAnyElement()
or
content.isElementLowerBoundOrUnknown(_)
or
content.isSingleton(any(DataFlow::Content::UnknownElementContent c))
) and
result = MkElementFilter()
}
pragma[nomagic]
private predicate hasWithoutContentSummary(
SummarizedCallable callable, ContentFilter filter, SummaryComponentStack input,
SummaryComponentStack output
) {
exists(DataFlow::ContentSet content |
callable.propagatesFlow(push(SummaryComponent::withoutContent(content), input), output, true) and
filter = getFilterFromWithoutContentStep(content) and
not isNonLocal(input.head()) and
not isNonLocal(output.head()) and
input != output
)
}
/**
* Gets a content filter to use for a `WithContent[content]` step, or has no result if
* the step cannot be handled by type-tracking.
*
* `WithContent` is often used to perform strong updates on individual collection elements (or rather
* to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive.
* However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter.
*/
private ContentFilter getFilterFromWithContentStep(DataFlow::ContentSet content) {
(
content.isAnyElement()
or
content.isElementLowerBound(_)
or
content.isElementLowerBoundOrUnknown(_)
or
content.isSingleton(any(DataFlow::Content::ElementContent c))
) and
result = MkElementFilter()
}
pragma[nomagic]
private predicate hasWithContentSummary(
SummarizedCallable callable, ContentFilter filter, SummaryComponentStack input,
SummaryComponentStack output
) {
exists(DataFlow::ContentSet content |
callable.propagatesFlow(push(SummaryComponent::withContent(content), input), output, true) and
filter = getFilterFromWithContentStep(content) and
not isNonLocal(input.head()) and
not isNonLocal(output.head()) and
input != output
)
}
/**
* Holds if the given component can't be evaluated by `evaluateSummaryComponentStackLocal`.
*/
pragma[nomagic]
predicate isNonLocal(SummaryComponent component) {
component = SC::content(_)
or
component = SC::withContent(_)
}
/**
@@ -253,8 +435,8 @@ private predicate hasLoadSummary(
* as specified by `component`.
*/
bindingset[call, component]
private DataFlowPublic::Node evaluateSummaryComponentLocal(
DataFlowPublic::CallNode call, SummaryComponent component
private DataFlow::Node evaluateSummaryComponentLocal(
DataFlow::CallNode call, SummaryComponent component
) {
exists(DataFlowDispatch::ParameterPosition pos |
component = SummaryComponent::argument(pos) and
@@ -264,3 +446,81 @@ private DataFlowPublic::Node evaluateSummaryComponentLocal(
component = SummaryComponent::return() and
result = call
}
/**
* Holds if `callable` is relevant for type-tracking and we therefore want `stack` to
* be evaluated locally at its call sites.
*/
pragma[nomagic]
private predicate dependsOnSummaryComponentStack(
SummarizedCallable callable, SummaryComponentStack stack
) {
exists(callable.getACallSimple()) and
(
callable.propagatesFlow(stack, _, true)
or
callable.propagatesFlow(_, stack, true)
)
or
dependsOnSummaryComponentStackCons(callable, _, stack)
}
pragma[nomagic]
private predicate dependsOnSummaryComponentStackCons(
SummarizedCallable callable, SummaryComponent head, SummaryComponentStack tail
) {
dependsOnSummaryComponentStack(callable, SCS::push(head, tail))
}
pragma[nomagic]
private predicate dependsOnSummaryComponentStackConsLocal(
SummarizedCallable callable, SummaryComponent head, SummaryComponentStack tail
) {
dependsOnSummaryComponentStackCons(callable, head, tail) and
not isNonLocal(head)
}
pragma[nomagic]
private predicate dependsOnSummaryComponentStackLeaf(
SummarizedCallable callable, SummaryComponent leaf
) {
dependsOnSummaryComponentStack(callable, SCS::singleton(leaf))
}
/**
* Gets a data flow node corresponding to the local input or output of `call`
* identified by `stack`, if possible.
*/
pragma[nomagic]
private DataFlow::Node evaluateSummaryComponentStackLocal(
SummarizedCallable callable, DataFlow::CallNode call, SummaryComponentStack stack
) {
exists(SummaryComponent component |
dependsOnSummaryComponentStackLeaf(callable, component) and
stack = SCS::singleton(component) and
call.asExpr().getExpr() = callable.getACallSimple() and
result = evaluateSummaryComponentLocal(call, component)
)
or
exists(DataFlow::Node prev, SummaryComponent head, SummaryComponentStack tail |
prev = evaluateSummaryComponentStackLocal(callable, call, tail) and
dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head),
pragma[only_bind_out](tail)) and
stack = SCS::push(pragma[only_bind_out](head), pragma[only_bind_out](tail))
|
exists(DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos |
head = SummaryComponent::parameter(apos) and
DataFlowDispatch::parameterMatch(ppos, apos) and
result.(DataFlowPrivate::ParameterNodeImpl).isSourceParameterOf(prev.asExpr().getExpr(), ppos)
)
or
head = SummaryComponent::return() and
result.(DataFlowPrivate::SynthReturnNode).getCfgScope() = prev.asExpr().getExpr()
or
exists(DataFlow::ContentSet content |
head = SummaryComponent::withoutContent(content) and
not exists(getFilterFromWithoutContentStep(content)) and
result = prev
)
)
}

View File

@@ -37,14 +37,7 @@ import codeql.ruby.AST
import codeql.ruby.DataFlow
import codeql.ruby.TaintTracking
import TestUtilities.InlineExpectationsTest
private predicate defaultSource(DataFlow::Node src) {
src.asExpr().getExpr().(MethodCall).getMethodName() = ["source", "taint"]
}
private predicate defaultSink(DataFlow::Node sink) {
exists(MethodCall mc | mc.getMethodName() = "sink" | sink.asExpr().getExpr() = mc.getAnArgument())
}
import TestUtilities.InlineFlowTestUtil
class DefaultValueFlowConf extends DataFlow::Configuration {
DefaultValueFlowConf() { this = "qltest:defaultValueFlowConf" }
@@ -66,11 +59,6 @@ class DefaultTaintFlowConf extends TaintTracking::Configuration {
override int fieldFlowBranchLimit() { result = 1000 }
}
private string getSourceArgString(DataFlow::Node src) {
defaultSource(src) and
src.asExpr().getExpr().(MethodCall).getAnArgument().getConstantValue().toString() = result
}
class InlineFlowTest extends InlineExpectationsTest {
InlineFlowTest() { this = "HasFlowTest" }

View File

@@ -0,0 +1,22 @@
/**
* Defines the default source and sink recognition for `InlineFlowTest.qll`.
*
* We reuse these predicates in some type-tracking tests that don't wish to bring in the
* test configuration from `InlineFlowTest`.
*/
import codeql.ruby.AST
import codeql.ruby.DataFlow
predicate defaultSource(DataFlow::Node src) {
src.asExpr().getExpr().(MethodCall).getMethodName() = ["source", "taint"]
}
predicate defaultSink(DataFlow::Node sink) {
exists(MethodCall mc | mc.getMethodName() = "sink" | sink.asExpr().getExpr() = mc.getAnArgument())
}
string getSourceArgString(DataFlow::Node src) {
defaultSource(src) and
src.asExpr().getExpr().(MethodCall).getAnArgument().getConstantValue().toString() = result
}

View File

@@ -0,0 +1,33 @@
import ruby
import TestUtilities.InlineExpectationsTest
import TestUtilities.InlineFlowTestUtil
private import codeql.ruby.typetracking.TypeTracker
private DataFlow::LocalSourceNode track(TypeTracker t, DataFlow::CallNode source) {
t.start() and
defaultSource(source) and
result = source
or
exists(TypeTracker t2 | result = track(t2, source).track(t2, t))
}
DataFlow::LocalSourceNode track(DataFlow::CallNode source) {
result = track(TypeTracker::end(), source)
}
class TypeTrackingFlowTest extends InlineExpectationsTest {
TypeTrackingFlowTest() { this = "TypeTrackingFlowTest" }
override string getARelevantTag() { result = "hasValueFlow" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(DataFlow::Node sink, DataFlow::Node source |
defaultSink(sink) and
track(source).flowsTo(sink) and
location = sink.getLocation() and
element = sink.toString() and
tag = "hasValueFlow" and
value = getSourceArgString(source)
)
}
}

View File

@@ -14,7 +14,7 @@ Foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz")
Const = [1, 2, 3] #$ use=getMember("Array").getMethod("[]").getReturn()
Const.each do |c| #$ use=getMember("Const").getMethod("each").getReturn() def=getMember("Const").getMethod("each").getBlock()
puts c #$ use=getMember("Const").getMethod("each").getBlock().getParameter(0)
puts c #$ use=getMember("Const").getMethod("each").getBlock().getParameter(0) use=getMember("Const").getContent(element)
end
foo = Foo #$ use=getMember("Foo")

View File

@@ -0,0 +1,167 @@
| array_flow.rb:3:16:3:35 | # $ hasValueFlow=0.1 | Missing result:hasValueFlow=0.1 |
| array_flow.rb:5:16:5:35 | # $ hasValueFlow=0.1 | Missing result:hasValueFlow=0.1 |
| array_flow.rb:83:13:83:30 | # $ hasValueFlow=9 | Missing result:hasValueFlow=9 |
| array_flow.rb:107:10:107:13 | ...[...] | Unexpected result: hasValueFlow=11.2 |
| array_flow.rb:179:28:179:46 | # $ hasValueFlow=19 | Missing result:hasValueFlow=19 |
| array_flow.rb:180:28:180:46 | # $ hasValueFlow=19 | Missing result:hasValueFlow=19 |
| array_flow.rb:226:10:226:13 | ...[...] | Unexpected result: hasValueFlow=25 |
| array_flow.rb:242:14:242:14 | x | Unexpected result: hasValueFlow=27.2 |
| array_flow.rb:255:16:255:56 | # $ hasValueFlow=28.1 $ hasValueFlow=28.2 | Missing result:hasValueFlow=28.1 |
| array_flow.rb:319:10:319:13 | ...[...] | Unexpected result: hasValueFlow=36.1 |
| array_flow.rb:320:10:320:13 | ...[...] | Unexpected result: hasValueFlow=36.1 |
| array_flow.rb:321:10:321:13 | ...[...] | Unexpected result: hasValueFlow=36.1 |
| array_flow.rb:327:10:327:10 | b | Unexpected result: hasValueFlow=37.2 |
| array_flow.rb:328:10:328:13 | ...[...] | Unexpected result: hasValueFlow=37.1 |
| array_flow.rb:360:22:360:42 | # $ hasValueFlow=40.2 | Missing result:hasValueFlow=40.2 |
| array_flow.rb:374:10:374:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
| array_flow.rb:376:10:376:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
| array_flow.rb:377:10:377:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
| array_flow.rb:378:10:378:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
| array_flow.rb:407:12:407:30 | # $ hasValueFlow=45 | Missing result:hasValueFlow=45 |
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.3 |
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.4 |
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.5 |
| array_flow.rb:486:10:486:13 | ...[...] | Unexpected result: hasValueFlow=54.2 |
| array_flow.rb:486:10:486:13 | ...[...] | Unexpected result: hasValueFlow=54.4 |
| array_flow.rb:486:10:486:13 | ...[...] | Unexpected result: hasValueFlow=54.5 |
| array_flow.rb:490:10:490:13 | ...[...] | Unexpected result: hasValueFlow=54.2 |
| array_flow.rb:490:10:490:13 | ...[...] | Unexpected result: hasValueFlow=54.3 |
| array_flow.rb:490:10:490:13 | ...[...] | Unexpected result: hasValueFlow=54.5 |
| array_flow.rb:494:10:494:13 | ...[...] | Unexpected result: hasValueFlow=54.2 |
| array_flow.rb:494:10:494:13 | ...[...] | Unexpected result: hasValueFlow=54.3 |
| array_flow.rb:564:16:564:56 | # $ hasValueFlow=62.1 $ hasValueFlow=62.2 | Missing result:hasValueFlow=62.1 |
| array_flow.rb:575:16:575:34 | # $ hasValueFlow=63 | Missing result:hasValueFlow=63 |
| array_flow.rb:580:19:580:37 | # $ hasValueFlow=64 | Missing result:hasValueFlow=64 |
| array_flow.rb:582:16:582:34 | # $ hasValueFlow=64 | Missing result:hasValueFlow=64 |
| array_flow.rb:583:19:583:47 | # $ SPURIOUS: hasValueFlow=64 | Fixed spurious result:hasValueFlow=64 |
| array_flow.rb:584:16:584:34 | # $ hasValueFlow=64 | Missing result:hasValueFlow=64 |
| array_flow.rb:585:19:585:47 | # $ SPURIOUS: hasValueFlow=64 | Fixed spurious result:hasValueFlow=64 |
| array_flow.rb:646:10:646:13 | ...[...] | Unexpected result: hasValueFlow=70.1 |
| array_flow.rb:646:10:646:13 | ...[...] | Unexpected result: hasValueFlow=70.2 |
| array_flow.rb:646:10:646:13 | ...[...] | Unexpected result: hasValueFlow=70.3 |
| array_flow.rb:647:10:647:13 | ...[...] | Unexpected result: hasValueFlow=70.1 |
| array_flow.rb:647:10:647:13 | ...[...] | Unexpected result: hasValueFlow=70.3 |
| array_flow.rb:648:10:648:13 | ...[...] | Unexpected result: hasValueFlow=70.1 |
| array_flow.rb:648:10:648:13 | ...[...] | Unexpected result: hasValueFlow=70.2 |
| array_flow.rb:649:10:649:13 | ...[...] | Unexpected result: hasValueFlow=70.1 |
| array_flow.rb:649:10:649:13 | ...[...] | Unexpected result: hasValueFlow=70.2 |
| array_flow.rb:649:10:649:13 | ...[...] | Unexpected result: hasValueFlow=70.3 |
| array_flow.rb:650:10:650:13 | ...[...] | Unexpected result: hasValueFlow=70.2 |
| array_flow.rb:650:10:650:13 | ...[...] | Unexpected result: hasValueFlow=70.3 |
| array_flow.rb:651:10:651:13 | ...[...] | Unexpected result: hasValueFlow=70.1 |
| array_flow.rb:651:10:651:13 | ...[...] | Unexpected result: hasValueFlow=70.2 |
| array_flow.rb:651:10:651:13 | ...[...] | Unexpected result: hasValueFlow=70.3 |
| array_flow.rb:652:10:652:13 | ...[...] | Unexpected result: hasValueFlow=70.1 |
| array_flow.rb:652:10:652:13 | ...[...] | Unexpected result: hasValueFlow=70.3 |
| array_flow.rb:653:10:653:13 | ...[...] | Unexpected result: hasValueFlow=70.1 |
| array_flow.rb:653:10:653:13 | ...[...] | Unexpected result: hasValueFlow=70.2 |
| array_flow.rb:654:10:654:13 | ...[...] | Unexpected result: hasValueFlow=70.1 |
| array_flow.rb:654:10:654:13 | ...[...] | Unexpected result: hasValueFlow=70.2 |
| array_flow.rb:654:10:654:13 | ...[...] | Unexpected result: hasValueFlow=70.3 |
| array_flow.rb:655:10:655:13 | ...[...] | Unexpected result: hasValueFlow=70.2 |
| array_flow.rb:655:10:655:13 | ...[...] | Unexpected result: hasValueFlow=70.3 |
| array_flow.rb:708:14:708:14 | x | Unexpected result: hasValueFlow=76.2 |
| array_flow.rb:860:18:860:36 | # $ hasValueFlow=87 | Missing result:hasValueFlow=87 |
| array_flow.rb:861:18:861:36 | # $ hasValueFlow=87 | Missing result:hasValueFlow=87 |
| array_flow.rb:915:10:915:13 | ...[...] | Unexpected result: hasValueFlow=90.1 |
| array_flow.rb:915:10:915:13 | ...[...] | Unexpected result: hasValueFlow=90.2 |
| array_flow.rb:916:10:916:13 | ...[...] | Unexpected result: hasValueFlow=90.1 |
| array_flow.rb:916:10:916:13 | ...[...] | Unexpected result: hasValueFlow=90.2 |
| array_flow.rb:917:10:917:13 | ...[...] | Unexpected result: hasValueFlow=90.1 |
| array_flow.rb:918:10:918:13 | ...[...] | Unexpected result: hasValueFlow=90.1 |
| array_flow.rb:918:10:918:13 | ...[...] | Unexpected result: hasValueFlow=90.2 |
| array_flow.rb:919:10:919:13 | ...[...] | Unexpected result: hasValueFlow=90.1 |
| array_flow.rb:919:10:919:13 | ...[...] | Unexpected result: hasValueFlow=90.2 |
| array_flow.rb:920:10:920:13 | ...[...] | Unexpected result: hasValueFlow=90.2 |
| array_flow.rb:928:18:928:78 | # $ hasValueFlow=91.1 $ hasValueFlow=91.2 $ hasValueFlow=91.3 | Missing result:hasValueFlow=91.1 |
| array_flow.rb:928:18:928:78 | # $ hasValueFlow=91.1 $ hasValueFlow=91.2 $ hasValueFlow=91.3 | Missing result:hasValueFlow=91.2 |
| array_flow.rb:928:18:928:78 | # $ hasValueFlow=91.1 $ hasValueFlow=91.2 $ hasValueFlow=91.3 | Missing result:hasValueFlow=91.3 |
| array_flow.rb:929:18:929:78 | # $ hasValueFlow=91.1 $ hasValueFlow=91.2 $ hasValueFlow=91.3 | Missing result:hasValueFlow=91.1 |
| array_flow.rb:929:18:929:78 | # $ hasValueFlow=91.1 $ hasValueFlow=91.2 $ hasValueFlow=91.3 | Missing result:hasValueFlow=91.2 |
| array_flow.rb:929:18:929:78 | # $ hasValueFlow=91.1 $ hasValueFlow=91.2 $ hasValueFlow=91.3 | Missing result:hasValueFlow=91.3 |
| array_flow.rb:946:28:946:46 | # $ hasValueFlow=93 | Missing result:hasValueFlow=93 |
| array_flow.rb:947:28:947:46 | # $ hasValueFlow=93 | Missing result:hasValueFlow=93 |
| array_flow.rb:1007:16:1007:36 | # $ hasValueFlow=99.2 | Missing result:hasValueFlow=99.2 |
| array_flow.rb:1086:10:1086:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1086:10:1086:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1087:10:1087:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1088:10:1088:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1089:10:1089:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1089:10:1089:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1090:10:1090:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1090:10:1090:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1091:10:1091:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1092:10:1092:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1093:10:1093:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1093:10:1093:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1097:10:1097:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1098:10:1098:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1099:10:1099:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1099:10:1099:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1100:10:1100:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1100:10:1100:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1101:10:1101:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1102:10:1102:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1103:10:1103:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1103:10:1103:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1104:10:1104:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1104:10:1104:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1150:10:1150:10 | b | Unexpected result: hasValueFlow=108.2 |
| array_flow.rb:1151:10:1151:13 | ...[...] | Unexpected result: hasValueFlow=108.1 |
| array_flow.rb:1151:10:1151:13 | ...[...] | Unexpected result: hasValueFlow=108.2 |
| array_flow.rb:1153:10:1153:13 | ...[...] | Unexpected result: hasValueFlow=108.2 |
| array_flow.rb:1157:10:1157:13 | ...[...] | Unexpected result: hasValueFlow=108.2 |
| array_flow.rb:1159:10:1159:13 | ...[...] | Unexpected result: hasValueFlow=108.1 |
| array_flow.rb:1161:10:1161:13 | ...[...] | Unexpected result: hasValueFlow=108.2 |
| array_flow.rb:1212:10:1212:13 | ...[...] | Unexpected result: hasValueFlow=111.1 |
| array_flow.rb:1221:10:1221:13 | ...[...] | Unexpected result: hasValueFlow=111.1 |
| array_flow.rb:1226:10:1226:13 | ...[...] | Unexpected result: hasValueFlow=111.1 |
| array_flow.rb:1250:10:1250:10 | b | Unexpected result: hasValueFlow=112.2 |
| array_flow.rb:1253:10:1253:13 | ...[...] | Unexpected result: hasValueFlow=112.1 |
| array_flow.rb:1253:10:1253:13 | ...[...] | Unexpected result: hasValueFlow=112.2 |
| array_flow.rb:1274:10:1274:13 | ...[...] | Unexpected result: hasValueFlow=112.1 |
| array_flow.rb:1276:10:1276:13 | ...[...] | Unexpected result: hasValueFlow=112.2 |
| array_flow.rb:1280:10:1280:13 | ...[...] | Unexpected result: hasValueFlow=112.2 |
| array_flow.rb:1285:10:1285:13 | ...[...] | Unexpected result: hasValueFlow=112.1 |
| array_flow.rb:1287:10:1287:13 | ...[...] | Unexpected result: hasValueFlow=112.2 |
| array_flow.rb:1291:10:1291:13 | ...[...] | Unexpected result: hasValueFlow=112.2 |
| array_flow.rb:1296:10:1296:13 | ...[...] | Unexpected result: hasValueFlow=112.1 |
| array_flow.rb:1298:10:1298:13 | ...[...] | Unexpected result: hasValueFlow=112.2 |
| array_flow.rb:1330:10:1330:13 | ...[...] | Unexpected result: hasValueFlow=112.2 |
| array_flow.rb:1334:10:1334:13 | ...[...] | Unexpected result: hasValueFlow=112.1 |
| array_flow.rb:1437:10:1437:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1438:10:1438:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1439:10:1439:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1440:10:1440:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1442:10:1442:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1443:10:1443:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1444:10:1444:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1445:10:1445:13 | ...[...] | Unexpected result: hasValueFlow=121.2 |
| array_flow.rb:1445:10:1445:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1446:10:1446:13 | ...[...] | Unexpected result: hasValueFlow=121.2 |
| array_flow.rb:1446:10:1446:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1448:10:1448:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1449:10:1449:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1450:10:1450:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1451:10:1451:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1452:10:1452:13 | ...[...] | Unexpected result: hasValueFlow=121.3 |
| array_flow.rb:1500:18:1500:39 | # $ hasValueFlow=128.1 | Missing result:hasValueFlow=128.1 |
| array_flow.rb:1501:18:1501:39 | # $ hasValueFlow=128.2 | Missing result:hasValueFlow=128.2 |
| array_flow.rb:1502:18:1502:39 | # $ hasValueFlow=128.3 | Missing result:hasValueFlow=128.3 |
| array_flow.rb:1551:10:1551:13 | ...[...] | Unexpected result: hasValueFlow=132.1 |
| array_flow.rb:1551:10:1551:13 | ...[...] | Unexpected result: hasValueFlow=132.2 |
| array_flow.rb:1552:10:1552:13 | ...[...] | Unexpected result: hasValueFlow=132.1 |
| array_flow.rb:1552:10:1552:13 | ...[...] | Unexpected result: hasValueFlow=132.2 |
| array_flow.rb:1553:10:1553:13 | ...[...] | Unexpected result: hasValueFlow=132.1 |
| array_flow.rb:1554:10:1554:13 | ...[...] | Unexpected result: hasValueFlow=132.1 |
| array_flow.rb:1554:10:1554:13 | ...[...] | Unexpected result: hasValueFlow=132.2 |
| array_flow.rb:1555:10:1555:13 | ...[...] | Unexpected result: hasValueFlow=132.1 |
| array_flow.rb:1555:10:1555:13 | ...[...] | Unexpected result: hasValueFlow=132.2 |
| array_flow.rb:1556:10:1556:13 | ...[...] | Unexpected result: hasValueFlow=132.2 |
| array_flow.rb:1589:18:1589:39 | # $ hasValueFlow=134.3 | Missing result:hasValueFlow=134.3 |
| array_flow.rb:1590:18:1590:39 | # $ hasValueFlow=134.2 | Missing result:hasValueFlow=134.2 |
| array_flow.rb:1591:18:1591:39 | # $ hasValueFlow=134.1 | Missing result:hasValueFlow=134.1 |
| array_flow.rb:1611:19:1611:40 | # $ hasValueFlow=136.1 | Missing result:hasValueFlow=136.1 |
| array_flow.rb:1614:19:1614:70 | # $ hasValueFlow=136.2 $ SPURIOUS hasValueFlow=136.1 | Missing result:hasValueFlow=136.1 |
| array_flow.rb:1614:19:1614:70 | # $ hasValueFlow=136.2 $ SPURIOUS hasValueFlow=136.1 | Missing result:hasValueFlow=136.2 |
| array_flow.rb:1615:19:1615:40 | # $ hasValueFlow=136.1 | Missing result:hasValueFlow=136.1 |

View File

@@ -0,0 +1 @@
import TestUtilities.InlineTypeTrackingFlowTest

View File

@@ -0,0 +1,27 @@
| hash_flow.rb:65:21:65:40 | # $ hasValueFlow=3.3 | Missing result:hasValueFlow=3.3 |
| hash_flow.rb:66:21:66:49 | # $ SPURIOUS hasValueFlow=3.3 | Missing result:hasValueFlow=3.3 |
| hash_flow.rb:114:10:114:17 | ...[...] | Unexpected result: hasValueFlow=7.2 |
| hash_flow.rb:117:10:117:17 | ...[...] | Unexpected result: hasValueFlow=7.1 |
| hash_flow.rb:117:10:117:17 | ...[...] | Unexpected result: hasValueFlow=7.2 |
| hash_flow.rb:119:10:119:17 | ...[...] | Unexpected result: hasValueFlow=7.1 |
| hash_flow.rb:152:16:152:36 | # $ hasValueFlow=10.1 | Missing result:hasValueFlow=10.1 |
| hash_flow.rb:163:10:163:17 | ...[...] | Unexpected result: hasValueFlow=9.1 |
| hash_flow.rb:187:10:187:17 | ...[...] | Unexpected result: hasValueFlow=12.1 |
| hash_flow.rb:219:27:219:47 | # $ hasValueFlow=14.2 | Missing result:hasValueFlow=14.2 |
| hash_flow.rb:291:10:291:14 | ...[...] | Unexpected result: hasValueFlow=19.1 |
| hash_flow.rb:294:10:294:14 | ...[...] | Unexpected result: hasValueFlow=19.3 |
| hash_flow.rb:453:22:453:42 | # $ hasValueFlow=27.3 | Missing result:hasValueFlow=27.3 |
| hash_flow.rb:455:22:455:42 | # $ hasValueFlow=27.4 | Missing result:hasValueFlow=27.4 |
| hash_flow.rb:467:16:467:36 | # $ hasValueFlow=28.1 | Missing result:hasValueFlow=28.1 |
| hash_flow.rb:513:22:513:42 | # $ hasValueFlow=31.1 | Missing result:hasValueFlow=31.1 |
| hash_flow.rb:515:10:515:20 | ( ... ) | Unexpected result: hasValueFlow=31.3 |
| hash_flow.rb:515:22:515:42 | # $ hasValueFlow=31.2 | Missing result:hasValueFlow=31.2 |
| hash_flow.rb:559:17:559:57 | # $ hasValueFlow=34.1 $ hasValueFlow=34.2 | Missing result:hasValueFlow=34.1 |
| hash_flow.rb:559:17:559:57 | # $ hasValueFlow=34.1 $ hasValueFlow=34.2 | Missing result:hasValueFlow=34.2 |
| hash_flow.rb:571:18:571:38 | # $ hasValueFlow=35.1 | Missing result:hasValueFlow=35.1 |
| hash_flow.rb:591:20:591:60 | # $ hasValueFlow=36.1 $ hasValueFlow=36.2 | Missing result:hasValueFlow=36.1 |
| hash_flow.rb:591:20:591:60 | # $ hasValueFlow=36.1 $ hasValueFlow=36.2 | Missing result:hasValueFlow=36.2 |
| hash_flow.rb:668:14:668:18 | value | Unexpected result: hasValueFlow=41.3 |
| hash_flow.rb:671:10:671:19 | ( ... ) | Unexpected result: hasValueFlow=41.1 |
| hash_flow.rb:702:22:702:42 | # $ hasValueFlow=42.3 | Missing result:hasValueFlow=42.3 |
| hash_flow.rb:704:22:704:42 | # $ hasValueFlow=42.4 | Missing result:hasValueFlow=42.4 |

View File

@@ -0,0 +1 @@
import TestUtilities.InlineTypeTrackingFlowTest

View File

@@ -92,6 +92,7 @@ track
| type_tracker.rb:23:15:23:15 | 2 | type tracker with call steps | type_tracker.rb:18:20:18:21 | p2 |
| type_tracker.rb:23:15:23:15 | 2 | type tracker without call steps | type_tracker.rb:23:15:23:15 | 2 |
| type_tracker.rb:25:1:28:3 | &block | type tracker without call steps | type_tracker.rb:25:1:28:3 | &block |
| type_tracker.rb:25:1:28:3 | **kwargs | type tracker without call steps | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:25:1:28:3 | keyword | type tracker without call steps | type_tracker.rb:25:1:28:3 | keyword |
| type_tracker.rb:25:1:28:3 | return return in keyword | type tracker without call steps | type_tracker.rb:25:1:28:3 | return return in keyword |
| type_tracker.rb:25:1:28:3 | return return in keyword | type tracker without call steps | type_tracker.rb:30:1:30:21 | call to keyword |
@@ -114,39 +115,57 @@ track
| type_tracker.rb:27:5:27:11 | call to puts | type tracker without call steps | type_tracker.rb:30:1:30:21 | call to keyword |
| type_tracker.rb:27:5:27:11 | call to puts | type tracker without call steps | type_tracker.rb:31:1:31:21 | call to keyword |
| type_tracker.rb:27:5:27:11 | call to puts | type tracker without call steps | type_tracker.rb:32:1:32:27 | call to keyword |
| type_tracker.rb:30:1:30:21 | ** | type tracker with call steps | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:30:1:30:21 | ** | type tracker without call steps | type_tracker.rb:30:1:30:21 | ** |
| type_tracker.rb:30:1:30:21 | call to keyword | type tracker without call steps | type_tracker.rb:30:1:30:21 | call to keyword |
| type_tracker.rb:30:9:30:10 | :p1 | type tracker without call steps | type_tracker.rb:30:9:30:10 | :p1 |
| type_tracker.rb:30:9:30:13 | Pair | type tracker without call steps | type_tracker.rb:30:9:30:13 | Pair |
| type_tracker.rb:30:13:30:13 | 3 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:30:13:30:13 | 3 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:30:13:30:13 | 3 | type tracker with call steps with content element :p1 | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:30:13:30:13 | 3 | type tracker without call steps | type_tracker.rb:30:13:30:13 | 3 |
| type_tracker.rb:30:13:30:13 | 3 | type tracker without call steps with content element :p1 | type_tracker.rb:30:1:30:21 | ** |
| type_tracker.rb:30:16:30:17 | :p2 | type tracker without call steps | type_tracker.rb:30:16:30:17 | :p2 |
| type_tracker.rb:30:16:30:20 | Pair | type tracker without call steps | type_tracker.rb:30:16:30:20 | Pair |
| type_tracker.rb:30:20:30:20 | 4 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:30:20:30:20 | 4 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:30:20:30:20 | 4 | type tracker with call steps with content element :p2 | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:30:20:30:20 | 4 | type tracker without call steps | type_tracker.rb:30:20:30:20 | 4 |
| type_tracker.rb:30:20:30:20 | 4 | type tracker without call steps with content element :p2 | type_tracker.rb:30:1:30:21 | ** |
| type_tracker.rb:31:1:31:21 | ** | type tracker with call steps | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:31:1:31:21 | ** | type tracker without call steps | type_tracker.rb:31:1:31:21 | ** |
| type_tracker.rb:31:1:31:21 | call to keyword | type tracker without call steps | type_tracker.rb:31:1:31:21 | call to keyword |
| type_tracker.rb:31:9:31:10 | :p2 | type tracker without call steps | type_tracker.rb:31:9:31:10 | :p2 |
| type_tracker.rb:31:9:31:13 | Pair | type tracker without call steps | type_tracker.rb:31:9:31:13 | Pair |
| type_tracker.rb:31:13:31:13 | 5 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:31:13:31:13 | 5 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:31:13:31:13 | 5 | type tracker with call steps with content element :p2 | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:31:13:31:13 | 5 | type tracker without call steps | type_tracker.rb:31:13:31:13 | 5 |
| type_tracker.rb:31:13:31:13 | 5 | type tracker without call steps with content element :p2 | type_tracker.rb:31:1:31:21 | ** |
| type_tracker.rb:31:16:31:17 | :p1 | type tracker without call steps | type_tracker.rb:31:16:31:17 | :p1 |
| type_tracker.rb:31:16:31:20 | Pair | type tracker without call steps | type_tracker.rb:31:16:31:20 | Pair |
| type_tracker.rb:31:20:31:20 | 6 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:31:20:31:20 | 6 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:31:20:31:20 | 6 | type tracker with call steps with content element :p1 | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:31:20:31:20 | 6 | type tracker without call steps | type_tracker.rb:31:20:31:20 | 6 |
| type_tracker.rb:31:20:31:20 | 6 | type tracker without call steps with content element :p1 | type_tracker.rb:31:1:31:21 | ** |
| type_tracker.rb:32:1:32:27 | ** | type tracker with call steps | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:32:1:32:27 | ** | type tracker without call steps | type_tracker.rb:32:1:32:27 | ** |
| type_tracker.rb:32:1:32:27 | call to keyword | type tracker without call steps | type_tracker.rb:32:1:32:27 | call to keyword |
| type_tracker.rb:32:9:32:11 | :p2 | type tracker without call steps | type_tracker.rb:32:9:32:11 | :p2 |
| type_tracker.rb:32:9:32:16 | Pair | type tracker without call steps | type_tracker.rb:32:9:32:16 | Pair |
| type_tracker.rb:32:16:32:16 | 7 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:32:16:32:16 | 7 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:32:16:32:16 | 7 | type tracker with call steps with content element :p2 | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:32:16:32:16 | 7 | type tracker without call steps | type_tracker.rb:32:16:32:16 | 7 |
| type_tracker.rb:32:16:32:16 | 7 | type tracker without call steps with content element :p2 | type_tracker.rb:32:1:32:27 | ** |
| type_tracker.rb:32:19:32:21 | :p1 | type tracker without call steps | type_tracker.rb:32:19:32:21 | :p1 |
| type_tracker.rb:32:19:32:26 | Pair | type tracker without call steps | type_tracker.rb:32:19:32:26 | Pair |
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps with content element :p1 | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:32:26:32:26 | 8 | type tracker without call steps | type_tracker.rb:32:26:32:26 | 8 |
| type_tracker.rb:32:26:32:26 | 8 | type tracker without call steps with content element :p1 | type_tracker.rb:32:1:32:27 | ** |
| type_tracker.rb:34:1:53:3 | &block | type tracker without call steps | type_tracker.rb:34:1:53:3 | &block |
| type_tracker.rb:34:1:53:3 | return return in throughArray | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:1:53:3 | self in throughArray | type tracker without call steps | type_tracker.rb:34:1:53:3 | self in throughArray |
@@ -157,7 +176,9 @@ track
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element | type_tracker.rb:38:13:38:25 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element 0 or unknown | type_tracker.rb:35:11:35:15 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element 0 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element 0 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
@@ -169,8 +190,11 @@ track
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:40:5:40:12 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:38:13:38:25 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:35:11:35:15 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
@@ -213,21 +237,27 @@ track
| type_tracker.rb:42:14:42:26 | call to [] | type tracker without call steps | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:42:15:42:15 | 1 | type tracker without call steps | type_tracker.rb:42:15:42:15 | 1 |
| type_tracker.rb:42:15:42:15 | 1 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:15:42:15 | 1 | type tracker without call steps with content element | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:15:42:15 | 1 | type tracker without call steps with content element 0 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:42:17:42:17 | 2 | type tracker without call steps | type_tracker.rb:42:17:42:17 | 2 |
| type_tracker.rb:42:17:42:17 | 2 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:17:42:17 | 2 | type tracker without call steps with content element | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:17:42:17 | 2 | type tracker without call steps with content element 1 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:42:19:42:19 | 3 | type tracker without call steps | type_tracker.rb:42:19:42:19 | 3 |
| type_tracker.rb:42:19:42:19 | 3 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:19:42:19 | 3 | type tracker without call steps with content element | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:19:42:19 | 3 | type tracker without call steps with content element 2 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:42:21:42:21 | 4 | type tracker without call steps | type_tracker.rb:42:21:42:21 | 4 |
| type_tracker.rb:42:21:42:21 | 4 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:21:42:21 | 4 | type tracker without call steps with content element | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:21:42:21 | 4 | type tracker without call steps with content element 3 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:42:23:42:23 | 5 | type tracker without call steps | type_tracker.rb:42:23:42:23 | 5 |
| type_tracker.rb:42:23:42:23 | 5 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:23:42:23 | 5 | type tracker without call steps with content element | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:23:42:23 | 5 | type tracker without call steps with content element 4 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:42:25:42:25 | 6 | type tracker without call steps | type_tracker.rb:42:25:42:25 | 6 |
| type_tracker.rb:42:25:42:25 | 6 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:25:42:25 | 6 | type tracker without call steps with content element | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:42:25:42:25 | 6 | type tracker without call steps with content element 5 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:43:5:43:10 | [post] array2 | type tracker without call steps | type_tracker.rb:43:5:43:10 | [post] array2 |
| type_tracker.rb:43:5:43:13 | call to []= | type tracker without call steps | type_tracker.rb:43:5:43:13 | call to []= |
@@ -262,26 +292,38 @@ track
| type_tracker.rb:50:15:50:15 | 1 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:15:50:15 | 1 | type tracker without call steps | type_tracker.rb:50:15:50:15 | 1 |
| type_tracker.rb:50:15:50:15 | 1 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:15:50:15 | 1 | type tracker without call steps with content element | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:15:50:15 | 1 | type tracker without call steps with content element | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:15:50:15 | 1 | type tracker without call steps with content element 0 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:50:17:50:17 | 2 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:17:50:17 | 2 | type tracker without call steps | type_tracker.rb:50:17:50:17 | 2 |
| type_tracker.rb:50:17:50:17 | 2 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:17:50:17 | 2 | type tracker without call steps with content element | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:17:50:17 | 2 | type tracker without call steps with content element | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:17:50:17 | 2 | type tracker without call steps with content element 1 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:50:19:50:19 | 3 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:19:50:19 | 3 | type tracker without call steps | type_tracker.rb:50:19:50:19 | 3 |
| type_tracker.rb:50:19:50:19 | 3 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:19:50:19 | 3 | type tracker without call steps with content element | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:19:50:19 | 3 | type tracker without call steps with content element | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:19:50:19 | 3 | type tracker without call steps with content element 2 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:50:21:50:21 | 4 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:21:50:21 | 4 | type tracker without call steps | type_tracker.rb:50:21:50:21 | 4 |
| type_tracker.rb:50:21:50:21 | 4 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:21:50:21 | 4 | type tracker without call steps with content element | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:21:50:21 | 4 | type tracker without call steps with content element | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:21:50:21 | 4 | type tracker without call steps with content element 3 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:50:23:50:23 | 5 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:23:50:23 | 5 | type tracker without call steps | type_tracker.rb:50:23:50:23 | 5 |
| type_tracker.rb:50:23:50:23 | 5 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:23:50:23 | 5 | type tracker without call steps with content element | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:23:50:23 | 5 | type tracker without call steps with content element | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:23:50:23 | 5 | type tracker without call steps with content element 4 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:50:25:50:25 | 6 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:25:50:25 | 6 | type tracker without call steps | type_tracker.rb:50:25:50:25 | 6 |
| type_tracker.rb:50:25:50:25 | 6 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:25:50:25 | 6 | type tracker without call steps with content element | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:25:50:25 | 6 | type tracker without call steps with content element | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:25:50:25 | 6 | type tracker without call steps with content element 5 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:51:5:51:10 | [post] array4 | type tracker without call steps | type_tracker.rb:51:5:51:10 | [post] array4 |
| type_tracker.rb:51:5:51:13 | call to []= | type tracker without call steps | type_tracker.rb:51:5:51:13 | call to []= |
@@ -430,6 +472,7 @@ trackEnd
| type_tracker.rb:23:15:23:15 | 2 | type_tracker.rb:20:10:20:11 | p2 |
| type_tracker.rb:23:15:23:15 | 2 | type_tracker.rb:23:15:23:15 | 2 |
| type_tracker.rb:25:1:28:3 | &block | type_tracker.rb:25:1:28:3 | &block |
| type_tracker.rb:25:1:28:3 | **kwargs | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:25:1:28:3 | keyword | type_tracker.rb:25:1:28:3 | keyword |
| type_tracker.rb:25:1:28:3 | return return in keyword | type_tracker.rb:25:1:28:3 | return return in keyword |
| type_tracker.rb:25:1:28:3 | return return in keyword | type_tracker.rb:30:1:30:21 | call to keyword |
@@ -460,6 +503,8 @@ trackEnd
| type_tracker.rb:27:5:27:11 | call to puts | type_tracker.rb:30:1:30:21 | call to keyword |
| type_tracker.rb:27:5:27:11 | call to puts | type_tracker.rb:31:1:31:21 | call to keyword |
| type_tracker.rb:27:5:27:11 | call to puts | type_tracker.rb:32:1:32:27 | call to keyword |
| type_tracker.rb:30:1:30:21 | ** | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:30:1:30:21 | ** | type_tracker.rb:30:1:30:21 | ** |
| type_tracker.rb:30:1:30:21 | call to keyword | type_tracker.rb:30:1:30:21 | call to keyword |
| type_tracker.rb:30:9:30:10 | :p1 | type_tracker.rb:30:9:30:10 | :p1 |
| type_tracker.rb:30:9:30:13 | Pair | type_tracker.rb:30:9:30:13 | Pair |
@@ -473,6 +518,8 @@ trackEnd
| type_tracker.rb:30:20:30:20 | 4 | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:30:20:30:20 | 4 | type_tracker.rb:27:10:27:11 | p2 |
| type_tracker.rb:30:20:30:20 | 4 | type_tracker.rb:30:20:30:20 | 4 |
| type_tracker.rb:31:1:31:21 | ** | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:31:1:31:21 | ** | type_tracker.rb:31:1:31:21 | ** |
| type_tracker.rb:31:1:31:21 | call to keyword | type_tracker.rb:31:1:31:21 | call to keyword |
| type_tracker.rb:31:9:31:10 | :p2 | type_tracker.rb:31:9:31:10 | :p2 |
| type_tracker.rb:31:9:31:13 | Pair | type_tracker.rb:31:9:31:13 | Pair |
@@ -486,6 +533,8 @@ trackEnd
| type_tracker.rb:31:20:31:20 | 6 | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:31:20:31:20 | 6 | type_tracker.rb:26:10:26:11 | p1 |
| type_tracker.rb:31:20:31:20 | 6 | type_tracker.rb:31:20:31:20 | 6 |
| type_tracker.rb:32:1:32:27 | ** | type_tracker.rb:25:1:28:3 | **kwargs |
| type_tracker.rb:32:1:32:27 | ** | type_tracker.rb:32:1:32:27 | ** |
| type_tracker.rb:32:1:32:27 | call to keyword | type_tracker.rb:32:1:32:27 | call to keyword |
| type_tracker.rb:32:9:32:11 | :p2 | type_tracker.rb:32:9:32:11 | :p2 |
| type_tracker.rb:32:9:32:16 | Pair | type_tracker.rb:32:9:32:16 | Pair |

View File

@@ -55,7 +55,7 @@ underscore
| LotsOfCapitalLetters | lots_of_capital_letters |
| invalid | invalid |
mimeTypeInstances
| action_dispatch/mime_type.rb:2:6:2:28 | Use getMember("Mime").getMethod("fetch").getReturn() |
| action_dispatch/mime_type.rb:2:6:2:28 | Use getMember("Mime").getContent(element_text/html) |
| action_dispatch/mime_type.rb:3:6:3:32 | Use getMember("Mime").getMember("Type").getMethod("new").getReturn() |
| action_dispatch/mime_type.rb:4:6:4:35 | Use getMember("Mime").getMember("Type").getMethod("lookup").getReturn() |
| action_dispatch/mime_type.rb:5:6:5:43 | Use getMember("Mime").getMember("Type").getMethod("lookup_by_extension").getReturn() |

View File

@@ -206,6 +206,9 @@ getTarget
| calls.rb:542:1:542:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:543:1:543:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:543:1:543:27 | call to baz | calls.rb:535:5:538:7 | baz |
| calls.rb:545:2:545:6 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:545:20:545:24 | call to baz | calls.rb:51:5:57:7 | baz |
| calls.rb:546:26:546:37 | call to capitalize | calls.rb:97:5:97:23 | capitalize |
| hello.rb:12:5:12:24 | call to include | calls.rb:108:5:110:7 | include |
| hello.rb:14:16:14:20 | call to hello | hello.rb:2:5:4:7 | hello |
| hello.rb:20:16:20:20 | call to super | hello.rb:13:5:15:7 | message |
@@ -319,6 +322,10 @@ unresolvedCall
| calls.rb:531:1:531:24 | call to bar |
| calls.rb:541:1:541:27 | call to foo |
| calls.rb:542:1:542:27 | call to bar |
| calls.rb:545:1:545:7 | call to [] |
| calls.rb:545:1:545:26 | call to each |
| calls.rb:546:1:546:13 | call to [] |
| calls.rb:546:1:546:39 | call to each |
| hello.rb:20:16:20:26 | ... + ... |
| hello.rb:20:16:20:34 | ... + ... |
| hello.rb:20:16:20:40 | ... + ... |

View File

@@ -28,7 +28,7 @@ module M
instance_m # NoMethodError
self.instance_m # NoMethodError
singleton_m
self.singleton_m
end
@@ -44,18 +44,18 @@ class C
include M
instance_m # NoMethodError
self.instance_m # NoMethodError
singleton_m # NoMethodError
self.singleton_m # NoMethodError
def baz
instance_m
self.instance_m
instance_m
self.instance_m
singleton_m # NoMethodError
self.singleton_m # NoMethodError
end
end
end
c = C.new
c.baz
@@ -192,16 +192,16 @@ class Singletons
puts "singleton_a"
self.singleton_b
end
def self.singleton_b
puts "singleton_b"
self.singleton_c
end
def self.singleton_c
puts "singleton_c"
end
def self.singleton_d
puts "singleton_d"
self.singleton_a
@@ -224,7 +224,7 @@ class Singletons
self.singleton_g
end
end
Singletons.singleton_a
Singletons.singleton_f
@@ -423,7 +423,7 @@ class ConditionalInstanceMethods
def m2
puts "ConditionalInstanceMethods#m2"
def m3
puts "ConditionalInstanceMethods#m3"
@@ -541,3 +541,6 @@ end
ProtectedMethodsSub.new.foo # NoMethodError
ProtectedMethodsSub.new.bar # NoMethodError
ProtectedMethodsSub.new.baz
[C.new].each { |c| c.baz }
["a","b","c"].each { |s| s.capitalize }

File diff suppressed because it is too large Load Diff