mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Merge pull request #10650 from asgerf/rb/summarize-more
Ruby: more type-tracking steps
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
22
ruby/ql/test/TestUtilities/InlineFlowTestUtil.qll
Normal file
22
ruby/ql/test/TestUtilities/InlineFlowTestUtil.qll
Normal 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
|
||||
}
|
||||
33
ruby/ql/test/TestUtilities/InlineTypeTrackingFlowTest.qll
Normal file
33
ruby/ql/test/TestUtilities/InlineTypeTrackingFlowTest.qll
Normal 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
import TestUtilities.InlineTypeTrackingFlowTest
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
import TestUtilities.InlineTypeTrackingFlowTest
|
||||
@@ -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 |
|
||||
|
||||
@@ -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() |
|
||||
|
||||
@@ -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 | ... + ... |
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user