Merge pull request #12964 from hvitved/ruby/remove-synth-returns

Ruby: Remove canonical return nodes
This commit is contained in:
Tom Hvitved
2023-06-08 10:07:48 +02:00
committed by GitHub
13 changed files with 1055 additions and 782 deletions

View File

@@ -778,7 +778,7 @@ module API {
or
exists(TypeTracker t2 |
result = trackUseNode(src, t2).track(t2, t) and
not result instanceof DataFlowPrivate::SelfParameterNode
not result instanceof DataFlow::SelfParameterNode
)
}
@@ -800,7 +800,7 @@ module API {
or
exists(TypeBackTracker t2, DataFlow::LocalSourceNode mid |
mid = trackDefNode(rhs, t2) and
not mid instanceof DataFlowPrivate::SelfParameterNode and
not mid instanceof DataFlow::SelfParameterNode and
result = mid.backtrack(t2, t)
)
}

View File

@@ -8,18 +8,21 @@ private import FlowSummaryImpl as FlowSummaryImpl
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.dataflow.SSA
private import codeql.util.Boolean
private import codeql.util.Unit
/**
* A `LocalSourceNode` for a `self` variable. This is either an implicit `self`
* parameter or an implicit SSA entry definition.
* A `LocalSourceNode` for a `self` variable. This is the implicit `self`
* parameter, when it exists, otherwise the implicit SSA entry definition.
*/
private class SelfLocalSourceNode extends DataFlow::LocalSourceNode {
private SelfVariable self;
SelfLocalSourceNode() {
self = this.(SelfParameterNode).getSelfVariable()
self = this.(SelfParameterNodeImpl).getSelfVariable()
or
self = this.(SsaSelfDefinitionNode).getVariable()
self = this.(SsaSelfDefinitionNode).getVariable() and
not LocalFlow::localFlowSsaParamInput(_, this)
}
/** Gets the `self` variable. */
@@ -470,35 +473,28 @@ private module Cached {
import Cached
pragma[nomagic]
private DataFlow::LocalSourceNode trackModuleAccess(Module m, TypeTracker t) {
t.start() and m = resolveConstantReadAccess(result.asExpr().getExpr())
or
exists(TypeTracker t2, StepSummary summary |
result = trackModuleAccessRec(m, t2, summary) and t = t2.append(summary)
)
private predicate isNotSelf(DataFlow::Node n) { not n instanceof SelfParameterNodeImpl }
private module TrackModuleInput implements CallGraphConstruction::Simple::InputSig {
class State = Module;
predicate start(DataFlow::Node start, Module m) {
m = resolveConstantReadAccess(start.asExpr().getExpr())
}
// We exclude steps into `self` parameters, and instead rely on the type of the
// enclosing module
predicate filter(DataFlow::Node n) { n instanceof SelfParameterNodeImpl }
}
/**
* We exclude steps into `self` parameters, and instead rely on the type of the
* enclosing module.
*/
pragma[nomagic]
private DataFlow::LocalSourceNode trackModuleAccessRec(Module m, TypeTracker t, StepSummary summary) {
StepSummary::step(trackModuleAccess(m, t), result, summary) and
not result instanceof SelfParameterNode
}
pragma[nomagic]
private DataFlow::LocalSourceNode trackModuleAccess(Module m) {
result = trackModuleAccess(m, TypeTracker::end())
}
predicate trackModuleAccess = CallGraphConstruction::Simple::Make<TrackModuleInput>::track/1;
pragma[nomagic]
private predicate hasUserDefinedNew(Module m) {
exists(DataFlow::MethodNode method |
// not `getAnAncestor` because singleton methods cannot be included
singletonMethodOnModule(method.asCallableAstNode(), "new", m.getSuperClass*()) and
not method.getSelfParameter().getAMethodCall("allocate").flowsTo(method.getAReturningNode())
not method.getSelfParameter().getAMethodCall("allocate").flowsTo(method.getAReturnNode())
)
}
@@ -531,141 +527,162 @@ private predicate isStandardNewCall(RelevantCall new, Module m, boolean exact) {
)
}
/** Holds if `n` is an instance of type `tp`. */
private predicate isInstance(DataFlow::Node n, Module tp, boolean exact) {
n.asExpr().getExpr() instanceof NilLiteral and
tp = TResolved("NilClass") and
exact = true
or
n.asExpr().getExpr().(BooleanLiteral).isFalse() and
tp = TResolved("FalseClass") and
exact = true
or
n.asExpr().getExpr().(BooleanLiteral).isTrue() and
tp = TResolved("TrueClass") and
exact = true
or
n.asExpr().getExpr() instanceof IntegerLiteral and
tp = TResolved("Integer") and
exact = true
or
n.asExpr().getExpr() instanceof FloatLiteral and
tp = TResolved("Float") and
exact = true
or
n.asExpr().getExpr() instanceof RationalLiteral and
tp = TResolved("Rational") and
exact = true
or
n.asExpr().getExpr() instanceof ComplexLiteral and
tp = TResolved("Complex") and
exact = true
or
n.asExpr().getExpr() instanceof StringlikeLiteral and
tp = TResolved("String") and
exact = true
or
n.asExpr() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode and
tp = TResolved("Array") and
exact = true
or
n.asExpr() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode and
tp = TResolved("Hash") and
exact = true
or
n.asExpr().getExpr() instanceof MethodBase and
tp = TResolved("Symbol") and
exact = true
or
n.asParameter() instanceof BlockParameter and
tp = TResolved("Proc") and
exact = true
or
n.asExpr().getExpr() instanceof Lambda and
tp = TResolved("Proc") and
exact = true
or
isStandardNewCall(n.asExpr(), tp, exact)
or
// `self` reference in method or top-level (but not in module or singleton method,
// where instance methods cannot be called; only singleton methods)
n =
any(SelfLocalSourceNode self |
exists(MethodBase m |
selfInMethod(self.getVariable(), m, tp) and
not m instanceof SingletonMethod and
if m.getEnclosingModule() instanceof Toplevel then exact = true else exact = false
)
or
selfInToplevel(self.getVariable(), tp) and
exact = true
)
or
// `in C => c then c.foo`
asModulePattern(n, tp) and
exact = false
or
// `case object when C then object.foo`
hasAdjacentTypeCheckedReads(_, _, n.asExpr(), tp) and
exact = false
}
pragma[nomagic]
private DataFlow::Node trackInstance(Module tp, boolean exact, TypeTracker t) {
t.start() and
(
isInstance(result, tp, exact)
or
exists(Module m |
(if m.isClass() then tp = TResolved("Class") else tp = TResolved("Module")) and
exact = true
|
// needed for e.g. `C.new`
m = resolveConstantReadAccess(result.asExpr().getExpr())
or
// needed for e.g. `self.include`
selfInModule(result.(SelfLocalSourceNode).getVariable(), m)
or
// needed for e.g. `self.puts`
selfInMethod(result.(SelfLocalSourceNode).getVariable(), any(SingletonMethod sm), m)
)
)
or
exists(TypeTracker t2, StepSummary summary |
result = trackInstanceRec(tp, t2, exact, summary) and t = t2.append(summary)
)
}
private predicate localFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
localFlowStepTypeTracker(nodeFrom, nodeTo) and
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
* against, and apply an open-world assumption when determining possible dispatch
* targets.
*/
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) and
not result instanceof SelfParameterNode
private module TrackInstanceInput implements CallGraphConstruction::InputSig {
pragma[nomagic]
private predicate isInstanceNoCall(DataFlow::Node n, Module tp, boolean exact) {
n.asExpr().getExpr() instanceof NilLiteral and
tp = TResolved("NilClass") and
exact = true
or
localFlowStep(mid, result, summary) and
not hasAdjacentTypeCheckedReads(result)
)
n.asExpr().getExpr().(BooleanLiteral).isFalse() and
tp = TResolved("FalseClass") and
exact = true
or
n.asExpr().getExpr().(BooleanLiteral).isTrue() and
tp = TResolved("TrueClass") and
exact = true
or
n.asExpr().getExpr() instanceof IntegerLiteral and
tp = TResolved("Integer") and
exact = true
or
n.asExpr().getExpr() instanceof FloatLiteral and
tp = TResolved("Float") and
exact = true
or
n.asExpr().getExpr() instanceof RationalLiteral and
tp = TResolved("Rational") and
exact = true
or
n.asExpr().getExpr() instanceof ComplexLiteral and
tp = TResolved("Complex") and
exact = true
or
n.asExpr().getExpr() instanceof StringlikeLiteral and
tp = TResolved("String") and
exact = true
or
n.asExpr() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode and
tp = TResolved("Array") and
exact = true
or
n.asExpr() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode and
tp = TResolved("Hash") and
exact = true
or
n.asExpr().getExpr() instanceof MethodBase and
tp = TResolved("Symbol") and
exact = true
or
n.asParameter() instanceof BlockParameter and
tp = TResolved("Proc") and
exact = true
or
n.asExpr().getExpr() instanceof Lambda and
tp = TResolved("Proc") and
exact = true
or
// `self` reference in method or top-level (but not in module or singleton method,
// where instance methods cannot be called; only singleton methods)
n =
any(SelfLocalSourceNode self |
exists(MethodBase m |
selfInMethod(self.getVariable(), m, tp) and
not m instanceof SingletonMethod and
if m.getEnclosingModule() instanceof Toplevel then exact = true else exact = false
)
or
selfInToplevel(self.getVariable(), tp) and
exact = true
)
or
// `in C => c then c.foo`
asModulePattern(n, tp) and
exact = false
or
// `case object when C then object.foo`
hasAdjacentTypeCheckedReads(_, _, n.asExpr(), tp) and
exact = false
}
pragma[nomagic]
private predicate isInstanceCall(DataFlow::Node n, Module tp, boolean exact) {
isStandardNewCall(n.asExpr(), tp, exact)
}
/** Holds if `n` is an instance of type `tp`. */
pragma[inline]
private predicate isInstance(DataFlow::Node n, Module tp, boolean exact) {
isInstanceNoCall(n, tp, exact)
or
isInstanceCall(n, tp, exact)
}
pragma[nomagic]
private predicate hasAdjacentTypeCheckedReads(DataFlow::Node node) {
hasAdjacentTypeCheckedReads(_, _, node.asExpr(), _)
}
newtype State = additional MkState(Module m, Boolean exact)
predicate start(DataFlow::Node start, State state) {
exists(Module tp, boolean exact | state = MkState(tp, exact) |
isInstance(start, tp, exact)
or
exists(Module m |
(if m.isClass() then tp = TResolved("Class") else tp = TResolved("Module")) and
exact = true
|
// needed for e.g. `C.new`
m = resolveConstantReadAccess(start.asExpr().getExpr())
or
// needed for e.g. `self.include`
selfInModule(start.(SelfLocalSourceNode).getVariable(), m)
or
// needed for e.g. `self.puts`
selfInMethod(start.(SelfLocalSourceNode).getVariable(), any(SingletonMethod sm), m)
)
)
}
pragma[nomagic]
predicate stepNoCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
// We exclude steps into `self` parameters. For those, we instead rely on the type of
// the enclosing module
StepSummary::smallstepNoCall(nodeFrom, nodeTo, summary) and
isNotSelf(nodeTo)
or
// We exclude steps into type checked variables. For those, we instead rely on the
// type being checked against
localFlowStep(nodeFrom, nodeTo, summary) and
not hasAdjacentTypeCheckedReads(nodeTo)
}
predicate stepCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
StepSummary::smallstepCall(nodeFrom, nodeTo, summary)
}
class StateProj = Unit;
Unit stateProj(State state) { exists(state) and exists(result) }
// We exclude steps into `self` parameters, and instead rely on the type of the
// enclosing module
predicate filter(DataFlow::Node n, Unit u) {
n instanceof SelfParameterNodeImpl and
exists(u)
}
}
pragma[nomagic]
private DataFlow::Node trackInstance(Module tp, boolean exact) {
result = trackInstance(tp, exact, TypeTracker::end())
result =
CallGraphConstruction::Make<TrackInstanceInput>::track(TrackInstanceInput::MkState(tp, exact))
}
pragma[nomagic]
@@ -706,30 +723,17 @@ private CfgScope getTargetInstance(RelevantCall call, string method) {
)
}
pragma[nomagic]
private DataFlow::LocalSourceNode trackBlock(Block block, TypeTracker t) {
t.start() and result.asExpr().getExpr() = block
or
exists(TypeTracker t2, StepSummary summary |
result = trackBlockRec(block, t2, summary) and
t = t2.append(summary)
)
private module TrackBlockInput implements CallGraphConstruction::Simple::InputSig {
class State = Block;
predicate start(DataFlow::Node start, Block block) { start.asExpr().getExpr() = block }
// We exclude steps into `self` parameters, and instead rely on the type of the
// enclosing module
predicate filter(DataFlow::Node n) { n instanceof SelfParameterNodeImpl }
}
/**
* We exclude steps into `self` parameters, which may happen when the code
* base contains implementations of `call`.
*/
pragma[nomagic]
private DataFlow::LocalSourceNode trackBlockRec(Block block, TypeTracker t, StepSummary summary) {
StepSummary::step(trackBlock(block, t), result, summary) and
not result instanceof SelfParameterNode
}
pragma[nomagic]
private DataFlow::LocalSourceNode trackBlock(Block block) {
result = trackBlock(block, TypeTracker::end())
}
private predicate trackBlock = CallGraphConstruction::Simple::Make<TrackBlockInput>::track/1;
/** Holds if `m` is a singleton method named `name`, defined on `object. */
private predicate singletonMethod(MethodBase m, string name, Expr object) {
@@ -896,92 +900,98 @@ predicate singletonMethodOnInstance(MethodBase method, string name, Expr object)
)
}
/**
* Holds if there is reverse flow from `nodeFrom` to `nodeTo` via a parameter.
*
* This is only used for tracking singleton methods, where we want to be able
* to handle cases like
*
* ```rb
* def add_singleton x
* def x.foo; end
* end
*
* y = add_singleton C.new
* y.foo
* ```
*
* and
*
* ```rb
* class C
* def add_singleton_to_self
* def self.foo; end
* end
* end
*
* y = C.new
* y.add_singleton_to_self
* y.foo
* ```
*/
pragma[nomagic]
private predicate paramReturnFlow(
DataFlow::Node nodeFrom, DataFlow::PostUpdateNode nodeTo, StepSummary summary
) {
exists(RelevantCall call, DataFlow::Node arg, DataFlow::ParameterNode p, Expr nodeFromPreExpr |
TypeTrackerSpecific::callStep(call, arg, p) and
nodeTo.getPreUpdateNode() = arg and
summary.toString() = "return" and
(
nodeFromPreExpr = nodeFrom.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr().getExpr()
private module TrackSingletonMethodOnInstanceInput implements CallGraphConstruction::InputSig {
/**
* Holds if there is reverse flow from `nodeFrom` to `nodeTo` via a parameter.
*
* This is only used for tracking singleton methods, where we want to be able
* to handle cases like
*
* ```rb
* def add_singleton x
* def x.foo; end
* end
*
* y = add_singleton C.new
* y.foo
* ```
*
* and
*
* ```rb
* class C
* def add_singleton_to_self
* def self.foo; end
* end
* end
*
* y = C.new
* y.add_singleton_to_self
* y.foo
* ```
*/
pragma[nomagic]
private predicate paramReturnFlow(
DataFlow::Node nodeFrom, DataFlow::PostUpdateNode nodeTo, StepSummary summary
) {
exists(RelevantCall call, DataFlow::Node arg, DataFlow::ParameterNode p, Expr nodeFromPreExpr |
TypeTrackerSpecific::callStep(call, arg, p) and
nodeTo.getPreUpdateNode() = arg and
summary.toString() = "return" and
(
nodeFromPreExpr = nodeFrom.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr().getExpr()
or
nodeFromPreExpr = nodeFrom.asExpr().getExpr() and
singletonMethodOnInstance(_, _, nodeFromPreExpr)
)
|
nodeFromPreExpr = p.getParameter().(NamedParameter).getVariable().getAnAccess()
or
nodeFromPreExpr = nodeFrom.asExpr().getExpr() and
singletonMethodOnInstance(_, _, nodeFromPreExpr)
nodeFromPreExpr = p.(SelfParameterNodeImpl).getSelfVariable().getAnAccess()
)
|
nodeFromPreExpr = p.getParameter().(NamedParameter).getVariable().getAnAccess()
or
nodeFromPreExpr = p.(SelfParameterNode).getSelfVariable().getAnAccess()
)
}
}
pragma[nomagic]
private DataFlow::Node trackSingletonMethodOnInstance(MethodBase method, string name, TypeTracker t) {
t.start() and
singletonMethodOnInstance(method, name, result.asExpr().getExpr())
or
exists(TypeTracker t2, StepSummary summary |
result = trackSingletonMethodOnInstanceRec(method, name, t2, summary) and
t = t2.append(summary) and
// Stop flow at redefinitions.
//
// Example:
// ```rb
// def x.foo; end
// def x.foo; end
// x.foo # <- we want to resolve this call to the second definition only
// ```
not singletonMethodOnInstance(_, name, result.asExpr().getExpr())
)
}
class State = MethodBase;
pragma[nomagic]
private DataFlow::Node trackSingletonMethodOnInstanceRec(
MethodBase method, string name, TypeTracker t, StepSummary summary
) {
exists(DataFlow::Node mid | mid = trackSingletonMethodOnInstance(method, name, t) |
StepSummary::smallstep(mid, result, summary)
predicate start(DataFlow::Node start, MethodBase method) {
singletonMethodOnInstance(method, _, start.asExpr().getExpr())
}
predicate stepNoCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
StepSummary::smallstepNoCall(nodeFrom, nodeTo, summary)
or
paramReturnFlow(mid, result, summary)
localFlowStep(nodeFrom, nodeTo, summary)
}
predicate stepCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
StepSummary::smallstepCall(nodeFrom, nodeTo, summary)
or
localFlowStep(mid, result, summary)
)
paramReturnFlow(nodeFrom, nodeTo, summary)
}
class StateProj extends string {
StateProj() { singletonMethodOnInstance(_, this, _) }
}
StateProj stateProj(MethodBase method) { singletonMethodOnInstance(method, result, _) }
// Stop flow at redefinitions.
//
// Example:
// ```rb
// def x.foo; end
// def x.foo; end
// x.foo # <- we want to resolve this call to the second definition only
// ```
predicate filter(DataFlow::Node n, StateProj name) {
singletonMethodOnInstance(_, name, n.asExpr().getExpr())
}
}
pragma[nomagic]
private DataFlow::Node trackSingletonMethodOnInstance(MethodBase method, string name) {
result = trackSingletonMethodOnInstance(method, name, TypeTracker::end())
result = CallGraphConstruction::Make<TrackSingletonMethodOnInstanceInput>::track(method) and
singletonMethodOnInstance(method, name, _)
}
/** Holds if a `self` access may be the receiver of `call` directly inside module `m`. */

View File

@@ -131,10 +131,10 @@ module LocalFlow {
/**
* Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
*/
predicate localFlowSsaParamInput(Node nodeFrom, Node nodeTo) {
predicate localFlowSsaParamInput(Node nodeFrom, SsaDefinitionExtNode nodeTo) {
nodeTo = getParameterDefNode(nodeFrom.(ParameterNodeImpl).getParameter())
or
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNode).getMethod())
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNodeImpl).getMethod())
}
/**
@@ -143,14 +143,13 @@ module LocalFlow {
*
* This is intended to recover from flow not currently recognised by ordinary capture flow.
*/
predicate localFlowSsaParamCaptureInput(Node nodeFrom, Node nodeTo) {
exists(Ssa::CapturedEntryDefinition def, ParameterNodeImpl p |
(nodeFrom = p or LocalFlow::localFlowSsaParamInput(p, nodeFrom)) and
predicate localFlowSsaParamCaptureInput(ParameterNodeImpl nodeFrom, Node nodeTo) {
exists(Ssa::CapturedEntryDefinition def |
nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def
|
p.getParameter().(NamedParameter).getVariable() = def.getSourceVariable()
nodeFrom.getParameter().(NamedParameter).getVariable() = def.getSourceVariable()
or
p.(SelfParameterNode).getSelfVariable() = def.getSourceVariable()
nodeFrom.(SelfParameterNode).getSelfVariable() = def.getSourceVariable()
)
}
@@ -164,7 +163,7 @@ module LocalFlow {
/**
* Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving
* SSA definition `def`.
* some SSA definition.
*/
private predicate localSsaFlowStep(Node nodeFrom, Node nodeTo) {
exists(SsaImpl::DefinitionExt def |
@@ -182,6 +181,8 @@ module LocalFlow {
// Flow into phi (read) SSA definition node from def
localFlowSsaInputFromDef(nodeFrom, def, nodeTo)
)
or
localFlowSsaParamInput(nodeFrom, nodeTo)
// TODO
// or
// // Flow into uncertain SSA definition
@@ -223,6 +224,13 @@ module LocalFlow {
op.getExpr() instanceof BinaryLogicalOperation and
nodeFrom.asExpr() = op.getAnOperand()
)
or
nodeTo.(ParameterNodeImpl).getParameter() =
any(NamedParameter p |
p.(OptionalParameter).getDefaultValue() = nodeFrom.asExpr().getExpr()
or
p.(KeywordParameter).getDefaultValue() = nodeFrom.asExpr().getExpr()
)
}
}
@@ -279,12 +287,6 @@ private module Cached {
newtype TNode =
TExprNode(CfgNodes::ExprCfgNode n) { TaintTrackingPrivate::forceCachingInSameStage() } or
TReturningNode(CfgNodes::ReturningCfgNode n) or
TSynthReturnNode(CfgScope scope, ReturnKind kind) {
exists(ReturningNode ret |
ret.(NodeImpl).getCfgScope() = scope and
ret.getKind() = kind
)
} or
TSsaDefinitionExtNode(SsaImpl::DefinitionExt def) or
TNormalParameterNode(Parameter p) {
p instanceof SimpleParameter or
@@ -326,12 +328,6 @@ private module Cached {
TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or
TSynthHashSplatParameterNode or TSummaryParameterNode;
private predicate defaultValueFlow(NamedParameter p, ExprNode e) {
p.(OptionalParameter).getDefaultValue() = e.getExprNode().getExpr()
or
p.(KeywordParameter).getDefaultValue() = e.getExprNode().getExpr()
}
cached
Location getLocation(NodeImpl n) { result = n.getLocationImpl() }
@@ -346,12 +342,6 @@ private module Cached {
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
defaultValueFlow(nodeTo.(ParameterNodeImpl).getParameter(), nodeFrom)
or
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
or
nodeTo.(SynthReturnNode).getAnInput() = nodeFrom
or
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo) and
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
or
@@ -373,10 +363,6 @@ private module Cached {
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
defaultValueFlow(nodeTo.(ParameterNodeImpl).getParameter(), nodeFrom)
or
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
or
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo)
or
// Simple flow through library code is included in the exposed local
@@ -386,19 +372,11 @@ private module Cached {
/**
* This is the local flow predicate that is used in type tracking.
*
* This needs to exclude `localFlowSsaParamInput` due to a performance trick
* in type tracking, where such steps are treated as call steps.
*/
cached
predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
exists(NamedParameter p |
defaultValueFlow(p, nodeFrom) and
nodeTo = LocalFlow::getParameterDefNode(p)
)
or
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo)
or
// Flow into phi node from read
@@ -440,12 +418,10 @@ private module Cached {
n instanceof ExprNode and
not reachedFromExprOrEntrySsaDef(n)
or
// Ensure all entry SSA definitions are local sources -- for parameters, this
// is needed by type tracking
entrySsaDefinition(n)
or
// Needed for flow out in type tracking
n instanceof SynthReturnNode
// Ensure all entry SSA definitions are local sources, except those that correspond
// to parameters (which are themselves local sources)
entrySsaDefinition(n) and
not LocalFlow::localFlowSsaParamInput(_, n)
or
// Needed for stores in type tracking
TypeTrackerSpecific::storeStepIntoSourceNode(_, n, _)
@@ -507,7 +483,7 @@ private module Cached {
*/
cached
predicate exprNodeReturnedFromCached(ExprNode e, Callable c) {
exists(ReturningNode r |
exists(ReturnNode r |
nodeGetEnclosingCallable(r).asCallable() = c and
(
r.(ExplicitReturnNode).getReturningNode().getReturnedValueNode() = e.asExpr() or
@@ -542,8 +518,6 @@ predicate nodeIsHidden(Node n) {
or
n instanceof SummaryParameterNode
or
n instanceof SynthReturnNode
or
n instanceof SynthHashSplatParameterNode
or
n instanceof SynthHashSplatArgumentNode
@@ -658,10 +632,10 @@ private module ParameterNodes {
* The value of the `self` parameter at function entry, viewed as a node in a data
* flow graph.
*/
class SelfParameterNode extends ParameterNodeImpl, TSelfParameterNode {
class SelfParameterNodeImpl extends ParameterNodeImpl, TSelfParameterNode {
private MethodBase method;
SelfParameterNode() { this = TSelfParameterNode(method) }
SelfParameterNodeImpl() { this = TSelfParameterNode(method) }
final MethodBase getMethod() { result = method }
@@ -937,24 +911,26 @@ private class NewCall extends DataFlowCall {
NewCall() { this.asCall().getExpr().(MethodCall).getMethodName() = "new" }
}
/** A data-flow node that represents a value syntactically returned by a callable. */
abstract class ReturningNode extends Node {
/** Gets the kind of this return node. */
abstract ReturnKind getKind();
pragma[nomagic]
predicate hasKind(ReturnKind kind, CfgScope scope) {
kind = this.getKind() and
scope = this.(NodeImpl).getCfgScope()
}
}
/** A data-flow node that represents a value returned by a callable. */
abstract class ReturnNode extends Node {
/** Gets the kind of this return node. */
abstract ReturnKind getKind();
}
/** A data-flow node that represents a value returned by a callable. */
abstract class SourceReturnNode extends ReturnNode {
/** Gets the kind of this return node. */
abstract ReturnKind getKindSource(); // only exists to avoid spurious negative recursion
final override ReturnKind getKind() { result = this.getKindSource() }
pragma[nomagic]
predicate hasKind(ReturnKind kind, CfgScope scope) {
kind = this.getKindSource() and
scope = this.(NodeImpl).getCfgScope()
}
}
private module ReturnNodes {
private predicate isValid(CfgNodes::ReturningCfgNode node) {
exists(ReturningStmt stmt, Callable scope |
@@ -976,14 +952,14 @@ private module ReturnNodes {
* A data-flow node that represents an expression explicitly returned by
* a callable.
*/
class ExplicitReturnNode extends ReturningNode, ReturningStatementNode {
class ExplicitReturnNode extends SourceReturnNode, ReturningStatementNode {
ExplicitReturnNode() {
isValid(n) and
n.getASuccessor().(CfgNodes::AnnotatedExitNode).isNormal() and
n.getScope() instanceof Callable
}
override ReturnKind getKind() {
override ReturnKind getKindSource() {
if n.getNode() instanceof BreakStmt
then result instanceof BreakReturnKind
else
@@ -1012,10 +988,10 @@ private module ReturnNodes {
* a callable. An implicit return happens when an expression can be the
* last thing that is evaluated in the body of the callable.
*/
class ExprReturnNode extends ReturningNode, ExprNode {
class ExprReturnNode extends SourceReturnNode, ExprNode {
ExprReturnNode() { exists(Callable c | implicitReturn(c, this) = c.getAStmt()) }
override ReturnKind getKind() {
override ReturnKind getKindSource() {
exists(CfgScope scope | scope = this.(NodeImpl).getCfgScope() |
if isUserDefinedNew(scope)
then result instanceof NewReturnKind
@@ -1040,7 +1016,7 @@ private module ReturnNodes {
* the implicit `self` reference in `@x` will return data stored in the field
* `x` out to the call `C.new`.
*/
class InitializeReturnNode extends ExprPostUpdateNode, ReturningNode {
class InitializeReturnNode extends ExprPostUpdateNode, ReturnNode {
InitializeReturnNode() {
exists(Method initialize |
this.getCfgScope() = initialize and
@@ -1053,32 +1029,6 @@ private module ReturnNodes {
override ReturnKind getKind() { result instanceof NewReturnKind }
}
/**
* A synthetic data-flow node for joining flow from different syntactic
* returns into a single node.
*
* This node only exists to avoid computing the product of a large fan-in
* with a large fan-out.
*/
class SynthReturnNode extends NodeImpl, ReturnNode, TSynthReturnNode {
private CfgScope scope;
private ReturnKind kind;
SynthReturnNode() { this = TSynthReturnNode(scope, kind) }
/** Gets a syntactic return node that flows into this synthetic node. */
pragma[nomagic]
ReturningNode getAnInput() { result.hasKind(kind, scope) }
override ReturnKind getKind() { result = kind }
override CfgScope getCfgScope() { result = scope }
override Location getLocationImpl() { result = scope.getLocation() }
override string toStringImpl() { result = "return " + kind + " in " + scope }
}
private class SummaryReturnNode extends SummaryNode, ReturnNode {
private ReturnKind rk;
@@ -1339,9 +1289,6 @@ private import PostUpdateNodes
/** A node that performs a type cast. */
class CastNode extends Node {
CastNode() {
// ensure that actual return nodes are included in the path graph
this instanceof ReturningNode
or
// ensure that all variable assignments are included in the path graph
this.(SsaDefinitionExtNode).getDefinitionExt() instanceof Ssa::WriteDefinition
}

View File

@@ -195,10 +195,22 @@ class ParameterNode extends LocalSourceNode, TParameterNode instanceof Parameter
/** Gets the parameter corresponding to this node, if any. */
final Parameter getParameter() { result = super.getParameter() }
/** Gets the callable that this parameter belongs to. */
final Callable getCallable() { result = super.getCfgScope() }
/** Gets the name of the parameter, if any. */
final string getName() { result = this.getParameter().(NamedParameter).getName() }
}
/**
* The value of an implicit `self` parameter at function entry, viewed as a node in a data
* flow graph.
*/
class SelfParameterNode extends ParameterNode instanceof SelfParameterNodeImpl {
/** Gets the underlying `self` variable. */
final SelfVariable getSelfVariable() { result = super.getSelfVariable() }
}
/**
* A data-flow node that is a source of local flow.
*/
@@ -328,9 +340,6 @@ private module Cached {
exists(Node mid | hasLocalSource(mid, source) |
localFlowStepTypeTracker(mid, sink)
or
// Explicitly include the SSA param input step as type-tracking omits this step.
LocalFlow::localFlowSsaParamInput(mid, sink)
or
LocalFlow::localFlowSsaParamCaptureInput(mid, sink)
)
}
@@ -1176,19 +1185,15 @@ class CallableNode extends StmtSequenceNode {
result = this.getBlockParameter().getAMethodCall("call")
}
/**
* Gets the canonical return node from this callable.
*
* Each callable has exactly one such node, and its location may not correspond
* to any particular return site - consider using `getAReturningNode` to get nodes
* whose locations correspond to return sites.
*/
Node getReturn() { result.(SynthReturnNode).getCfgScope() = callable }
/**
* Gets a data flow node whose value is about to be returned by this callable.
*/
Node getAReturningNode() { result = this.getReturn().(SynthReturnNode).getAnInput() }
Node getAReturnNode() { result.(ReturnNode).(NodeImpl).getCfgScope() = callable }
/**
* DEPRECATED. Use `getAReturnNode` instead.
*/
deprecated Node getAReturningNode() { result = this.getAReturnNode() }
}
/**

View File

@@ -7,7 +7,6 @@ private import codeql.ruby.Concepts
private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.internal.DataFlowDispatch
private import codeql.ruby.dataflow.internal.DataFlowPrivate
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.Stdlib
private import codeql.ruby.frameworks.Core
@@ -319,19 +318,19 @@ private class ActiveRecordModelFinderCall extends ActiveRecordModelInstantiation
// A `self` reference that may resolve to an active record model object
private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation,
SsaSelfDefinitionNode
DataFlow::SelfParameterNode
{
private ActiveRecordModelClass cls;
ActiveRecordModelClassSelfReference() {
exists(MethodBase m |
m = this.getCfgScope() and
m = this.getCallable() and
m.getEnclosingModule() = cls and
m = cls.getAMethod()
) and
// In a singleton method, `self` refers to the class itself rather than an
// instance of that class
not this.getSelfScope() instanceof SingletonMethod
not this.getSelfVariable().getDeclaringScope() instanceof SingletonMethod
}
final override ActiveRecordModelClass getClass() { result = cls }

View File

@@ -21,7 +21,7 @@ module Rack {
AppCandidate() {
call = this.getInstanceMethod("call") and
call.getNumberOfParameters() = 1 and
call.getReturn() = trackRackResponse()
call.getAReturnNode() = trackRackResponse()
}
/**

View File

@@ -412,9 +412,7 @@ module Filters {
/**
* Holds if `n` is the self parameter of method `m`.
*/
private predicate selfParameter(DataFlowPrivate::SelfParameterNode n, Method m) {
m = n.getMethod()
}
private predicate selfParameter(DataFlow::SelfParameterNode n, Method m) { m = n.getCallable() }
/**
* A class defining additional jump steps arising from filters.

View File

@@ -224,6 +224,50 @@ private module Cached {
private import Cached
private predicate step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
stepNoCall(nodeFrom, nodeTo, summary)
or
stepCall(nodeFrom, nodeTo, summary)
}
pragma[nomagic]
private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) {
step(nodeFrom, _, summary)
}
bindingset[nodeFrom, t]
pragma[inline_late]
pragma[noopt]
private TypeTracker stepInlineLate(TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
exists(StepSummary summary |
stepProj(nodeFrom, summary) and
result = t.append(summary) and
step(nodeFrom, nodeTo, summary)
)
}
private predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
smallstepNoCall(nodeFrom, nodeTo, summary)
or
smallstepCall(nodeFrom, nodeTo, summary)
}
pragma[nomagic]
private predicate smallstepProj(Node nodeFrom, StepSummary summary) {
smallstep(nodeFrom, _, summary)
}
bindingset[nodeFrom, t]
pragma[inline_late]
pragma[noopt]
private TypeTracker smallstepInlineLate(TypeTracker t, Node nodeFrom, Node nodeTo) {
exists(StepSummary summary |
smallstepProj(nodeFrom, summary) and
result = t.append(summary) and
smallstep(nodeFrom, nodeTo, summary)
)
}
/**
* Holds if `nodeFrom` is being written to the `content` of the object in `nodeTo`.
*
@@ -298,21 +342,50 @@ class StepSummary extends TStepSummary {
module StepSummary {
/**
* Gets the summary that corresponds to having taken a forwards
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
* inter-procedural step from `nodeFrom` to `nodeTo`.
*
* This predicate is inlined, which enables better join-orders when
* the call graph construction and type tracking are mutually recursive.
* In such cases, non-linear recursion involving `step` will be limited
* to non-linear recursion for the parts of `step` that involve the
* call graph.
* This predicate should normally not be used; consider using `step`
* instead.
*/
predicate stepCall = Cached::stepCall/3;
/**
* Gets the summary that corresponds to having taken a forwards
* intra-procedural step from `nodeFrom` to `nodeTo`.
*
* This predicate should normally not be used; consider using `step`
* instead.
*/
predicate stepNoCall = Cached::stepNoCall/3;
/**
* Gets the summary that corresponds to having taken a forwards
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
*/
pragma[inline]
predicate step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
stepNoCall(nodeFrom, nodeTo, summary)
or
stepCall(nodeFrom, nodeTo, summary)
}
/**
* Gets the summary that corresponds to having taken a forwards
* inter-procedural step from `nodeFrom` to `nodeTo`.
*
* This predicate should normally not be used; consider using `step`
* instead.
*/
predicate smallstepNoCall = Cached::smallstepNoCall/3;
/**
* Gets the summary that corresponds to having taken a forwards
* intra-procedural step from `nodeFrom` to `nodeTo`.
*
* This predicate should normally not be used; consider using `step`
* instead.
*/
predicate smallstepCall = Cached::smallstepCall/3;
/**
* Gets the summary that corresponds to having taken a forwards
* local, heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
@@ -320,7 +393,6 @@ module StepSummary {
* Unlike `StepSummary::step`, this predicate does not compress
* type-preserving steps.
*/
pragma[inline]
predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
smallstepNoCall(nodeFrom, nodeTo, summary)
or
@@ -431,10 +503,7 @@ class TypeTracker extends TTypeTracker {
*/
pragma[inline]
TypeTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
exists(StepSummary summary |
StepSummary::step(nodeFrom, pragma[only_bind_out](nodeTo), pragma[only_bind_into](summary)) and
result = this.append(pragma[only_bind_into](summary))
)
result = stepInlineLate(this, nodeFrom, nodeTo)
}
/**
@@ -463,10 +532,7 @@ class TypeTracker extends TTypeTracker {
*/
pragma[inline]
TypeTracker smallstep(Node nodeFrom, Node nodeTo) {
exists(StepSummary summary |
StepSummary::smallstep(nodeFrom, nodeTo, summary) and
result = this.append(summary)
)
result = smallstepInlineLate(this, nodeFrom, nodeTo)
or
simpleLocalFlowStep(nodeFrom, nodeTo) and
result = this
@@ -481,6 +547,39 @@ module TypeTracker {
TypeTracker end() { result.end() }
}
pragma[nomagic]
private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) {
step(_, nodeTo, summary)
}
bindingset[nodeTo, t]
pragma[inline_late]
pragma[noopt]
private TypeBackTracker backStepInlineLate(
TypeBackTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo
) {
exists(StepSummary summary |
backStepProj(nodeTo, summary) and
result = t.prepend(summary) and
step(nodeFrom, nodeTo, summary)
)
}
private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) {
smallstep(_, nodeTo, summary)
}
bindingset[nodeTo, t]
pragma[inline_late]
pragma[noopt]
private TypeBackTracker backSmallstepInlineLate(TypeBackTracker t, Node nodeFrom, Node nodeTo) {
exists(StepSummary summary |
backSmallstepProj(nodeTo, summary) and
result = t.prepend(summary) and
smallstep(nodeFrom, nodeTo, summary)
)
}
/**
* A summary of the steps needed to back-track a use of a value to a given dataflow node.
*
@@ -564,10 +663,7 @@ class TypeBackTracker extends TTypeBackTracker {
*/
pragma[inline]
TypeBackTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
exists(StepSummary summary |
StepSummary::step(pragma[only_bind_out](nodeFrom), nodeTo, pragma[only_bind_into](summary)) and
this = result.prepend(pragma[only_bind_into](summary))
)
this = backStepInlineLate(result, nodeFrom, nodeTo)
}
/**
@@ -596,10 +692,7 @@ class TypeBackTracker extends TTypeBackTracker {
*/
pragma[inline]
TypeBackTracker smallstep(Node nodeFrom, Node nodeTo) {
exists(StepSummary summary |
StepSummary::smallstep(nodeFrom, nodeTo, summary) and
this = result.prepend(summary)
)
this = backSmallstepInlineLate(result, nodeFrom, nodeTo)
or
simpleLocalFlowStep(nodeFrom, nodeTo) and
this = result
@@ -635,3 +728,169 @@ module TypeBackTracker {
*/
TypeBackTracker end() { result.end() }
}
/**
* INTERNAL: Do not use.
*
* Provides logic for constructing a call graph in mutual recursion with type tracking.
*
* When type tracking is used to construct a call graph, we cannot use the join-order
* from `stepInlineLate`, because `step` becomes a recursive call, which means that we
* will have a conjunct with 3 recursive calls: the call to `step`, the call to `stepProj`,
* and the recursive type tracking call itself. The solution is to split the three-way
* non-linear recursion into two non-linear predicates: one that first joins with the
* projected `stepCall` relation, followed by a predicate that joins with the full
* `stepCall` relation (`stepNoCall` not being recursive, can be join-ordered in the
* same way as in `stepInlineLate`).
*/
module CallGraphConstruction {
/** The input to call graph construction. */
signature module InputSig {
/** A state to track during type tracking. */
class State;
/** Holds if type tracking should start at `start` in state `state`. */
predicate start(Node start, State state);
/**
* Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
* which _does not_ depend on the call graph.
*
* Implementing this predicate using `StepSummary::[small]stepNoCall` yields
* standard type tracking.
*/
predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary);
/**
* Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
* which _does_ depend on the call graph.
*
* Implementing this predicate using `StepSummary::[small]stepCall` yields
* standard type tracking.
*/
predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary);
/** A projection of an element from the state space. */
class StateProj;
/** Gets the projection of `state`. */
StateProj stateProj(State state);
/** Holds if type tracking should stop at `n` when we are tracking projected state `stateProj`. */
predicate filter(Node n, StateProj stateProj);
}
/** Provides the `track` predicate for use in call graph construction. */
module Make<InputSig Input> {
pragma[nomagic]
private predicate stepNoCallProj(Node nodeFrom, StepSummary summary) {
Input::stepNoCall(nodeFrom, _, summary)
}
pragma[nomagic]
private predicate stepCallProj(Node nodeFrom, StepSummary summary) {
Input::stepCall(nodeFrom, _, summary)
}
bindingset[nodeFrom, t]
pragma[inline_late]
pragma[noopt]
private TypeTracker stepNoCallInlineLate(
TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo
) {
exists(StepSummary summary |
stepNoCallProj(nodeFrom, summary) and
result = t.append(summary) and
Input::stepNoCall(nodeFrom, nodeTo, summary)
)
}
bindingset[state]
pragma[inline_late]
private Input::StateProj stateProjInlineLate(Input::State state) {
result = Input::stateProj(state)
}
pragma[nomagic]
private Node track(Input::State state, TypeTracker t) {
t.start() and Input::start(result, state)
or
exists(Input::StateProj stateProj |
stateProj = stateProjInlineLate(state) and
not Input::filter(result, stateProj)
|
exists(TypeTracker t2 | t = stepNoCallInlineLate(t2, track(state, t2), result))
or
exists(StepSummary summary |
// non-linear recursion
Input::stepCall(trackCall(state, t, summary), result, summary)
)
)
}
bindingset[t, summary]
pragma[inline_late]
private TypeTracker appendInlineLate(TypeTracker t, StepSummary summary) {
result = t.append(summary)
}
pragma[nomagic]
private Node trackCall(Input::State state, TypeTracker t, StepSummary summary) {
exists(TypeTracker t2 |
// non-linear recursion
result = track(state, t2) and
stepCallProj(result, summary) and
t = appendInlineLate(t2, summary)
)
}
/** Gets a node that can be reached from _some_ start node in state `state`. */
pragma[nomagic]
Node track(Input::State state) { result = track(state, TypeTracker::end()) }
}
/** A simple version of `CallGraphConstruction` that uses standard type tracking. */
module Simple {
/** The input to call graph construction. */
signature module InputSig {
/** A state to track during type tracking. */
class State;
/** Holds if type tracking should start at `start` in state `state`. */
predicate start(Node start, State state);
/** Holds if type tracking should stop at `n`. */
predicate filter(Node n);
}
/** Provides the `track` predicate for use in call graph construction. */
module Make<InputSig Input> {
private module I implements CallGraphConstruction::InputSig {
private import codeql.util.Unit
class State = Input::State;
predicate start(Node start, State state) { Input::start(start, state) }
predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary) {
StepSummary::stepNoCall(nodeFrom, nodeTo, summary)
}
predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary) {
StepSummary::stepCall(nodeFrom, nodeTo, summary)
}
class StateProj = Unit;
Unit stateProj(State state) { exists(state) and exists(result) }
predicate filter(Node n, Unit u) {
Input::filter(n) and
exists(u)
}
}
import CallGraphConstruction::Make<I>
}
}
}

View File

@@ -79,7 +79,7 @@ predicate jumpStep = DataFlowPrivate::jumpStep/2;
/** Holds if there is direct flow from `param` to a return. */
pragma[nomagic]
private predicate flowThrough(DataFlowPublic::ParameterNode param) {
exists(DataFlowPrivate::ReturningNode returnNode, DataFlowDispatch::ReturnKind rk |
exists(DataFlowPrivate::SourceReturnNode returnNode, DataFlowDispatch::ReturnKind rk |
param.flowsTo(returnNode) and
returnNode.hasKind(rk, param.(DataFlowPrivate::NodeImpl).getCfgScope())
|
@@ -221,15 +221,7 @@ predicate callStep(ExprNodes::CallCfgNode call, Node arg, DataFlowPrivate::Param
* recursion (or, at best, terrible performance), since identifying calls to library
* methods is done using API graphs (which uses type tracking).
*/
predicate callStep(Node nodeFrom, Node nodeTo) {
callStep(_, nodeFrom, nodeTo)
or
// In normal data-flow, this will be a local flow step. But for type tracking
// we model it as a call step, in order to avoid computing a potential
// self-cross product of all calls to a function that returns one of its parameters
// (only to later filter that flow out using `TypeTracker::append`).
DataFlowPrivate::LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
}
predicate callStep(Node nodeFrom, Node nodeTo) { callStep(_, nodeFrom, nodeTo) }
/**
* Holds if `nodeFrom` steps to `nodeTo` by being returned from a call.
@@ -241,19 +233,13 @@ predicate callStep(Node nodeFrom, Node nodeTo) {
predicate returnStep(Node nodeFrom, Node nodeTo) {
exists(ExprNodes::CallCfgNode call |
nodeFrom instanceof DataFlowPrivate::ReturnNode and
not nodeFrom instanceof DataFlowPrivate::InitializeReturnNode and
nodeFrom.(DataFlowPrivate::NodeImpl).getCfgScope() = DataFlowDispatch::getTarget(call) and
// deliberately do not include `getInitializeTarget`, since calls to `new` should not
// get the return value from `initialize`. Any fields being set in the initializer
// will reach all reads via `callStep` and `localFieldStep`.
nodeTo.asExpr().getNode() = call.getNode()
)
or
// In normal data-flow, this will be a local flow step. But for type tracking
// we model it as a returning flow step, in order to avoid computing a potential
// self-cross product of all calls to a function that returns one of its parameters
// (only to later filter that flow out using `TypeTracker::append`).
nodeTo.(DataFlowPrivate::SynthReturnNode).getAnInput() = nodeFrom and
not nodeFrom instanceof DataFlowPrivate::InitializeReturnNode
}
/**
@@ -609,26 +595,15 @@ private DataFlow::Node evaluateSummaryComponentStackLocal(
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,
DataFlowPrivate::ParameterNodeImpl p
|
exists(DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos |
head = SummaryComponent::parameter(apos) and
DataFlowDispatch::parameterMatch(ppos, apos) and
p.isSourceParameterOf(prev.asExpr().getExpr(), ppos) and
// We need to include both `p` and the SSA definition for `p`, since in type-tracking
// the step from `p` to the SSA definition is considered a call step.
result =
[p.(DataFlow::Node), DataFlowPrivate::LocalFlow::getParameterDefNode(p.getParameter())]
result.(DataFlowPrivate::ParameterNodeImpl).isSourceParameterOf(prev.asExpr().getExpr(), ppos)
)
or
exists(DataFlowPrivate::SynthReturnNode ret |
head = SummaryComponent::return() and
ret.getCfgScope() = prev.asExpr().getExpr() and
// We need to include both `ret` and `ret.getAnInput()`, since in type-tracking
// the step from `ret.getAnInput()` to `ret` is considered a return step.
result = [ret.(DataFlow::Node), ret.getAnInput()]
)
head = SummaryComponent::return() and
result.(DataFlowPrivate::ReturnNode).(DataFlowPrivate::NodeImpl).getCfgScope() =
prev.asExpr().getExpr()
or
exists(DataFlow::ContentSet content |
head = SummaryComponent::withoutContent(content) and

View File

@@ -2,7 +2,7 @@ import codeql.ruby.AST
import codeql.ruby.dataflow.internal.DataFlowPrivate
import codeql.ruby.dataflow.internal.DataFlowDispatch
query predicate ret(ReturningNode node) { any() }
query predicate ret(SourceReturnNode node) { any() }
query predicate arg(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) {
n.argumentOf(call, pos) and

View File

@@ -1,129 +1,72 @@
track
| type_tracker.rb:1:1:10:3 | self (Container) | type tracker without call steps | type_tracker.rb:1:1:10:3 | self (Container) |
| type_tracker.rb:1:1:53:4 | self (type_tracker.rb) | type tracker with call steps | type_tracker.rb:18:1:21:3 | self (positional) |
| type_tracker.rb:1:1:53:4 | self (type_tracker.rb) | type tracker with call steps | type_tracker.rb:18:1:21:3 | self in positional |
| type_tracker.rb:1:1:53:4 | self (type_tracker.rb) | type tracker with call steps | type_tracker.rb:25:1:28:3 | self (keyword) |
| type_tracker.rb:1:1:53:4 | self (type_tracker.rb) | type tracker with call steps | type_tracker.rb:25:1:28:3 | self in keyword |
| type_tracker.rb:1:1:53:4 | self (type_tracker.rb) | type tracker without call steps | type_tracker.rb:1:1:53:4 | self (type_tracker.rb) |
| type_tracker.rb:2:5:5:7 | &block | type tracker without call steps | type_tracker.rb:2:5:5:7 | &block |
| type_tracker.rb:2:5:5:7 | field= | type tracker without call steps | type_tracker.rb:2:5:5:7 | field= |
| type_tracker.rb:2:5:5:7 | return return in field= | type tracker without call steps | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:2:5:5:7 | return return in field= | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:5:5:7 | self (field=) | type tracker with call steps | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:2:5:5:7 | self (field=) | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:2:5:5:7 | self (field=) | type tracker without call steps | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:2:5:5:7 | self in field= | type tracker with call steps | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:2:5:5:7 | self in field= | type tracker with call steps | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:2:5:5:7 | self in field= | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:2:5:5:7 | self in field= | type tracker without call steps | type_tracker.rb:2:5:5:7 | self in field= |
| type_tracker.rb:2:16:2:18 | val | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type tracker with call steps | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:3:9:3:23 | call to puts | type tracker without call steps | type_tracker.rb:3:9:3:23 | call to puts |
| type_tracker.rb:3:14:3:23 | call to field | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:4:9:4:14 | @field | type tracker without call steps | type_tracker.rb:4:9:4:14 | @field |
| type_tracker.rb:7:5:9:7 | &block | type tracker without call steps | type_tracker.rb:7:5:9:7 | &block |
| type_tracker.rb:7:5:9:7 | field | type tracker without call steps | type_tracker.rb:7:5:9:7 | field |
| type_tracker.rb:7:5:9:7 | return return in field | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:7:5:9:7 | return return in field | type tracker without call steps | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:7:5:9:7 | return return in field | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:7:5:9:7 | self (field) | type tracker without call steps | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:7:5:9:7 | self in field | type tracker with call steps | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:7:5:9:7 | self in field | type tracker without call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:8:9:8:14 | @field | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:8:9:8:14 | @field | type tracker without call steps | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:8:9:8:14 | @field | type tracker without call steps | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:8:9:8:14 | @field | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:12:1:16:3 | &block | type tracker without call steps | type_tracker.rb:12:1:16:3 | &block |
| type_tracker.rb:12:1:16:3 | m | type tracker without call steps | type_tracker.rb:12:1:16:3 | m |
| type_tracker.rb:12:1:16:3 | return return in m | type tracker without call steps | type_tracker.rb:12:1:16:3 | return return in m |
| type_tracker.rb:12:1:16:3 | self (m) | type tracker without call steps | type_tracker.rb:12:1:16:3 | self (m) |
| type_tracker.rb:12:1:16:3 | self in m | type tracker with call steps | type_tracker.rb:12:1:16:3 | self (m) |
| type_tracker.rb:12:1:16:3 | self in m | type tracker without call steps | type_tracker.rb:12:1:16:3 | self in m |
| type_tracker.rb:13:5:13:7 | var | type tracker without call steps | type_tracker.rb:13:5:13:7 | var |
| type_tracker.rb:13:11:13:19 | Container | type tracker without call steps | type_tracker.rb:13:11:13:19 | Container |
| type_tracker.rb:13:11:13:23 | call to new | type tracker with call steps | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:13:11:13:23 | call to new | type tracker with call steps | type_tracker.rb:2:5:5:7 | self in field= |
| type_tracker.rb:13:11:13:23 | call to new | type tracker with call steps | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:13:11:13:23 | call to new | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:13:11:13:23 | call to new | type tracker without call steps | type_tracker.rb:13:11:13:23 | call to new |
| type_tracker.rb:14:5:14:7 | [post] var | type tracker with call steps | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:14:5:14:7 | [post] var | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:14:5:14:7 | [post] var | type tracker without call steps | type_tracker.rb:14:5:14:7 | [post] var |
| type_tracker.rb:14:5:14:13 | call to field= | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps with content attribute field | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps with content attribute field | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:14:17:14:23 | "hello" |
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps with content attribute field | type_tracker.rb:14:5:14:7 | [post] var |
| type_tracker.rb:14:17:14:23 | __synth__0 | type tracker without call steps | type_tracker.rb:14:17:14:23 | __synth__0 |
| type_tracker.rb:15:5:15:18 | call to puts | type tracker without call steps | type_tracker.rb:12:1:16:3 | return return in m |
| type_tracker.rb:15:5:15:18 | call to puts | type tracker without call steps | type_tracker.rb:15:5:15:18 | call to puts |
| type_tracker.rb:15:10:15:18 | call to field | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:18:1:21:3 | &block | type tracker without call steps | type_tracker.rb:18:1:21:3 | &block |
| type_tracker.rb:18:1:21:3 | positional | type tracker without call steps | type_tracker.rb:18:1:21:3 | positional |
| type_tracker.rb:18:1:21:3 | return return in positional | type tracker without call steps | type_tracker.rb:18:1:21:3 | return return in positional |
| type_tracker.rb:18:1:21:3 | return return in positional | type tracker without call steps | type_tracker.rb:23:1:23:16 | call to positional |
| type_tracker.rb:18:1:21:3 | self (positional) | type tracker without call steps | type_tracker.rb:18:1:21:3 | self (positional) |
| type_tracker.rb:18:1:21:3 | self in positional | type tracker with call steps | type_tracker.rb:18:1:21:3 | self (positional) |
| type_tracker.rb:18:1:21:3 | self in positional | type tracker without call steps | type_tracker.rb:18:1:21:3 | self in positional |
| type_tracker.rb:18:16:18:17 | p1 | type tracker with call steps | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type tracker without call steps | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type tracker without call steps | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type tracker without call steps | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:20:18:21 | p2 | type tracker with call steps | type_tracker.rb:18:20:18:21 | p2 |
| type_tracker.rb:18:20:18:21 | p2 | type tracker without call steps | type_tracker.rb:18:20:18:21 | p2 |
| type_tracker.rb:18:20:18:21 | p2 | type tracker without call steps | type_tracker.rb:18:20:18:21 | p2 |
| type_tracker.rb:18:20:18:21 | p2 | type tracker without call steps | type_tracker.rb:18:20:18:21 | p2 |
| type_tracker.rb:19:5:19:11 | call to puts | type tracker without call steps | type_tracker.rb:19:5:19:11 | call to puts |
| type_tracker.rb:20:5:20:11 | call to puts | type tracker without call steps | type_tracker.rb:18:1:21:3 | return return in positional |
| type_tracker.rb:20:5:20:11 | call to puts | type tracker without call steps | type_tracker.rb:20:5:20:11 | call to puts |
| type_tracker.rb:20:5:20:11 | call to puts | type tracker without call steps | type_tracker.rb:23:1:23:16 | call to positional |
| type_tracker.rb:23:1:23:16 | call to positional | type tracker without call steps | type_tracker.rb:23:1:23:16 | call to positional |
| type_tracker.rb:23:12:23:12 | 1 | type tracker with call steps | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:23:12:23:12 | 1 | type tracker with call steps | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:23:12:23:12 | 1 | type tracker without call steps | type_tracker.rb:23:12:23:12 | 1 |
| 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 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 |
| type_tracker.rb:25:1:28:3 | return return in keyword | type tracker without call steps | type_tracker.rb:31:1:31:21 | call to keyword |
| type_tracker.rb:25:1:28:3 | return return in keyword | type tracker without call steps | type_tracker.rb:32:1:32:27 | call to keyword |
| type_tracker.rb:25:1:28:3 | self (keyword) | type tracker without call steps | type_tracker.rb:25:1:28:3 | self (keyword) |
| type_tracker.rb:25:1:28:3 | self in keyword | type tracker with call steps | type_tracker.rb:25:1:28:3 | self (keyword) |
| type_tracker.rb:25:1:28:3 | self in keyword | type tracker without call steps | type_tracker.rb:25:1:28:3 | self in keyword |
| type_tracker.rb:25:13:25:14 | p1 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:25:13:25:14 | p1 | type tracker without call steps | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:25:13:25:14 | p1 | type tracker without call steps | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:25:13:25:14 | p1 | type tracker without call steps | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:25:18:25:19 | p2 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:25:18:25:19 | p2 | type tracker without call steps | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:25:18:25:19 | p2 | type tracker without call steps | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:25:18:25:19 | p2 | type tracker without call steps | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:26:5:26:11 | call to puts | type tracker without call steps | type_tracker.rb:26:5:26:11 | call to puts |
| type_tracker.rb:27:5:27:11 | call to puts | type tracker without call steps | type_tracker.rb:25:1:28:3 | return return in keyword |
| type_tracker.rb:27:5:27:11 | call to puts | type tracker without call steps | type_tracker.rb:27:5:27:11 | call to puts |
| 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 |
@@ -134,14 +77,12 @@ track
| 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 | ** |
@@ -151,14 +92,12 @@ track
| 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 | ** |
@@ -168,68 +107,33 @@ track
| 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 |
| type_tracker.rb:34:1:53:3 | throughArray | type tracker without call steps | type_tracker.rb:34:1:53:3 | throughArray |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:36:5:36:10 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:40:5:40:12 | ...[...] |
| 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 [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:36:5:36:10 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:36:5:36:10 | ...[...] |
| 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: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: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 | 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: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: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: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: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 | 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: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: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 [] |
| 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 [] |
| type_tracker.rb:34:23:34:23 | y | type tracker with call steps | type_tracker.rb:34:23:34:23 | y |
| type_tracker.rb:34:23:34:23 | y | type tracker without call steps | type_tracker.rb:34:23:34:23 | y |
| type_tracker.rb:34:23:34:23 | y | type tracker without call steps | type_tracker.rb:34:23:34:23 | y |
| type_tracker.rb:34:23:34:23 | y | type tracker without call steps | type_tracker.rb:34:23:34:23 | y |
| type_tracker.rb:34:26:34:26 | z | type tracker with call steps | type_tracker.rb:34:26:34:26 | z |
| type_tracker.rb:34:26:34:26 | z | type tracker without call steps | type_tracker.rb:34:26:34:26 | z |
| type_tracker.rb:34:26:34:26 | z | type tracker without call steps | type_tracker.rb:34:26:34:26 | z |
| type_tracker.rb:34:26:34:26 | z | type tracker without call steps | type_tracker.rb:34:26:34:26 | z |
| type_tracker.rb:35:5:35:7 | tmp | type tracker without call steps | type_tracker.rb:35:5:35:7 | tmp |
@@ -315,46 +219,33 @@ track
| type_tracker.rb:50:5:50:10 | array4 | type tracker without call steps | type_tracker.rb:50:5:50:10 | array4 |
| type_tracker.rb:50:14:50:26 | Array | type tracker without call steps | type_tracker.rb:50:14:50:26 | Array |
| type_tracker.rb:50:14:50:26 | call to [] | type tracker without call steps | type_tracker.rb:50:14:50:26 | call to [] |
| 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 []= |
| type_tracker.rb:51:17:51:19 | __synth__0 | type tracker without call steps | type_tracker.rb:51:17:51:19 | __synth__0 |
| type_tracker.rb:52:5:52:13 | ...[...] | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:52:5:52:13 | ...[...] | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
trackEnd
| type_tracker.rb:1:1:10:3 | self (Container) | type_tracker.rb:1:1:10:3 | self (Container) |
@@ -373,15 +264,6 @@ trackEnd
| type_tracker.rb:1:1:53:4 | self (type_tracker.rb) | type_tracker.rb:32:1:32:27 | self |
| type_tracker.rb:2:5:5:7 | &block | type_tracker.rb:2:5:5:7 | &block |
| type_tracker.rb:2:5:5:7 | field= | type_tracker.rb:2:5:5:7 | field= |
| type_tracker.rb:2:5:5:7 | return return in field= | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:2:5:5:7 | return return in field= | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:5:5:7 | self (field=) | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:2:5:5:7 | self (field=) | type_tracker.rb:3:9:3:23 | self |
| type_tracker.rb:2:5:5:7 | self (field=) | type_tracker.rb:3:14:3:17 | self |
| type_tracker.rb:2:5:5:7 | self (field=) | type_tracker.rb:4:9:4:14 | self |
| type_tracker.rb:2:5:5:7 | self (field=) | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:2:5:5:7 | self (field=) | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:2:5:5:7 | self (field=) | type_tracker.rb:8:9:8:14 | self |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:2:5:5:7 | self in field= |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:3:9:3:23 | self |
@@ -390,25 +272,14 @@ trackEnd
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:8:9:8:14 | self |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:4:9:4:20 | ... = ... |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:4:9:4:20 | ... = ... |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:4:18:4:20 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:4:18:4:20 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:3:9:3:23 | call to puts | type_tracker.rb:3:9:3:23 | call to puts |
| type_tracker.rb:3:14:3:23 | call to field | type_tracker.rb:3:14:3:23 | call to field |
@@ -416,23 +287,14 @@ trackEnd
| type_tracker.rb:7:5:9:7 | &block | type_tracker.rb:7:5:9:7 | &block |
| type_tracker.rb:7:5:9:7 | field | type_tracker.rb:1:1:10:3 | Container |
| type_tracker.rb:7:5:9:7 | field | type_tracker.rb:7:5:9:7 | field |
| type_tracker.rb:7:5:9:7 | return return in field | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:7:5:9:7 | return return in field | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:7:5:9:7 | return return in field | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:7:5:9:7 | self (field) | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:7:5:9:7 | self (field) | type_tracker.rb:8:9:8:14 | self |
| type_tracker.rb:7:5:9:7 | self in field | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:7:5:9:7 | self in field | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:7:5:9:7 | self in field | type_tracker.rb:8:9:8:14 | self |
| type_tracker.rb:8:9:8:14 | @field | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:8:9:8:14 | @field | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:8:9:8:14 | @field | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:8:9:8:14 | @field | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:12:1:16:3 | &block | type_tracker.rb:12:1:16:3 | &block |
| type_tracker.rb:12:1:16:3 | m | type_tracker.rb:12:1:16:3 | m |
| type_tracker.rb:12:1:16:3 | return return in m | type_tracker.rb:12:1:16:3 | return return in m |
| type_tracker.rb:12:1:16:3 | self (m) | type_tracker.rb:12:1:16:3 | self (m) |
| type_tracker.rb:12:1:16:3 | self (m) | type_tracker.rb:15:5:15:18 | self |
| type_tracker.rb:12:1:16:3 | self in m | type_tracker.rb:12:1:16:3 | self (m) |
| type_tracker.rb:12:1:16:3 | self in m | type_tracker.rb:12:1:16:3 | self in m |
| type_tracker.rb:12:1:16:3 | self in m | type_tracker.rb:15:5:15:18 | self |
@@ -470,16 +332,10 @@ trackEnd
| type_tracker.rb:14:17:14:23 | "hello" | type_tracker.rb:14:17:14:23 | __synth__0 |
| type_tracker.rb:14:17:14:23 | "hello" | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:14:17:14:23 | __synth__0 | type_tracker.rb:14:17:14:23 | __synth__0 |
| type_tracker.rb:15:5:15:18 | call to puts | type_tracker.rb:12:1:16:3 | return return in m |
| type_tracker.rb:15:5:15:18 | call to puts | type_tracker.rb:15:5:15:18 | call to puts |
| type_tracker.rb:15:10:15:18 | call to field | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:18:1:21:3 | &block | type_tracker.rb:18:1:21:3 | &block |
| type_tracker.rb:18:1:21:3 | positional | type_tracker.rb:18:1:21:3 | positional |
| type_tracker.rb:18:1:21:3 | return return in positional | type_tracker.rb:18:1:21:3 | return return in positional |
| type_tracker.rb:18:1:21:3 | return return in positional | type_tracker.rb:23:1:23:16 | call to positional |
| type_tracker.rb:18:1:21:3 | self (positional) | type_tracker.rb:18:1:21:3 | self (positional) |
| type_tracker.rb:18:1:21:3 | self (positional) | type_tracker.rb:19:5:19:11 | self |
| type_tracker.rb:18:1:21:3 | self (positional) | type_tracker.rb:20:5:20:11 | self |
| type_tracker.rb:18:1:21:3 | self in positional | type_tracker.rb:18:1:21:3 | self (positional) |
| type_tracker.rb:18:1:21:3 | self in positional | type_tracker.rb:18:1:21:3 | self in positional |
| type_tracker.rb:18:1:21:3 | self in positional | type_tracker.rb:19:5:19:11 | self |
@@ -487,17 +343,12 @@ trackEnd
| type_tracker.rb:18:16:18:17 | p1 | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type_tracker.rb:19:10:19:11 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type_tracker.rb:19:10:19:11 | p1 |
| type_tracker.rb:18:20:18:21 | p2 | type_tracker.rb:18:20:18:21 | p2 |
| type_tracker.rb:18:20:18:21 | p2 | type_tracker.rb:18:20:18:21 | p2 |
| type_tracker.rb:18:20:18:21 | p2 | type_tracker.rb:18:20:18:21 | p2 |
| type_tracker.rb:18:20:18:21 | p2 | type_tracker.rb:18:20:18:21 | p2 |
| type_tracker.rb:18:20:18:21 | p2 | type_tracker.rb:20:10:20:11 | p2 |
| type_tracker.rb:18:20:18:21 | p2 | type_tracker.rb:20:10:20:11 | p2 |
| type_tracker.rb:19:5:19:11 | call to puts | type_tracker.rb:19:5:19:11 | call to puts |
| type_tracker.rb:20:5:20:11 | call to puts | type_tracker.rb:18:1:21:3 | return return in positional |
| type_tracker.rb:20:5:20:11 | call to puts | type_tracker.rb:20:5:20:11 | call to puts |
| type_tracker.rb:20:5:20:11 | call to puts | type_tracker.rb:23:1:23:16 | call to positional |
| type_tracker.rb:23:1:23:16 | call to positional | type_tracker.rb:23:1:23:16 | call to positional |
@@ -512,13 +363,6 @@ trackEnd
| 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 |
| type_tracker.rb:25:1:28:3 | return return in keyword | type_tracker.rb:31:1:31:21 | call to keyword |
| type_tracker.rb:25:1:28:3 | return return in keyword | type_tracker.rb:32:1:32:27 | call to keyword |
| type_tracker.rb:25:1:28:3 | self (keyword) | type_tracker.rb:25:1:28:3 | self (keyword) |
| type_tracker.rb:25:1:28:3 | self (keyword) | type_tracker.rb:26:5:26:11 | self |
| type_tracker.rb:25:1:28:3 | self (keyword) | type_tracker.rb:27:5:27:11 | self |
| type_tracker.rb:25:1:28:3 | self in keyword | type_tracker.rb:25:1:28:3 | self (keyword) |
| type_tracker.rb:25:1:28:3 | self in keyword | type_tracker.rb:25:1:28:3 | self in keyword |
| type_tracker.rb:25:1:28:3 | self in keyword | type_tracker.rb:26:5:26:11 | self |
@@ -526,17 +370,12 @@ trackEnd
| type_tracker.rb:25:13:25:14 | p1 | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:25:13:25:14 | p1 | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:25:13:25:14 | p1 | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:25:13:25:14 | p1 | type_tracker.rb:25:13:25:14 | p1 |
| type_tracker.rb:25:13:25:14 | p1 | type_tracker.rb:26:10:26:11 | p1 |
| type_tracker.rb:25:13:25:14 | p1 | type_tracker.rb:26:10:26:11 | p1 |
| type_tracker.rb:25:18:25:19 | p2 | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:25:18:25:19 | p2 | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:25:18:25:19 | p2 | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:25:18:25:19 | p2 | type_tracker.rb:25:18:25:19 | p2 |
| type_tracker.rb:25:18:25:19 | p2 | type_tracker.rb:27:10:27:11 | p2 |
| type_tracker.rb:25:18:25:19 | p2 | type_tracker.rb:27:10:27:11 | p2 |
| type_tracker.rb:26:5:26:11 | call to puts | type_tracker.rb:26:5:26:11 | call to puts |
| type_tracker.rb:27:5:27:11 | call to puts | type_tracker.rb:25:1:28:3 | return return in keyword |
| type_tracker.rb:27:5:27:11 | call to puts | type_tracker.rb:27:5:27:11 | call to puts |
| 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 |
@@ -587,80 +426,45 @@ trackEnd
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:26:10:26:11 | p1 |
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:32:26:32:26 | 8 |
| type_tracker.rb:34:1:53:3 | &block | type_tracker.rb:34:1:53:3 | &block |
| type_tracker.rb:34:1:53:3 | return return in throughArray | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:1:53:3 | self in throughArray | type_tracker.rb:34:1:53:3 | self in throughArray |
| type_tracker.rb:34:1:53:3 | throughArray | type_tracker.rb:34:1:53:3 | throughArray |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:35:12:35:14 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:35:12:35:14 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:36:5:36:10 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:36:5:36:10 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:5:39:12 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:5:39:12 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:5:39:18 | ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:5:39:18 | ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | ... = ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | ... = ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:40:5:40:12 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:40:5:40:12 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:5:43:13 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:5:43:13 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:5:43:19 | ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:5:43:19 | ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | ... = ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | ... = ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:5:47:13 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:5:47:13 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:5:47:19 | ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:5:47:19 | ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | ... = ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | ... = ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:5:51:13 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:5:51:13 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:5:51:19 | ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:5:51:19 | ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | ... = ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | ... = ... |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | __synth__0 |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:34:23:34:23 | y |
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:34:23:34:23 | y |
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:34:23:34:23 | y |
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:34:23:34:23 | y |
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:39:11:39:11 | y |
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:39:11:39:11 | y |
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:44:12:44:12 | y |
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:44:12:44:12 | y |
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:51:12:51:12 | y |
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:51:12:51:12 | y |
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:34:26:34:26 | z |
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:34:26:34:26 | z |
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:34:26:34:26 | z |
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:34:26:34:26 | z |
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:52:12:52:12 | z |
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:52:12:52:12 | z |
| type_tracker.rb:35:5:35:7 | tmp | type_tracker.rb:35:5:35:7 | tmp |
| type_tracker.rb:35:11:35:15 | Array | type_tracker.rb:35:11:35:15 | Array |
@@ -743,29 +547,22 @@ trackEnd
| type_tracker.rb:50:14:50:26 | call to [] | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:50:14:50:26 | call to [] | type_tracker.rb:51:5:51:10 | array4 |
| type_tracker.rb:50:14:50:26 | call to [] | type_tracker.rb:52:5:52:10 | array4 |
| type_tracker.rb:50:15:50:15 | 1 | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:15:50:15 | 1 | type_tracker.rb:50:15:50:15 | 1 |
| type_tracker.rb:50:15:50:15 | 1 | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:17:50:17 | 2 | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:17:50:17 | 2 | type_tracker.rb:50:17:50:17 | 2 |
| type_tracker.rb:50:17:50:17 | 2 | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:19:50:19 | 3 | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:19:50:19 | 3 | type_tracker.rb:50:19:50:19 | 3 |
| type_tracker.rb:50:19:50:19 | 3 | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:21:50:21 | 4 | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:21:50:21 | 4 | type_tracker.rb:50:21:50:21 | 4 |
| type_tracker.rb:50:21:50:21 | 4 | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:23:50:23 | 5 | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:23:50:23 | 5 | type_tracker.rb:50:23:50:23 | 5 |
| type_tracker.rb:50:23:50:23 | 5 | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:50:25:50:25 | 6 | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:50:25:50:25 | 6 | type_tracker.rb:50:25:50:25 | 6 |
| type_tracker.rb:50:25:50:25 | 6 | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:51:5:51:10 | [post] array4 | type_tracker.rb:51:5:51:10 | [post] array4 |
| type_tracker.rb:51:5:51:10 | [post] array4 | type_tracker.rb:52:5:52:10 | array4 |
| type_tracker.rb:51:5:51:13 | call to []= | type_tracker.rb:51:5:51:13 | call to []= |
| type_tracker.rb:51:17:51:19 | __synth__0 | type_tracker.rb:51:17:51:19 | __synth__0 |
| type_tracker.rb:52:5:52:13 | ...[...] | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:52:5:52:13 | ...[...] | type_tracker.rb:52:5:52:13 | ...[...] |
forwardButNoBackwardFlow
backwardButNoForwardFlow