Merge pull request #108 from github/hvitved/ssa

Add SSA library
This commit is contained in:
Tom Hvitved
2021-01-29 15:12:14 +01:00
committed by GitHub
33 changed files with 2377 additions and 240 deletions

2
codeql

Submodule codeql updated: fc2fe6cccb...65ea01e145

View File

@@ -0,0 +1,22 @@
import ruby
import codeql_ruby.dataflow.SSA
import codeql_ruby.controlflow.ControlFlowGraph
query predicate nonUniqueDef(CfgNode read, Ssa::Definition def) {
read = def.getARead() and
exists(Ssa::Definition other | read = other.getARead() and other != def)
}
query predicate readWithoutDef(LocalVariableReadAccess read) {
exists(CfgNode node |
node = read.getAControlFlowNode() and
not node = any(Ssa::Definition def).getARead()
)
}
query predicate deadDef(Ssa::Definition def, LocalVariable v) {
v = def.getSourceVariable() and
not v.isCaptured() and
not exists(def.getARead()) and
not def = any(Ssa::PhiNode phi).getAnInput()
}

View File

@@ -29,6 +29,7 @@ class AstNode extends @ast_node {
string getAPrimaryQlClass() { result = "???" }
/** Gets a textual representation of this node. */
cached
string toString() { result = "AstNode" }
/** Gets the location of this node. */

View File

@@ -0,0 +1,5 @@
/** Provides classes representing the control flow graph. */
import controlflow.ControlFlowGraph
import controlflow.CfgNodes as CfgNodes
import controlflow.BasicBlocks

View File

@@ -1,5 +1,8 @@
private import codeql_ruby.AST
private import codeql_ruby.CFG
private import internal.Expr
private import internal.Variable
private import codeql_ruby.controlflow.internal.ControlFlowGraphImpl
/**
* An expression.
@@ -10,6 +13,18 @@ class Expr extends AstNode {
Expr::Range range;
Expr() { this = range }
/** Gets a control-flow node for this expression, if any. */
CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this }
/** Gets the control-flow scope of this expression, if any. */
CfgScope getCfgScope() { result = getCfgScope(this) }
/** Gets the variable scope that this expression belongs to. */
VariableScope getVariableScope() { result = enclosingScope(this) }
/** Gets the enclosing callable, if any. */
Callable getEnclosingCallable() { result = this.getCfgScope() }
}
/**

View File

@@ -1,9 +1,10 @@
private import codeql_ruby.AST
private import codeql_ruby.controlflow.ControlFlowGraph
private import internal.TreeSitter
private import internal.Method
/** A callable. */
class Callable extends AstNode {
class Callable extends AstNode, CfgScope {
Callable::Range range;
Callable() { range = this }

View File

@@ -22,10 +22,10 @@ class Parameter extends AstNode {
final int getPosition() { result = pos }
/** Gets a variable introduced by this parameter. */
Variable getAVariable() { none() }
LocalVariable getAVariable() { none() }
/** Gets the variable named `name` introduced by this parameter. */
final Variable getVariable(string name) {
final LocalVariable getVariable(string name) {
result = this.getAVariable() and
result.getName() = name
}
@@ -37,7 +37,7 @@ class Parameter extends AstNode {
* This includes both simple parameters and tuple parameters.
*/
class PatternParameter extends Parameter, Pattern {
override Variable getAVariable() { result = Pattern.super.getAVariable() }
override LocalVariable getAVariable() { result = Pattern.super.getAVariable() }
}
/** A parameter defined using a tuple pattern. */
@@ -53,9 +53,9 @@ class NamedParameter extends Parameter {
string getName() { none() }
/** Gets the variable introduced by this parameter. */
Variable getVariable() { none() }
LocalVariable getVariable() { none() }
override Variable getAVariable() { result = this.getVariable() }
override LocalVariable getAVariable() { result = this.getVariable() }
/** Gets an access to this parameter. */
final VariableAccess getAnAccess() { result = this.getVariable().getAnAccess() }
@@ -65,9 +65,9 @@ class NamedParameter extends Parameter {
class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern {
final override string getName() { result = range.getVariableName() }
final override Variable getVariable() { result = TLocalVariable(_, _, this) }
final override LocalVariable getVariable() { result = TLocalVariable(_, _, this) }
final override Variable getAVariable() { result = this.getVariable() }
final override LocalVariable getAVariable() { result = this.getVariable() }
final override string getAPrimaryQlClass() { result = "SimpleParameter" }
@@ -85,7 +85,7 @@ class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern
class BlockParameter extends @block_parameter, NamedParameter {
final override Generated::BlockParameter generated;
final override Variable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
final override LocalVariable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
final override string getAPrimaryQlClass() { result = "BlockParameter" }
@@ -106,7 +106,7 @@ class BlockParameter extends @block_parameter, NamedParameter {
class HashSplatParameter extends @hash_splat_parameter, NamedParameter {
final override Generated::HashSplatParameter generated;
final override Variable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
final override LocalVariable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
final override string getAPrimaryQlClass() { result = "HashSplatParameter" }
@@ -129,7 +129,7 @@ class HashSplatParameter extends @hash_splat_parameter, NamedParameter {
class KeywordParameter extends @keyword_parameter, NamedParameter {
final override Generated::KeywordParameter generated;
final override Variable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
final override LocalVariable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
final override string getAPrimaryQlClass() { result = "KeywordParameter" }
@@ -164,7 +164,7 @@ class KeywordParameter extends @keyword_parameter, NamedParameter {
class OptionalParameter extends @optional_parameter, NamedParameter {
final override Generated::OptionalParameter generated;
final override Variable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
final override LocalVariable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
final override string getAPrimaryQlClass() { result = "OptionalParameter" }
@@ -191,7 +191,7 @@ class OptionalParameter extends @optional_parameter, NamedParameter {
class SplatParameter extends @splat_parameter, NamedParameter {
final override Generated::SplatParameter generated;
final override Variable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
final override LocalVariable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
final override string getAPrimaryQlClass() { result = "SplatParameter" }

View File

@@ -57,6 +57,25 @@ class LocalVariable extends Variable, TLocalVariable {
override LocalVariable::Range range;
final override LocalVariableAccess getAnAccess() { result.getVariable() = this }
/** Gets the access where this local variable is first introduced. */
VariableAccess getDefiningAccess() { result = range.getDefiningAccess() }
/**
* Holds if this variable is captured. For example in
*
* ```rb
* def m x
* x.times do |y|
* puts x
* end
* puts x
* end
* ```
*
* `x` is a captured variable, whereas `y` is not.
*/
predicate isCaptured() { this.getAnAccess().isCapturedAccess() }
}
/** A global variable. */
@@ -133,6 +152,23 @@ class LocalVariableAccess extends VariableAccess, @token_identifier {
final override string getAPrimaryQlClass() {
not this instanceof SimpleParameter and result = "LocalVariableAccess"
}
/**
* Holds if this access is a captured variable access. For example in
*
* ```rb
* def m x
* x.times do |y|
* puts x
* end
* puts x
* end
* ```
*
* the access to `x` in the first `puts x` is a captured access, while
* the access to `x` in the second `puts x` is not.
*/
final predicate isCapturedAccess() { isCapturedAccess(this) }
}
/** An access to a local variable where the value is updated. */

View File

@@ -39,6 +39,7 @@ predicate implicitParameterAssignmentNode(Generated::AstNode n, Callable c) {
module Pattern {
abstract class Range extends AstNode {
cached
Range() {
explicitAssignmentNode(this, _)
or

View File

@@ -9,11 +9,6 @@ private Generated::AstNode parent(Generated::AstNode n) {
not n = any(VariableScope s).getScopeElement()
}
/** Gets the enclosing scope for `node`. */
private VariableScope enclosingScope(Generated::AstNode node) {
result.getScopeElement() = parent*(node.getParent())
}
private predicate parameterAssignment(
CallableScope::Range scope, string name, Generated::Identifier i
) {
@@ -98,6 +93,12 @@ private class CapturingScope extends VariableScope {
cached
private module Cached {
/** Gets the enclosing scope for `node`. */
cached
VariableScope enclosingScope(Generated::AstNode node) {
result.getScopeElement() = parent*(node.getParent())
}
cached
newtype TScope =
TGlobalScope() or
@@ -301,6 +302,11 @@ private module Cached {
or
scopeDefinesParameterVariable(_, _, access)
}
cached
predicate isCapturedAccess(LocalVariableAccess::Range access) {
access.getVariable().getDeclaringScope() != enclosingScope(access)
}
}
import Cached
@@ -391,6 +397,8 @@ module LocalVariable {
final override Location getLocation() { result = i.getLocation() }
final override VariableScope getDeclaringScope() { result = scope }
final VariableAccess getDefiningAccess() { result = i }
}
}

View File

@@ -4,6 +4,7 @@ private import codeql.Locations
private import codeql_ruby.ast.internal.TreeSitter::Generated
private import codeql_ruby.controlflow.ControlFlowGraph
private import internal.ControlFlowGraphImpl
private import CfgNodes
private import SuccessorTypes
/**
@@ -286,9 +287,7 @@ private module Cached {
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
/** Holds if `bb` is an exit basic block that represents normal exit. */
private predicate normalExitBB(BasicBlock bb) {
bb.getANode().(CfgNodes::AnnotatedExitNode).isNormal()
}
private predicate normalExitBB(BasicBlock bb) { bb.getANode().(AnnotatedExitNode).isNormal() }
/** Holds if `dom` is an immediate post-dominator of `bb`. */
cached
@@ -313,7 +312,7 @@ private module Cached {
private import Cached
/** Holds if `bb` is an entry basic block. */
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof CfgNodes::EntryNode }
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryNode }
/**
* An entry basic block, that is, a basic block whose first node is
@@ -330,7 +329,17 @@ class EntryBasicBlock extends BasicBlock {
* an annotated exit node.
*/
class AnnotatedExitBasicBlock extends BasicBlock {
AnnotatedExitBasicBlock() { this.getANode() instanceof CfgNodes::AnnotatedExitNode }
private boolean normal;
AnnotatedExitBasicBlock() {
exists(AnnotatedExitNode n |
n = this.getANode() and
if n.isNormal() then normal = true else normal = false
)
}
/** Holds if this block represent a normal exit. */
final predicate isNormal() { normal = true }
}
/**
@@ -338,12 +347,10 @@ class AnnotatedExitBasicBlock extends BasicBlock {
* an exit node.
*/
class ExitBasicBlock extends BasicBlock {
ExitBasicBlock() { this.getLastNode() instanceof CfgNodes::ExitNode }
ExitBasicBlock() { this.getLastNode() instanceof ExitNode }
}
private module JoinBlockPredecessors {
private import CfgNodes
private predicate id(AstNode x, AstNode y) { x = y }
private predicate idOf(AstNode x, int y) = equivalenceRelation(id/2)(x, y)

View File

@@ -0,0 +1,202 @@
/** Provides classes representing nodes in a control flow graph. */
private import codeql_ruby.AST
private import codeql_ruby.ast.internal.TreeSitter
private import codeql_ruby.controlflow.BasicBlocks
private import ControlFlowGraph
private import internal.ControlFlowGraphImpl
private import internal.Splitting
/** An entry node for a given scope. */
class EntryNode extends CfgNode, TEntryNode {
private CfgScope scope;
EntryNode() { this = TEntryNode(scope) }
final override EntryBasicBlock getBasicBlock() { result = CfgNode.super.getBasicBlock() }
final override Location getLocation() { result = scope.getLocation() }
final override string toString() { result = "enter " + scope }
}
/** An exit node for a given scope, annotated with the type of exit. */
class AnnotatedExitNode extends CfgNode, TAnnotatedExitNode {
private CfgScope scope;
private boolean normal;
AnnotatedExitNode() { this = TAnnotatedExitNode(scope, normal) }
/** Holds if this node represent a normal exit. */
final predicate isNormal() { normal = true }
final override AnnotatedExitBasicBlock getBasicBlock() { result = CfgNode.super.getBasicBlock() }
final override Location getLocation() { result = scope.getLocation() }
final override string toString() {
exists(string s |
normal = true and s = "normal"
or
normal = false and s = "abnormal"
|
result = "exit " + scope + " (" + s + ")"
)
}
}
/** An exit node for a given scope. */
class ExitNode extends CfgNode, TExitNode {
private CfgScope scope;
ExitNode() { this = TExitNode(scope) }
final override Location getLocation() { result = scope.getLocation() }
final override string toString() { result = "exit " + scope }
}
/**
* A node for an AST node.
*
* Each AST node maps to zero or more `AstCfgNode`s: zero when the node in unreachable
* (dead) code or not important for control flow, and multiple when there are different
* splits for the AST node.
*/
class AstCfgNode extends CfgNode, TAstNode {
private Splits splits;
private Generated::AstNode n;
AstCfgNode() { this = TAstNode(n, splits) }
final override AstNode getNode() { result = n }
final override string toString() {
exists(string s |
// TODO: Remove once the SSA implementation is based on the AST layer
s = n.(AstNode).toString() and
s != "AstNode"
or
n.(AstNode).toString() = "AstNode" and
s = n.toString()
|
result = "[" + this.getSplitsString() + "] " + s
or
not exists(this.getSplitsString()) and result = s
)
}
/** Gets a comma-separated list of strings for each split in this node, if any. */
final string getSplitsString() {
result = splits.toString() and
result != ""
}
/** Gets a split for this control flow node, if any. */
final Split getASplit() { result = splits.getASplit() }
}
/** A control-flow node that wraps an AST expression. */
class ExprCfgNode extends AstCfgNode {
Expr e;
ExprCfgNode() { e = this.getNode() }
/** Gets the underlying expression. */
Expr getExpr() { result = e }
}
/**
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
*/
abstract private class ExprChildMapping extends Expr {
/**
* Holds if `child` is a (possibly nested) child of this expression
* for which we would like to find a matching CFG child.
*/
abstract predicate relevantChild(Expr child);
private AstNode getAChildStar() {
result = this
or
result.(Generated::AstNode).getParent() = this.getAChildStar()
}
pragma[noinline]
private BasicBlock getABasicBlockInScope() { result.getANode().getNode() = this.getAChildStar() }
pragma[nomagic]
private predicate reachesBasicBlockBase(Expr child, CfgNode cfn, BasicBlock bb) {
this.relevantChild(child) and
cfn = this.getAControlFlowNode() and
bb.getANode() = cfn
}
pragma[nomagic]
private predicate reachesBasicBlock(Expr child, CfgNode cfn, BasicBlock bb) {
this.reachesBasicBlockBase(child, cfn, bb)
or
this.relevantChild(child) and
this.reachesBasicBlockRec(child, cfn, bb) and
bb = this.getABasicBlockInScope()
}
pragma[nomagic]
private predicate reachesBasicBlockRec(Expr child, CfgNode cfn, BasicBlock bb) {
exists(BasicBlock mid | this.reachesBasicBlock(child, cfn, mid) |
bb = mid.getASuccessor()
or
bb = mid.getAPredecessor()
)
}
/**
* Holds if there is a control-flow path from `cfn` to `cfnChild`, where `cfn`
* is a control-flow node for this expression, and `cfnChild` is a control-flow
* node for `child`.
*
* The path never escapes the syntactic scope of this expression.
*/
cached
predicate hasCfgChild(Expr child, CfgNode cfn, CfgNode cfnChild) {
exists(BasicBlock bb |
this.reachesBasicBlockBase(child, cfn, bb) and
cfnChild = bb.getANode() and
cfnChild = child.getAControlFlowNode()
)
or
exists(BasicBlock bb |
this.reachesBasicBlockRec(child, cfn, bb) and
cfnChild = bb.getANode() and
cfnChild = child.getAControlFlowNode()
)
}
}
/** Provides classes for control-flow nodes that wrap AST expressions. */
module ExprNodes {
// TODO: Add more classes
private class BinaryOperationExprChildMapping extends ExprChildMapping, BinaryOperation {
override predicate relevantChild(Expr e) { e = this.getAnOperand() }
}
/** A control-flow node that wraps a `BinaryOperation` AST expression. */
class BinaryOperationCfgNode extends ExprCfgNode {
override BinaryOperationExprChildMapping e;
final override BinaryOperation getExpr() { result = ExprCfgNode.super.getExpr() }
/** Gets the left operand of this binary operation. */
final ExprCfgNode getLeftOperand() { e.hasCfgChild(e.getLeftOperand(), this, result) }
/** Gets the right operand of this binary operation. */
final ExprCfgNode getRightOperand() { e.hasCfgChild(e.getRightOperand(), this, result) }
}
/** A control-flow node that wraps a `VariableReadAccess` AST expression. */
class VariableReadAccessCfgNode extends ExprCfgNode {
override VariableReadAccess e;
final override VariableReadAccess getExpr() { result = ExprCfgNode.super.getExpr() }
}
}

View File

@@ -10,13 +10,16 @@ private import internal.Splitting
private import internal.Completion
/** An AST node with an associated control-flow graph. */
class CfgScope extends AstNode {
CfgScope::Range_ range;
class CfgScope extends AST::AstNode {
CfgScope() { this instanceof CfgScope::Range_ }
CfgScope() { range = this }
/** Gets the name of this scope. */
string getName() { result = range.getName() }
/** Gets the CFG scope that this scope is nested under, if any. */
final CfgScope getOuterCfgScope() {
exists(AstNode parent |
parent.getAFieldOrChild() = this and
result = getCfgScope(parent)
)
}
}
/**
@@ -32,7 +35,7 @@ class CfgNode extends TCfgNode {
string toString() { none() }
/** Gets the AST node that this node corresponds to, if any. */
AstNode getNode() { none() }
AST::AstNode getNode() { none() }
/** Gets the location of this control flow node. */
Location getLocation() { result = this.getNode().getLocation() }
@@ -65,100 +68,6 @@ class CfgNode extends TCfgNode {
final predicate isBranch() { strictcount(this.getASuccessor()) > 1 }
}
/** Provides different types of control flow nodes. */
module CfgNodes {
/** An entry node for a given scope. */
class EntryNode extends CfgNode, TEntryNode {
private CfgScope scope;
EntryNode() { this = TEntryNode(scope) }
final override EntryBasicBlock getBasicBlock() { result = CfgNode.super.getBasicBlock() }
final override Location getLocation() { result = scope.getLocation() }
final override string toString() { result = "enter " + scope.getName() }
}
/** An exit node for a given scope, annotated with the type of exit. */
class AnnotatedExitNode extends CfgNode, TAnnotatedExitNode {
private CfgScope scope;
private boolean normal;
AnnotatedExitNode() { this = TAnnotatedExitNode(scope, normal) }
/** Holds if this node represent a normal exit. */
final predicate isNormal() { normal = true }
final override AnnotatedExitBasicBlock getBasicBlock() {
result = CfgNode.super.getBasicBlock()
}
final override Location getLocation() { result = scope.getLocation() }
final override string toString() {
exists(string s |
normal = true and s = "normal"
or
normal = false and s = "abnormal"
|
result = "exit " + scope.getName() + " (" + s + ")"
)
}
}
/** An exit node for a given scope. */
class ExitNode extends CfgNode, TExitNode {
private CfgScope scope;
ExitNode() { this = TExitNode(scope) }
final override Location getLocation() { result = scope.getLocation() }
final override string toString() { result = "exit " + scope.getName() }
}
/**
* A node for an AST node.
*
* Each AST node maps to zero or more `AstCfgNode`s: zero when the node in unreachable
* (dead) code or not important for control flow, and multiple when there are different
* splits for the AST node.
*/
class AstCfgNode extends CfgNode, TAstNode {
private Splits splits;
private AstNode n;
AstCfgNode() { this = TAstNode(n, splits) }
final override AstNode getNode() { result = n }
final override string toString() {
exists(string s |
// TODO: Remove once the SSA implementation is based on the AST layer
s = n.(AST::AstNode).toString() and
s != "AstNode"
or
n.(AST::AstNode).toString() = "AstNode" and
s = n.toString()
|
result = "[" + this.getSplitsString() + "] " + s
or
not exists(this.getSplitsString()) and result = s
)
}
/** Gets a comma-separated list of strings for each split in this node, if any. */
final string getSplitsString() {
result = splits.toString() and
result != ""
}
/** Gets a split for this control flow node, if any. */
final Split getASplit() { result = splits.getASplit() }
}
}
/** The type of a control flow successor. */
class SuccessorType extends TSuccessorType {
/** Gets a textual representation of successor type. */

View File

@@ -2,7 +2,7 @@
* @kind graph
*/
import codeql_ruby.controlflow.ControlFlowGraph
import codeql_ruby.CFG
query predicate nodes(CfgNode n) { any() }

View File

@@ -1,5 +1,5 @@
private import codeql_ruby.ast.internal.TreeSitter::Generated
private import codeql_ruby.controlflow.ControlFlowGraph
private import codeql_ruby.CFG
private import Completion
private import Splitting

View File

@@ -41,24 +41,18 @@ private import codeql.files.FileSystem
module CfgScope {
abstract class Range_ extends AstNode {
abstract string getName();
abstract predicate entry(AstNode first);
abstract predicate exit(AstNode last, Completion c);
}
private class ProgramScope extends Range_, Program {
final override string getName() { result = "top-level" }
final override predicate entry(AstNode first) { first(this, first) }
final override predicate exit(AstNode last, Completion c) { last(this, last, c) }
}
private class BeginBlockScope extends Range_, BeginBlock {
final override string getName() { result = "BEGIN block" }
final override predicate entry(AstNode first) {
first(this.(Trees::BeginBlockTree).getFirstChildNode(), first)
}
@@ -69,8 +63,6 @@ module CfgScope {
}
private class EndBlockScope extends Range_, EndBlock {
final override string getName() { result = "END block" }
final override predicate entry(AstNode first) {
first(this.(Trees::EndBlockTree).getFirstChildNode(), first)
}
@@ -83,8 +75,6 @@ module CfgScope {
private class MethodScope extends Range_, AstNode {
MethodScope() { this instanceof Method }
final override string getName() { result = this.(Method).getName().toString() }
final override predicate entry(AstNode first) {
this.(Trees::RescueEnsureBlockTree).firstInner(first)
}
@@ -97,8 +87,6 @@ module CfgScope {
private class SingletonMethodScope extends Range_, AstNode {
SingletonMethodScope() { this instanceof SingletonMethod }
final override string getName() { result = this.(SingletonMethod).getName().toString() }
final override predicate entry(AstNode first) {
this.(Trees::RescueEnsureBlockTree).firstInner(first)
}
@@ -111,8 +99,6 @@ module CfgScope {
private class DoBlockScope extends Range_, DoBlock {
DoBlockScope() { not this.getParent() instanceof Lambda }
final override string getName() { result = "do block" }
final override predicate entry(AstNode first) {
this.(Trees::RescueEnsureBlockTree).firstInner(first)
}
@@ -125,8 +111,6 @@ module CfgScope {
private class BlockScope extends Range_, Block {
BlockScope() { not this.getParent() instanceof Lambda }
final override string getName() { result = "block" }
final override predicate entry(AstNode first) {
first(this.(Trees::BlockTree).getFirstChildNode(), first)
}
@@ -137,8 +121,6 @@ module CfgScope {
}
private class LambdaScope extends Range_, Lambda {
final override string getName() { result = "lambda" }
final override predicate entry(AstNode first) {
first(this.getParameters(), first)
or
@@ -168,9 +150,6 @@ private AstNode parent(AstNode n) {
not n instanceof CfgScope
}
/** Gets the CFG scope of node `n`. */
CfgScope getScope(AstNode n) { result = unique(CfgScope scope | scope = parent+(n)) }
abstract private class ControlFlowTree extends AstNode {
/**
* Holds if `first` is the first element executed within this AST node.
@@ -913,7 +892,7 @@ module Trees {
pragma[noinline]
private AstNode getAChildInScope(AstNode n, CfgScope scope) {
result.getParent() = n and
scope = getScope(result)
scope = getCfgScope(result)
}
/** A block that may contain `rescue`/`ensure`. */
@@ -962,7 +941,7 @@ module Trees {
or
exists(AstNode mid |
mid = this.getAnEnsureDescendant() and
result = getAChildInScope(mid, getScope(mid)) and
result = getAChildInScope(mid, getCfgScope(mid)) and
not exists(RescueEnsureBlockTree nestedBlock |
result = nestedBlock.getEnsure() and
nestedBlock != this
@@ -976,7 +955,7 @@ module Trees {
*/
private predicate nestedEnsure(RescueEnsureBlockTree innerBlock) {
exists(Ensure innerEnsure |
innerEnsure = getAChildInScope(this.getAnEnsureDescendant(), getScope(this)) and
innerEnsure = getAChildInScope(this.getAnEnsureDescendant(), getCfgScope(this)) and
innerEnsure = innerBlock.getEnsure()
)
}
@@ -1279,6 +1258,10 @@ module Trees {
cached
private module Cached {
/** Gets the CFG scope of node `n`. */
cached
CfgScope getCfgScope(AstNode n) { result = unique(CfgScope scope | scope = parent+(n)) }
private predicate isAbnormalExitType(SuccessorType t) {
t instanceof RaiseSuccessor or t instanceof ExitSuccessor
}

View File

@@ -74,7 +74,7 @@ private predicate overlapping(CfgScope scope, SplitKind sk1, SplitKind sk2) {
exists(AstNode n |
sk1.appliesTo(n) and
sk2.appliesTo(n) and
scope = getScope(n)
scope = getCfgScope(n)
)
}
@@ -106,7 +106,7 @@ abstract private class SplitKind extends TSplitKind {
*/
predicate isEnabled(AstNode n) {
this.appliesTo(n) and
this.getRank(getScope(n)) <= maxSplits()
this.getRank(getCfgScope(n)) <= maxSplits()
}
/**

View File

@@ -0,0 +1,362 @@
/**
* Provides the module `Ssa` for working with static single assignment (SSA) form.
*/
/**
* Provides classes for working with static single assignment (SSA) form.
*/
module Ssa {
private import codeql.Locations
private import codeql_ruby.CFG
private import codeql_ruby.ast.Variable
private import internal.SsaImplCommon as SsaImplCommon
private import internal.SsaImpl as SsaImpl
private import CfgNodes::ExprNodes
/** A static single assignment (SSA) definition. */
class Definition extends SsaImplCommon::Definition {
/**
* Gets the control flow node of this SSA definition, if any. Phi nodes are
* examples of SSA definitions without a control flow node, as they are
* modelled at index `-1` in the relevant basic block.
*/
final CfgNode getControlFlowNode() {
exists(BasicBlock bb, int i | this.definesAt(_, bb, i) | result = bb.getNode(i))
}
/**
* Gets a control-flow node that reads the value of this SSA definition.
*
* Example:
*
* ```rb
* def m b # defines b_0
* i = 0 # defines i_0
* puts i # reads i_0
* puts i + 1 # reads i_0
* if b # reads b_0
* i = 1 # defines i_1
* puts i # reads i_1
* puts i + 1 # reads i_1
* else
* i = 2 # defines i_2
* puts i # reads i_2
* puts i + 1 # reads i_2
* end
* # defines i_3 = phi(i_1, i_2)
* puts i # reads i3
* end
* ```
*/
final VariableReadAccessCfgNode getARead() {
exists(LocalVariable v, BasicBlock bb, int i |
SsaImplCommon::ssaDefReachesRead(v, this, bb, i) and
SsaImpl::variableReadActual(bb, i, v) and
result = bb.getNode(i)
)
}
/**
* Gets a first control-flow node that reads the value of this SSA definition.
* That is, a read that can be reached from this definition without passing
* through other reads.
*
* Example:
*
* ```rb
* def m b # defines b_0
* i = 0 # defines i_0
* puts i # first read of i_0
* puts i + 1
* if b # first read of b_0
* i = 1 # defines i_1
* puts i # first read of i_1
* puts i + 1
* else
* i = 2 # defines i_2
* puts i # first read of i_2
* puts i + 1
* end
* # defines i_3 = phi(i_1, i_2)
* puts i # first read of i3
* end
* ```
*/
final VariableReadAccessCfgNode getAFirstRead() { SsaImpl::firstRead(this, result) }
/**
* Gets a last control-flow node that reads the value of this SSA definition.
* That is, a read that can reach the end of the enclosing CFG scope, or another
* SSA definition for the source variable, without passing through any other read.
*
* Example:
*
* ```rb
* def m b # defines b_0
* i = 0 # defines i_0
* puts i
* puts i + 1 # last read of i_0
* if b # last read of b_0
* i = 1 # defines i_1
* puts i
* puts i + 1 # last read of i_1
* else
* i = 2 # defines i_2
* puts i
* puts i + 1 # last read of i_2
* end
* # defines i_3 = phi(i_1, i_2)
* puts i # last read of i3
* end
* ```
*/
final VariableReadAccessCfgNode getALastRead() { SsaImpl::lastRead(this, result) }
/**
* Holds if `read1` and `read2` are adjacent reads of this SSA definition.
* That is, `read2` can be reached from `read1` without passing through
* another read.
*
* Example:
*
* ```rb
* def m b
* i = 0 # defines i_0
* puts i # reads i_0 (read1)
* puts i + 1 # reads i_0 (read2)
* if b
* i = 1 # defines i_1
* puts i # reads i_1 (read1)
* puts i + 1 # reads i_1 (read2)
* else
* i = 2 # defines i_2
* puts i # reads i_2 (read1)
* puts i + 1 # reads i_2 (read2)
* end
* puts i
* end
* ```
*/
final predicate hasAdjacentReads(
VariableReadAccessCfgNode read1, VariableReadAccessCfgNode read2
) {
SsaImpl::adjacentReadPair(this, read1, read2)
}
/**
* Gets a definition that ultimately defines this SSA definition and is
* not itself a phi node.
*
* Example:
*
* ```rb
* def m b
* i = 0 # defines i_0
* puts i
* puts i + 1
* if b
* i = 1 # defines i_1
* puts i
* puts i + 1
* else
* i = 2 # defines i_2
* puts i
* puts i + 1
* end
* # defines i_3 = phi(i_1, i_2); ultimate definitions are i_1 and i_2
* puts i
* end
* ```
*/
final override Definition getAnUltimateDefinition() {
result = SsaImplCommon::Definition.super.getAnUltimateDefinition()
}
override string toString() { result = this.getControlFlowNode().toString() }
/** Gets the location of this SSA definition. */
Location getLocation() { result = this.getControlFlowNode().getLocation() }
}
/**
* An SSA definition that corresponds to a write. For example `x = 10` in
*
* ```rb
* x = 10
* puts x
* ```
*/
class WriteDefinition extends Definition, SsaImplCommon::WriteDefinition {
private VariableWriteAccess write;
WriteDefinition() {
exists(BasicBlock bb, int i, Variable v |
this.definesAt(v, bb, i) and
SsaImpl::variableWriteActual(bb, i, v, write)
)
}
/** Gets the underlying write access. */
final VariableWriteAccess getWriteAccess() { result = write }
final override string toString() { result = Definition.super.toString() }
final override Location getLocation() { result = this.getControlFlowNode().getLocation() }
}
/**
* An SSA definition inserted at the beginning of a scope to represent an
* uninitialized local variable. For example, in
*
* ```rb
* def m
* x = 10 if b
* puts x
* end
* ```
*
* since the assignment to `x` is conditional, an unitialized definition for
* `x` is inserted at the start of `m`.
*/
class UninitializedDefinition extends Definition, SsaImplCommon::WriteDefinition {
UninitializedDefinition() {
exists(BasicBlock bb, int i, Variable v |
this.definesAt(v, bb, i) and
SsaImpl::uninitializedWrite(bb, i, v)
)
}
final override string toString() { result = "<uninitialized>" }
final override Location getLocation() { result = this.getBasicBlock().getLocation() }
}
/**
* An SSA definition inserted at the beginning of a scope to represent a
* captured local variable. For example, in
*
* ```rb
* def m x
* y = 0
* x.times do |x|
* y += x
* end
* return y
* end
* ```
*
* an entry definition for `y` is inserted at the start of the `do` block.
*/
class CapturedEntryDefinition extends Definition, SsaImplCommon::WriteDefinition {
CapturedEntryDefinition() {
exists(BasicBlock bb, int i, Variable v |
this.definesAt(v, bb, i) and
SsaImpl::capturedEntryWrite(bb, i, v)
)
}
final override string toString() { result = "<captured>" }
override Location getLocation() { result = this.getBasicBlock().getLocation() }
}
/**
* An SSA definition inserted at a call that may update the value of a captured
* variable. For example, in
*
* ```rb
* def m x
* y = 0
* x.times do |x|
* y += x
* end
* return y
* end
* ```
*
* a definition for `y` is inserted at the call to `times`.
*/
class CapturedCallDefinition extends Definition, SsaImplCommon::UncertainWriteDefinition {
CapturedCallDefinition() {
exists(Variable v, BasicBlock bb, int i |
this.definesAt(v, bb, i) and
SsaImpl::capturedCallWrite(bb, i, v)
)
}
final override Definition getPriorDefinition() {
result = SsaImplCommon::UncertainWriteDefinition.super.getPriorDefinition()
}
override string toString() { result = this.getControlFlowNode().toString() }
}
/**
* A phi node. For example, in
*
* ```rb
* if b
* x = 0
* else
* x = 1
* end
* puts x
* ```
*
* a phi node for `x` is inserted just before the call `puts x`.
*/
class PhiNode extends Definition, SsaImplCommon::PhiNode {
/**
* Gets an input of this phi node.
*
* Example:
*
* ```rb
* def m b
* i = 0 # defines i_0
* puts i
* puts i + 1
* if b
* i = 1 # defines i_1
* puts i
* puts i + 1
* else
* i = 2 # defines i_2
* puts i
* puts i + 1
* end
* # defines i_3 = phi(i_1, i_2); inputs are i_1 and i_2
* puts i
* end
* ```
*/
final override Definition getAnInput() { result = SsaImplCommon::PhiNode.super.getAnInput() }
private string getSplitString() {
result = this.getBasicBlock().getFirstNode().(CfgNodes::AstCfgNode).getSplitsString()
}
override string toString() {
exists(string prefix |
prefix = "[" + this.getSplitString() + "] "
or
not exists(this.getSplitString()) and
prefix = ""
|
result = prefix + "phi"
)
}
/*
* The location of a phi node is the same as the location of the first node
* in the basic block in which it is defined.
*
* Strictly speaking, the node is *before* the first node, but such a location
* does not exist in the source program.
*/
final override Location getLocation() {
result = this.getBasicBlock().getFirstNode().getLocation()
}
}
}

View File

@@ -0,0 +1,272 @@
private import SsaImplCommon
private import codeql_ruby.AST
private import codeql_ruby.CFG
private import codeql_ruby.ast.Variable
private import CfgNodes::ExprNodes
/** Holds if `v` is uninitialized at index `i` in entry block `bb`. */
predicate uninitializedWrite(EntryBasicBlock bb, int i, LocalVariable v) {
v.getDeclaringScope().getScopeElement() = bb.getScope() and
i = -1
}
/** Holds if `bb` contains a caputured read of variable `v`. */
pragma[noinline]
private predicate hasCapturedVariableRead(BasicBlock bb, LocalVariable v) {
exists(LocalVariableReadAccess read |
read = bb.getANode().getNode() and
read.isCapturedAccess() and
read.getVariable() = v
)
}
/**
* Holds if an entry definition is needed for captured variable `v` at index
* `i` in entry block `bb`.
*/
predicate capturedEntryWrite(EntryBasicBlock bb, int i, LocalVariable v) {
hasCapturedVariableRead(bb.getASuccessor*(), v) and
i = -1
}
/** Holds if `bb` contains a caputured write to variable `v`. */
pragma[noinline]
private predicate writesCapturedVariable(BasicBlock bb, LocalVariable v) {
exists(LocalVariableWriteAccess write |
write = bb.getANode().getNode() and
write.isCapturedAccess() and
write.getVariable() = v
)
}
/**
* Holds if a pseudo read of captured variable `v` should be inserted
* at index `i` in exit block `bb`.
*/
private predicate capturedExitRead(AnnotatedExitBasicBlock bb, int i, LocalVariable v) {
bb.isNormal() and
writesCapturedVariable(bb.getAPredecessor*(), v) and
i = bb.length()
}
private CfgScope getCaptureOuterCfgScope(Callable scope) {
result = scope.getOuterCfgScope() and
(
scope instanceof Block
or
scope instanceof Lambda
)
}
/** Holds if captured variable `v` is read inside `scope`. */
pragma[noinline]
private predicate hasCapturedRead(Variable v, CfgScope scope) {
any(LocalVariableReadAccess read |
read.getVariable() = v and scope = getCaptureOuterCfgScope*(read.getCfgScope())
).isCapturedAccess()
}
pragma[noinline]
private predicate hasVariableWriteWithCapturedRead(BasicBlock bb, LocalVariable v, CfgScope scope) {
hasCapturedRead(v, scope) and
exists(VariableWriteAccess write |
write = bb.getANode().getNode() and
write.getVariable() = v and
bb.getScope() = scope.getOuterCfgScope()
)
}
/**
* Holds if the call at index `i` in basic block `bb` may reach a callable
* that reads captured variable `v`.
*/
private predicate capturedCallRead(BasicBlock bb, int i, LocalVariable v) {
exists(CfgScope scope |
hasVariableWriteWithCapturedRead(bb.getAPredecessor*(), v, scope) and
bb.getNode(i).getNode() instanceof Call
|
not scope instanceof Block
or
// If the read happens inside a block, we restrict to the call that
// contains the block
scope = any(Call c | bb.getNode(i) = c.getAControlFlowNode()).getBlock()
)
}
/** Holds if captured variable `v` is written inside `scope`. */
pragma[noinline]
private predicate hasCapturedWrite(Variable v, CfgScope scope) {
any(LocalVariableWriteAccess write |
write.getVariable() = v and scope = getCaptureOuterCfgScope*(write.getCfgScope())
).isCapturedAccess()
}
/** Holds if `v` is read at index `i` in basic block `bb`. */
predicate variableReadActual(BasicBlock bb, int i, LocalVariable v) {
exists(VariableReadAccess read |
read.getVariable() = v and
read = bb.getNode(i).getNode()
)
}
/**
* Holds if a pseudo read of `v` is inserted at index `i` in basic block `bb`.
*
* Pseudo reads are used to make otherwise dead assignments live, as they will
* otherwise not get an SSA definition.
*/
predicate variableReadPseudo(BasicBlock bb, int i, LocalVariable v) {
capturedCallRead(bb, i, v)
or
capturedExitRead(bb, i, v)
}
pragma[noinline]
private predicate hasVariableReadWithCapturedWrite(BasicBlock bb, LocalVariable v, CfgScope scope) {
hasCapturedWrite(v, scope) and
exists(VariableReadAccess read |
read = bb.getANode().getNode() and
read.getVariable() = v and
bb.getScope() = scope.getOuterCfgScope()
)
}
private predicate adjacentDefReaches(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
adjacentDefRead(def, bb1, i1, bb2, i2)
or
exists(BasicBlock bb3, int i3 |
adjacentDefReaches(def, bb1, i1, bb3, i3) and
variableReadPseudo(bb3, i3, _) and
adjacentDefRead(def, bb3, i3, bb2, i2)
)
}
pragma[noinline]
private predicate adjacentDefActualRead(
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
) {
adjacentDefReaches(def, bb1, i1, bb2, i2) and
variableReadActual(bb2, i2, _)
}
private predicate adjacentDefPseudoRead(
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
) {
adjacentDefReaches(def, bb1, i1, bb2, i2) and
variableReadPseudo(bb2, i2, _)
}
private predicate reachesLastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
lastRefRedef(def, bb, i, next)
or
exists(BasicBlock bb0, int i0 |
reachesLastRefRedef(def, bb0, i0, next) and
adjacentDefPseudoRead(def, bb, i, bb0, i0)
)
}
private predicate reachesLastRef(Definition def, BasicBlock bb, int i) {
lastRef(def, bb, i)
or
exists(BasicBlock bb0, int i0 |
reachesLastRef(def, bb0, i0) and
adjacentDefPseudoRead(def, bb, i, bb0, i0)
)
}
cached
private module Cached {
/**
* Holds if the call at index `i` in basic block `bb` may reach a callable
* that writes captured variable `v`.
*/
cached
predicate capturedCallWrite(BasicBlock bb, int i, LocalVariable v) {
exists(CfgScope scope |
hasVariableReadWithCapturedWrite(bb.getASuccessor*(), v, scope) and
bb.getNode(i).getNode() instanceof Call
|
not scope instanceof Block
or
// If the write happens inside a block, we restrict to the call that
// contains the block
scope = any(Call c | bb.getNode(i) = c.getAControlFlowNode()).getBlock()
)
}
/**
* Holds if `v` is written at index `i` in basic block `bb`, and the corresponding
* AST write access is `write`.
*/
cached
predicate variableWriteActual(BasicBlock bb, int i, LocalVariable v, VariableWriteAccess write) {
exists(AstNode n |
write.getVariable() = v and
n = bb.getNode(i).getNode()
|
write.isExplicitWrite(n)
or
write.isImplicitWrite() and
n = write
)
}
/**
* Holds if the value defined at SSA definition `def` can reach a read at `read`,
* without passing through any other non-pseudo read.
*/
cached
predicate firstRead(Definition def, VariableReadAccessCfgNode read) {
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
def.definesAt(_, bb1, i1) and
adjacentDefActualRead(def, bb1, i1, bb2, i2) and
read = bb2.getNode(i2)
)
}
/**
* Holds if the read at `read2` is a read of the same SSA definition `def`
* as the read at `read1`, and `read2` can be reached from `read1` without
* passing through another non-pseudo read.
*/
cached
predicate adjacentReadPair(
Definition def, VariableReadAccessCfgNode read1, VariableReadAccessCfgNode read2
) {
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
read1 = bb1.getNode(i1) and
variableReadActual(bb1, i1, _) and
adjacentDefActualRead(def, bb1, i1, bb2, i2) and
read2 = bb2.getNode(i2)
)
}
/**
* Holds if the read of `def` at `read` may be a last read. That is, `read`
* can either reach another definition of the underlying source variable or
* the end of the CFG scope, without passing through another non-pseudo read.
*/
cached
predicate lastRead(Definition def, VariableReadAccessCfgNode read) {
exists(BasicBlock bb, int i |
reachesLastRef(def, bb, i) and
variableReadActual(bb, i, _) and
read = bb.getNode(i)
)
}
/**
* Holds if the reference to `def` at index `i` in basic block `bb` can reach
* another definition `next` of the same underlying source variable, without
* passing through another write or non-pseudo read.
*
* The reference is either a read of `def` or `def` itself.
*/
cached
predicate lastRefBeforeRedef(Definition def, BasicBlock bb, int i, Definition next) {
reachesLastRefRedef(def, bb, i, next) and
not variableReadPseudo(bb, i, def.getSourceVariable())
}
}
import Cached

View File

@@ -0,0 +1,590 @@
/**
* Provides a language-independant implementation of static single assignment
* (SSA) form.
*/
private import SsaImplSpecific
private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { getABasicBlockSuccessor(result) = bb }
cached
private module Cached {
/**
* Liveness analysis (based on source variables) to restrict the size of the
* SSA representation.
*/
private module Liveness {
/**
* A classification of variable references into reads (of a given kind) and
* (certain or uncertain) writes.
*/
private newtype TRefKind =
Read() or
Write(boolean certain) { certain = true or certain = false }
private class RefKind extends TRefKind {
string toString() {
this = Read() and result = "read"
or
exists(boolean certain | this = Write(certain) and result = "write (" + certain + ")")
}
int getOrder() {
this = Read() and
result = 0
or
this = Write(_) and
result = 1
}
}
/**
* Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`.
*/
private predicate ref(BasicBlock bb, int i, SourceVariable v, RefKind k) {
variableRead(bb, i, v) and k = Read()
or
exists(boolean certain | variableWrite(bb, i, v, certain) | k = Write(certain))
}
private newtype OrderedRefIndex =
MkOrderedRefIndex(int i, int tag) {
exists(RefKind rk | ref(_, i, _, rk) | tag = rk.getOrder())
}
private OrderedRefIndex refOrd(BasicBlock bb, int i, SourceVariable v, RefKind k, int ord) {
ref(bb, i, v, k) and
result = MkOrderedRefIndex(i, ord) and
ord = k.getOrder()
}
/**
* Gets the (1-based) rank of the reference to `v` at the `i`th node of
* basic block `bb`, which has the given reference kind `k`.
*
* Reads are considered before writes when they happen at the same index.
*/
private int refRank(BasicBlock bb, int i, SourceVariable v, RefKind k) {
refOrd(bb, i, v, k, _) =
rank[result](int j, int ord, OrderedRefIndex res |
res = refOrd(bb, j, v, _, ord)
|
res order by j, ord
)
}
private int maxRefRank(BasicBlock bb, SourceVariable v) {
result = refRank(bb, _, v, _) and
not result + 1 = refRank(bb, _, v, _)
}
/**
* Gets the (1-based) rank of the first reference to `v` inside basic block `bb`
* that is either a read or a certain write.
*/
private int firstReadOrCertainWrite(BasicBlock bb, SourceVariable v) {
result =
min(int r, RefKind k |
r = refRank(bb, _, v, k) and
k != Write(false)
|
r
)
}
/**
* Holds if source variable `v` is live at the beginning of basic block `bb`.
*/
predicate liveAtEntry(BasicBlock bb, SourceVariable v) {
// The first read or certain write to `v` inside `bb` is a read
refRank(bb, _, v, Read()) = firstReadOrCertainWrite(bb, v)
or
// There is no certain write to `v` inside `bb`, but `v` is live at entry
// to a successor basic block of `bb`
not exists(firstReadOrCertainWrite(bb, v)) and
liveAtExit(bb, v)
}
/**
* Holds if source variable `v` is live at the end of basic block `bb`.
*/
predicate liveAtExit(BasicBlock bb, SourceVariable v) {
liveAtEntry(getABasicBlockSuccessor(bb), v)
}
/**
* Holds if variable `v` is live in basic block `bb` at index `i`.
* The rank of `i` is `rnk` as defined by `refRank()`.
*/
private predicate liveAtRank(BasicBlock bb, int i, SourceVariable v, int rnk) {
exists(RefKind kind | rnk = refRank(bb, i, v, kind) |
rnk = maxRefRank(bb, v) and
liveAtExit(bb, v)
or
ref(bb, i, v, kind) and
kind = Read()
or
exists(RefKind nextKind |
liveAtRank(bb, _, v, rnk + 1) and
rnk + 1 = refRank(bb, _, v, nextKind) and
nextKind != Write(true)
)
)
}
/**
* Holds if variable `v` is live after the (certain or uncertain) write at
* index `i` inside basic block `bb`.
*/
predicate liveAfterWrite(BasicBlock bb, int i, SourceVariable v) {
exists(int rnk | rnk = refRank(bb, i, v, Write(_)) | liveAtRank(bb, i, v, rnk))
}
}
private import Liveness
/** Holds if `bb1` strictly dominates `bb2`. */
private predicate strictlyDominates(BasicBlock bb1, BasicBlock bb2) {
bb1 = getImmediateBasicBlockDominator+(bb2)
}
/** Holds if `bb1` dominates a predecessor of `bb2`. */
private predicate dominatesPredecessor(BasicBlock bb1, BasicBlock bb2) {
exists(BasicBlock pred | pred = getABasicBlockPredecessor(bb2) |
bb1 = pred
or
strictlyDominates(bb1, pred)
)
}
/** Holds if `df` is in the dominance frontier of `bb`. */
private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) {
dominatesPredecessor(bb, df) and
not strictlyDominates(bb, df)
}
/**
* Holds if `bb` is in the dominance frontier of a block containing a
* definition of `v`.
*/
pragma[noinline]
private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) {
exists(BasicBlock defbb, Definition def |
def.definesAt(v, defbb, _) and
inDominanceFrontier(defbb, bb)
)
}
cached
newtype TDefinition =
TWriteDef(SourceVariable v, BasicBlock bb, int i) {
variableWrite(bb, i, v, _) and
liveAfterWrite(bb, i, v)
} or
TPhiNode(SourceVariable v, BasicBlock bb) {
inDefDominanceFrontier(bb, v) and
liveAtEntry(bb, v)
}
private module SsaDefReaches {
newtype TSsaRefKind =
SsaRead() or
SsaDef()
/**
* A classification of SSA variable references into reads and definitions.
*/
class SsaRefKind extends TSsaRefKind {
string toString() {
this = SsaRead() and
result = "SsaRead"
or
this = SsaDef() and
result = "SsaDef"
}
int getOrder() {
this = SsaRead() and
result = 0
or
this = SsaDef() and
result = 1
}
}
/**
* Holds if the `i`th node of basic block `bb` is a reference to `v`,
* either a read (when `k` is `SsaRead()`) or an SSA definition (when `k`
* is `SsaDef()`).
*
* Unlike `Liveness::ref`, this includes `phi` nodes.
*/
predicate ssaRef(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
variableRead(bb, i, v) and
k = SsaRead()
or
exists(Definition def | def.definesAt(v, bb, i)) and
k = SsaDef()
}
private newtype OrderedSsaRefIndex =
MkOrderedSsaRefIndex(int i, SsaRefKind k) { ssaRef(_, i, _, k) }
private OrderedSsaRefIndex ssaRefOrd(
BasicBlock bb, int i, SourceVariable v, SsaRefKind k, int ord
) {
ssaRef(bb, i, v, k) and
result = MkOrderedSsaRefIndex(i, k) and
ord = k.getOrder()
}
/**
* Gets the (1-based) rank of the reference to `v` at the `i`th node of basic
* block `bb`, which has the given reference kind `k`.
*
* For example, if `bb` is a basic block with a phi node for `v` (considered
* to be at index -1), reads `v` at node 2, and defines it at node 5, we have:
*
* ```ql
* ssaRefRank(bb, -1, v, SsaDef()) = 1 // phi node
* ssaRefRank(bb, 2, v, Read()) = 2 // read at node 2
* ssaRefRank(bb, 5, v, SsaDef()) = 3 // definition at node 5
* ```
*
* Reads are considered before writes when they happen at the same index.
*/
int ssaRefRank(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
ssaRefOrd(bb, i, v, k, _) =
rank[result](int j, int ord, OrderedSsaRefIndex res |
res = ssaRefOrd(bb, j, v, _, ord)
|
res order by j, ord
)
}
int maxSsaRefRank(BasicBlock bb, SourceVariable v) {
result = ssaRefRank(bb, _, v, _) and
not result + 1 = ssaRefRank(bb, _, v, _)
}
/**
* Holds if the SSA definition `def` reaches rank index `rnk` in its own
* basic block `bb`.
*/
predicate ssaDefReachesRank(BasicBlock bb, Definition def, int rnk, SourceVariable v) {
exists(int i |
rnk = ssaRefRank(bb, i, v, SsaDef()) and
def.definesAt(v, bb, i)
)
or
ssaDefReachesRank(bb, def, rnk - 1, v) and
rnk = ssaRefRank(bb, _, v, SsaRead())
}
/**
* Holds if the SSA definition of `v` at `def` reaches index `i` in the same
* basic block `bb`, without crossing another SSA definition of `v`.
*/
predicate ssaDefReachesReadWithinBlock(SourceVariable v, Definition def, BasicBlock bb, int i) {
exists(int rnk |
ssaDefReachesRank(bb, def, rnk, v) and
rnk = ssaRefRank(bb, i, v, SsaRead()) and
variableRead(bb, i, v)
)
}
/**
* Holds if the SSA definition of `v` at `def` reaches uncertain SSA definition
* `redef` in the same basic block, without crossing another SSA definition of `v`.
*/
predicate ssaDefReachesUncertainDefWithinBlock(
SourceVariable v, Definition def, UncertainWriteDefinition redef
) {
exists(BasicBlock bb, int rnk, int i |
ssaDefReachesRank(bb, def, rnk, v) and
rnk = ssaRefRank(bb, i, v, SsaDef()) - 1 and
redef.definesAt(v, bb, i)
)
}
/**
* Same as `ssaRefRank()`, but restricted to a particular SSA definition `def`.
*/
int ssaDefRank(Definition def, SourceVariable v, BasicBlock bb, int i, SsaRefKind k) {
v = def.getSourceVariable() and
result = ssaRefRank(bb, i, v, k) and
(
ssaDefReachesRead(_, def, bb, i)
or
def.definesAt(_, bb, i)
)
}
predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) {
exists(ssaDefRank(def, v, bb, _, _))
}
pragma[noinline]
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) {
result = getABasicBlockSuccessor(bb) and
not defOccursInBlock(_, bb, def.getSourceVariable()) and
ssaDefReachesEndOfBlock(bb, def, _)
}
/**
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
* `bb2` is a transitive successor of `bb1`, `def` is live at the end of `bb1`,
* and the underlying variable for `def` is neither read nor written in any block
* on the path between `bb1` and `bb2`.
*/
predicate varBlockReaches(Definition def, BasicBlock bb1, BasicBlock bb2) {
defOccursInBlock(def, bb1, _) and
bb2 = getABasicBlockSuccessor(bb1)
or
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) |
bb2 = getAMaybeLiveSuccessor(def, mid)
)
}
/**
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
* `def` is read at index `i2` in basic block `bb2`, `bb2` is in a transitive
* successor block of `bb1`, and `def` is neither read nor written in any block
* on a path between `bb1` and `bb2`.
*/
predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
varBlockReaches(def, bb1, bb2) and
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and
variableRead(bb2, i2, _)
}
}
private import SsaDefReaches
pragma[noinline]
private predicate ssaDefReachesEndOfBlockRec(BasicBlock bb, Definition def, SourceVariable v) {
exists(BasicBlock idom | ssaDefReachesEndOfBlock(idom, def, v) |
// The construction of SSA form ensures that each read of a variable is
// dominated by its definition. An SSA definition therefore reaches a
// control flow node if it is the _closest_ SSA definition that dominates
// the node. If two definitions dominate a node then one must dominate the
// other, so therefore the definition of _closest_ is given by the dominator
// tree. Thus, reaching definitions can be calculated in terms of dominance.
idom = getImmediateBasicBlockDominator(bb)
)
}
/**
* Holds if the SSA definition of `v` at `def` reaches the end of basic
* block `bb`, at which point it is still live, without crossing another
* SSA definition of `v`.
*/
cached
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) {
exists(int last | last = maxSsaRefRank(bb, v) |
ssaDefReachesRank(bb, def, last, v) and
liveAtExit(bb, v)
)
or
ssaDefReachesEndOfBlockRec(bb, def, v) and
liveAtExit(bb, v) and
not ssaRef(bb, _, v, SsaDef())
}
/**
* Holds if the SSA definition of `v` at `def` reaches a read at index `i` in
* basic block `bb`, without crossing another SSA definition of `v`. The read
* is of kind `rk`.
*/
cached
predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) {
ssaDefReachesReadWithinBlock(v, def, bb, i)
or
variableRead(bb, i, v) and
ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and
not ssaDefReachesReadWithinBlock(v, _, bb, i)
}
/**
* Holds if the SSA definition of `v` at `def` reaches uncertain SSA definition
* `redef` without crossing another SSA definition of `v`.
*/
cached
predicate ssaDefReachesUncertainDef(
SourceVariable v, Definition def, UncertainWriteDefinition redef
) {
ssaDefReachesUncertainDefWithinBlock(v, def, redef)
or
exists(BasicBlock bb |
redef.definesAt(v, bb, _) and
ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and
not ssaDefReachesUncertainDefWithinBlock(v, _, redef)
)
}
/**
* Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read
* or a write), `def` is read at index `i2` in basic block `bb2`, and there is a
* path between them without any read of `def`.
*/
cached
predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
exists(int rnk |
rnk = ssaDefRank(def, _, bb1, i1, _) and
rnk + 1 = ssaDefRank(def, _, bb1, i2, SsaRead()) and
variableRead(bb1, i2, _) and
bb2 = bb1
)
or
exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and
defAdjacentRead(def, bb1, bb2, i2)
}
/**
* Holds if the node at index `i` in `bb` is a last reference to SSA definition
* `def`. The reference is last because it can reach another write `next`,
* without passing through another read or write.
*/
cached
predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) |
// Next reference to `v` inside `bb` is a write
next.definesAt(v, bb, j) and
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
or
// Can reach a write using one or more steps
rnk = maxSsaRefRank(bb, v) and
exists(BasicBlock bb2 |
varBlockReaches(def, bb, bb2) and
next.definesAt(v, bb2, j) and
1 = ssaRefRank(bb2, j, v, SsaDef())
)
)
}
/**
* Holds if the node at index `i` in `bb` is a last reference to SSA
* definition `def`.
*
* That is, the node can reach the end of the enclosing callable, or another
* SSA definition for the underlying source variable, without passing through
* another read.
*/
cached
predicate lastRef(Definition def, BasicBlock bb, int i) {
lastRefRedef(def, bb, i, _)
or
exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) |
// Can reach exit directly
bb instanceof ExitBasicBlock
or
// Can reach a block using one or more steps, where `def` is no longer live
exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) |
not defOccursInBlock(def, bb2, _) and
not ssaDefReachesEndOfBlock(bb2, def, _)
)
)
}
}
import Cached
/** A static single assignment (SSA) definition. */
class Definition extends TDefinition {
/** Gets the source variable underlying this SSA definition. */
SourceVariable getSourceVariable() { this.definesAt(result, _, _) }
/**
* Holds is this SSA definition is live at the end of basic block `bb`.
* That is, this definition reaches the end of basic block `bb`, at which
* point it is still live, without crossing another SSA definition of the
* same source variable.
*/
final predicate isLiveAtEndOfBlock(BasicBlock bb) { ssaDefReachesEndOfBlock(bb, this, _) }
/**
* Holds if this SSA definition defines `v` at index `i` in basic block `bb`.
* Phi nodes are considered to be at index `-1`, while normal variable writes
* are at the index of the control flow node they wrap.
*/
final predicate definesAt(SourceVariable v, BasicBlock bb, int i) {
this = TWriteDef(v, bb, i)
or
this = TPhiNode(v, bb) and i = -1
}
/** Gets the basic block to which this SSA definition belongs. */
final BasicBlock getBasicBlock() { this.definesAt(_, result, _) }
/**
* Gets an SSA definition whose value can flow to this one in one step. This
* includes inputs to phi nodes and the prior definitions of uncertain writes.
*/
private Definition getAPseudoInputOrPriorDefinition() {
result = this.(PhiNode).getAnInput() or
result = this.(UncertainWriteDefinition).getPriorDefinition()
}
/**
* Gets a definition that ultimately defines this SSA definition and is
* not itself a phi node.
*/
Definition getAnUltimateDefinition() {
result = this.getAPseudoInputOrPriorDefinition*() and
not result instanceof PhiNode
}
/** Gets a textual representation of this SSA definition. */
string toString() { none() }
}
/** An SSA definition that corresponds to a write. */
class WriteDefinition extends Definition, TWriteDef {
private SourceVariable v;
private BasicBlock bb;
private int i;
WriteDefinition() { this = TWriteDef(v, bb, i) }
override string toString() { result = "WriteDef" }
}
/** A phi node. */
class PhiNode extends Definition, TPhiNode {
/** Gets an input of this phi node. */
Definition getAnInput() {
exists(BasicBlock bb, BasicBlock pred, SourceVariable v |
this.definesAt(v, bb, _) and
getABasicBlockPredecessor(bb) = pred and
ssaDefReachesEndOfBlock(pred, result, v)
)
}
/** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
predicate hasInputFromBlock(Definition inp, BasicBlock bb) {
inp = this.getAnInput() and
getABasicBlockPredecessor(this.getBasicBlock()) = bb and
ssaDefReachesEndOfBlock(bb, inp, _)
}
override string toString() { result = "Phi" }
}
/**
* An SSA definition that represents an uncertain update of the underlying
* source variable.
*/
class UncertainWriteDefinition extends WriteDefinition {
UncertainWriteDefinition() {
exists(SourceVariable v, BasicBlock bb, int i |
this.definesAt(v, bb, i) and
variableWrite(bb, i, v, false)
)
}
/**
* Gets the immediately preceding definition. Since this update is uncertain,
* the value from the preceding definition might still be valid.
*/
Definition getPriorDefinition() { ssaDefReachesUncertainDef(_, result, this) }
}

View File

@@ -0,0 +1,38 @@
/** Provides the Ruby specific parameters for `SsaImplCommon.qll`. */
private import SsaImpl as SsaImpl
private import codeql_ruby.AST
private import codeql_ruby.ast.Parameter
private import codeql_ruby.ast.Variable
private import codeql_ruby.controlflow.BasicBlocks as BasicBlocks
private import codeql_ruby.controlflow.ControlFlowGraph
class BasicBlock = BasicBlocks::BasicBlock;
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() }
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
class ExitBasicBlock = BasicBlocks::ExitBasicBlock;
class SourceVariable = LocalVariable;
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
(
SsaImpl::uninitializedWrite(bb, i, v)
or
SsaImpl::capturedEntryWrite(bb, i, v)
or
SsaImpl::variableWriteActual(bb, i, v, _)
) and
certain = true
or
SsaImpl::capturedCallWrite(bb, i, v) and
certain = false
}
predicate variableRead(BasicBlock bb, int i, SourceVariable v) {
SsaImpl::variableReadActual(bb, i, v)
or
SsaImpl::variableReadPseudo(bb, i, v)
}

View File

@@ -0,0 +1,28 @@
/**
* @name Useless assignment to local variable
* @description An assignment to a local variable that is not used later on, or whose value is always
* overwritten, has no effect.
* @kind problem
* @problem.severity warning
* @id rb/useless-assignment-to-local
* @tags maintainability
* external/cwe/cwe-563
* @precision low
*/
import ruby
import codeql_ruby.dataflow.SSA
class RelevantLocalVariableWriteAccess extends LocalVariableWriteAccess {
RelevantLocalVariableWriteAccess() {
not this.getVariable().getName().charAt(0) = "_" and
not this = any(Parameter p).getAVariable().getDefiningAccess()
}
}
from RelevantLocalVariableWriteAccess write, LocalVariable v
where
v = write.getVariable() and
exists(write.getAControlFlowNode()) and
not exists(Ssa::WriteDefinition def | def.getWriteAccess() = write)
select write, "This assignment to $@ is useless, since its value is never read.", v, v.getName()

View File

@@ -0,0 +1,32 @@
/**
* @name Potentially uninitialized local variable
* @description Using a local variable before it is initialized gives the variable a default
* 'nil' value.
* @kind problem
* @problem.severity error
* @id rb/uninitialized-local-variable
* @tags reliability
* correctness
* @precision low
*/
import ruby
import codeql_ruby.dataflow.SSA
class RelevantLocalVariableReadAccess extends LocalVariableReadAccess {
RelevantLocalVariableReadAccess() {
not exists(Call c |
c.getReceiver() = this and
c.getMethodName() = "nil?"
)
}
}
from RelevantLocalVariableReadAccess read, LocalVariable v
where
v = read.getVariable() and
exists(Ssa::Definition def |
def.getAnUltimateDefinition() instanceof Ssa::UninitializedDefinition and
read = def.getARead().getExpr()
)
select read, "Local variable $@ may be used before it is initialized.", v, v.getName()

View File

@@ -0,0 +1,27 @@
/**
* @name Unused parameter.
* @description A parameter that is not used later on, or whose value is always overwritten,
* can be removed.
* @kind problem
* @problem.severity warning
* @id rb/unused-parameter
* @tags maintainability
* external/cwe/cwe-563
* @precision low
*/
import ruby
import codeql_ruby.dataflow.SSA
class RelevantParameterVariable extends LocalVariable {
RelevantParameterVariable() {
exists(Parameter p |
this = p.getAVariable() and
not this.getName().charAt(0) = "_"
)
}
}
from RelevantParameterVariable v
where not exists(Ssa::WriteDefinition def | def.getWriteAccess() = v.getDefiningAccess())
select v, "Unused parameter."

View File

@@ -1,5 +1,5 @@
break_ensure.rb:
# 1| enter top-level
# 1| enter AstNode
#-----| -> m1
# 1| enter m1
@@ -15,26 +15,26 @@ break_ensure.rb:
#-----| -> elements
case.rb:
# 1| enter top-level
# 1| enter AstNode
#-----| -> if_in_case
# 1| enter if_in_case
#-----| -> case ...
cfg.rb:
# 1| enter top-level
# 1| enter AstNode
#-----| -> bar
# 15| enter BEGIN block
# 15| enter AstNode
#-----| -> puts
# 19| enter END block
# 19| enter AstNode
#-----| -> puts
# 25| enter block
# 25| enter { ... }
#-----| -> x
# 29| enter block
# 29| enter { ... }
#-----| -> x
# 63| enter pattern
@@ -46,7 +46,7 @@ cfg.rb:
# 101| enter parameters
#-----| -> value
# 120| enter lambda
# 120| enter -> { ... }
#-----| -> x
# 142| enter print
@@ -61,11 +61,11 @@ cfg.rb:
# 187| enter run_block
#-----| -> call to yield
# 191| enter block
# 191| enter { ... }
#-----| -> x
exit.rb:
# 1| enter top-level
# 1| enter AstNode
#-----| -> m1
# 1| enter m1
@@ -75,14 +75,14 @@ exit.rb:
#-----| -> x
heredoc.rb:
# 1| enter top-level
# 1| enter AstNode
#-----| -> double_heredoc
# 1| enter double_heredoc
#-----| -> puts
ifs.rb:
# 1| enter top-level
# 1| enter AstNode
#-----| -> m1
# 1| enter m1
@@ -107,7 +107,7 @@ ifs.rb:
#-----| -> true
loops.rb:
# 1| enter top-level
# 1| enter AstNode
#-----| -> m1
# 1| enter m1
@@ -119,11 +119,11 @@ loops.rb:
# 24| enter m3
#-----| -> 1
# 25| enter do block
# 25| enter do ... end
#-----| -> x
raise.rb:
# 1| enter top-level
# 1| enter AstNode
#-----| -> Class
# 7| enter m1
@@ -168,7 +168,7 @@ raise.rb:
# 154| enter m14
#-----| -> element
# 155| enter block
# 155| enter { ... }
#-----| -> elem
break_ensure.rb:
@@ -410,7 +410,7 @@ break_ensure.rb:
#-----| -> call to puts
# 44| m4
#-----| -> exit top-level (normal)
#-----| -> exit AstNode (normal)
# 44| m4
#-----| -> m4
@@ -496,7 +496,7 @@ break_ensure.rb:
case.rb:
# 1| if_in_case
#-----| -> exit top-level (normal)
#-----| -> exit AstNode (normal)
# 1| if_in_case
#-----| -> if_in_case
@@ -617,7 +617,7 @@ cfg.rb:
#-----| -> EndBlock
# 16| call to puts
#-----| -> exit BEGIN block (normal)
#-----| -> exit AstNode (normal)
# 16| puts
#-----| -> hello
@@ -629,7 +629,7 @@ cfg.rb:
#-----| -> 41
# 20| call to puts
#-----| -> exit END block (normal)
#-----| -> exit AstNode (normal)
# 20| puts
#-----| -> world
@@ -662,7 +662,7 @@ cfg.rb:
#-----| -> puts
# 25| call to puts
#-----| -> exit block (normal)
#-----| -> exit { ... } (normal)
# 25| puts
#-----| -> x
@@ -698,7 +698,7 @@ cfg.rb:
#-----| -> x
# 29| call to call
#-----| -> exit block (normal)
#-----| -> exit { ... } (normal)
# 29| x
#-----| -> call
@@ -1399,7 +1399,7 @@ cfg.rb:
#-----| -> (..., ...)
# 120| Array
#-----| -> exit lambda (normal)
#-----| -> exit -> { ... } (normal)
# 120| y
#-----| -> x
@@ -2021,7 +2021,7 @@ cfg.rb:
# 188| 42
# 191| call to run_block
#-----| -> exit top-level (normal)
#-----| -> exit AstNode (normal)
# 191| run_block
#-----| -> { ... }
@@ -2033,7 +2033,7 @@ cfg.rb:
#-----| -> puts
# 191| call to puts
#-----| -> exit block (normal)
#-----| -> exit { ... } (normal)
# 191| puts
#-----| -> x
@@ -2083,7 +2083,7 @@ exit.rb:
#-----| -> call to puts
# 8| m2
#-----| -> exit top-level (normal)
#-----| -> exit AstNode (normal)
# 8| m2
#-----| -> m2
@@ -2124,7 +2124,7 @@ exit.rb:
heredoc.rb:
# 1| double_heredoc
#-----| -> exit top-level (normal)
#-----| -> exit AstNode (normal)
# 1| double_heredoc
#-----| -> double_heredoc
@@ -2501,7 +2501,7 @@ ifs.rb:
#-----| -> ... == ...
# 40| constant_condition
#-----| -> exit top-level (normal)
#-----| -> exit AstNode (normal)
# 40| constant_condition
#-----| -> constant_condition
@@ -2663,7 +2663,7 @@ loops.rb:
#-----| -> call to puts
# 24| m3
#-----| -> exit top-level (normal)
#-----| -> exit AstNode (normal)
# 24| m3
#-----| -> m3
@@ -2693,7 +2693,7 @@ loops.rb:
#-----| -> puts
# 26| call to puts
#-----| -> exit do block (normal)
#-----| -> exit do ... end (normal)
# 26| puts
#-----| -> x
@@ -3704,7 +3704,7 @@ raise.rb:
#-----| -> exit m13 (normal)
# 154| m14
#-----| -> exit top-level (normal)
#-----| -> exit AstNode (normal)
# 154| m14
#-----| -> m14
@@ -3728,10 +3728,10 @@ raise.rb:
#-----| -> element
# 155| ... if ...
#-----| -> exit block (normal)
#-----| -> exit { ... } (normal)
# 155| call to raise
#-----| raise -> exit block (abnormal)
#-----| raise -> exit { ... } (abnormal)
# 155| raise
#-----| ->
@@ -3750,7 +3750,7 @@ raise.rb:
#-----| -> call to nil?
break_ensure.rb:
# 1| exit top-level
# 1| exit AstNode
# 1| exit m1
@@ -3761,20 +3761,20 @@ break_ensure.rb:
# 44| exit m4
case.rb:
# 1| exit top-level
# 1| exit AstNode
# 1| exit if_in_case
cfg.rb:
# 1| exit top-level
# 1| exit AstNode
# 15| exit BEGIN block
# 15| exit AstNode
# 19| exit END block
# 19| exit AstNode
# 25| exit block
# 25| exit { ... }
# 29| exit block
# 29| exit { ... }
# 63| exit pattern
@@ -3782,7 +3782,7 @@ cfg.rb:
# 101| exit parameters
# 120| exit lambda
# 120| exit -> { ... }
# 142| exit print
@@ -3790,22 +3790,22 @@ cfg.rb:
# 153| exit two_parameters
# 191| exit block
# 191| exit { ... }
exit.rb:
# 1| exit top-level
# 1| exit AstNode
# 1| exit m1
# 8| exit m2
heredoc.rb:
# 1| exit top-level
# 1| exit AstNode
# 1| exit double_heredoc
ifs.rb:
# 1| exit top-level
# 1| exit AstNode
# 1| exit m1
@@ -3822,7 +3822,7 @@ ifs.rb:
# 40| exit constant_condition
loops.rb:
# 1| exit top-level
# 1| exit AstNode
# 1| exit m1
@@ -3830,10 +3830,10 @@ loops.rb:
# 24| exit m3
# 25| exit do block
# 25| exit do ... end
raise.rb:
# 1| exit top-level
# 1| exit AstNode
# 7| exit m1
@@ -3863,11 +3863,11 @@ raise.rb:
# 154| exit m14
# 155| exit block
# 155| exit { ... }
break_ensure.rb:
# 1| exit top-level (normal)
#-----| -> exit top-level
# 1| exit AstNode (normal)
#-----| -> exit AstNode
# 1| exit m1 (normal)
#-----| -> exit m1
@@ -3885,27 +3885,27 @@ break_ensure.rb:
#-----| -> exit m4
case.rb:
# 1| exit top-level (normal)
#-----| -> exit top-level
# 1| exit AstNode (normal)
#-----| -> exit AstNode
# 1| exit if_in_case (normal)
#-----| -> exit if_in_case
cfg.rb:
# 1| exit top-level (normal)
#-----| -> exit top-level
# 1| exit AstNode (normal)
#-----| -> exit AstNode
# 15| exit BEGIN block (normal)
#-----| -> exit BEGIN block
# 15| exit AstNode (normal)
#-----| -> exit AstNode
# 19| exit END block (normal)
#-----| -> exit END block
# 19| exit AstNode (normal)
#-----| -> exit AstNode
# 25| exit block (normal)
#-----| -> exit block
# 25| exit { ... } (normal)
#-----| -> exit { ... }
# 29| exit block (normal)
#-----| -> exit block
# 29| exit { ... } (normal)
#-----| -> exit { ... }
# 63| exit pattern (normal)
#-----| -> exit pattern
@@ -3916,8 +3916,8 @@ cfg.rb:
# 101| exit parameters (normal)
#-----| -> exit parameters
# 120| exit lambda (normal)
#-----| -> exit lambda
# 120| exit -> { ... } (normal)
#-----| -> exit -> { ... }
# 142| exit print (normal)
#-----| -> exit print
@@ -3928,12 +3928,12 @@ cfg.rb:
# 153| exit two_parameters (normal)
#-----| -> exit two_parameters
# 191| exit block (normal)
#-----| -> exit block
# 191| exit { ... } (normal)
#-----| -> exit { ... }
exit.rb:
# 1| exit top-level (normal)
#-----| -> exit top-level
# 1| exit AstNode (normal)
#-----| -> exit AstNode
# 1| exit m1 (abnormal)
#-----| -> exit m1
@@ -3948,15 +3948,15 @@ exit.rb:
#-----| -> exit m2
heredoc.rb:
# 1| exit top-level (normal)
#-----| -> exit top-level
# 1| exit AstNode (normal)
#-----| -> exit AstNode
# 1| exit double_heredoc (normal)
#-----| -> exit double_heredoc
ifs.rb:
# 1| exit top-level (normal)
#-----| -> exit top-level
# 1| exit AstNode (normal)
#-----| -> exit AstNode
# 1| exit m1 (normal)
#-----| -> exit m1
@@ -3980,8 +3980,8 @@ ifs.rb:
#-----| -> exit constant_condition
loops.rb:
# 1| exit top-level (normal)
#-----| -> exit top-level
# 1| exit AstNode (normal)
#-----| -> exit AstNode
# 1| exit m1 (normal)
#-----| -> exit m1
@@ -3992,12 +3992,12 @@ loops.rb:
# 24| exit m3 (normal)
#-----| -> exit m3
# 25| exit do block (normal)
#-----| -> exit do block
# 25| exit do ... end (normal)
#-----| -> exit do ... end
raise.rb:
# 1| exit top-level (normal)
#-----| -> exit top-level
# 1| exit AstNode (normal)
#-----| -> exit AstNode
# 7| exit m1 (abnormal)
#-----| -> exit m1
@@ -4065,8 +4065,8 @@ raise.rb:
# 154| exit m14 (normal)
#-----| -> exit m14
# 155| exit block (abnormal)
#-----| -> exit block
# 155| exit { ... } (abnormal)
#-----| -> exit { ... }
# 155| exit block (normal)
#-----| -> exit block
# 155| exit { ... } (normal)
#-----| -> exit { ... }

View File

@@ -25,6 +25,15 @@ parameterVariable
| parameters.rb:54:14:54:24 | y | parameters.rb:54:14:54:14 | y |
| scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x |
| scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b |
| ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x |
| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b |
| ssa.rb:49:9:49:20 | x | ssa.rb:49:9:49:9 | x |
| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a |
parameterNoVariable
| parameters.rb:45:22:45:22 | _ |
parameterVariableNoAccess
@@ -32,3 +41,4 @@ parameterVariableNoAccess
| nested_scopes.rb:18:26:18:26 | x | nested_scopes.rb:18:26:18:26 | x |
| scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x |
| scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x |
| ssa.rb:49:9:49:20 | x | ssa.rb:49:9:49:9 | x |

View File

@@ -0,0 +1,326 @@
definition
| nested_scopes.rb:5:3:5:7 | ... = ... | nested_scopes.rb:5:3:5:3 | a |
| nested_scopes.rb:7:5:7:9 | ... = ... | nested_scopes.rb:7:5:7:5 | a |
| nested_scopes.rb:9:7:9:11 | ... = ... | nested_scopes.rb:9:7:9:7 | a |
| nested_scopes.rb:11:9:11:13 | ... = ... | nested_scopes.rb:11:9:11:9 | a |
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:16:29:16:29 | a |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a |
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y |
| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block |
| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name |
| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size |
| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first |
| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle |
| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last |
| parameters.rb:35:1:38:3 | <uninitialized> | parameters.rb:35:16:35:16 | b |
| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a |
| parameters.rb:35:16:35:20 | ... = ... | parameters.rb:35:16:35:16 | b |
| parameters.rb:37:3:37:6 | phi | parameters.rb:35:16:35:16 | b |
| parameters.rb:40:1:43:3 | <uninitialized> | parameters.rb:40:15:40:15 | e |
| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d |
| parameters.rb:40:15:40:19 | ... = ... | parameters.rb:40:15:40:15 | e |
| parameters.rb:42:3:42:6 | phi | parameters.rb:40:15:40:15 | e |
| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ |
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b |
| parameters.rb:53:1:53:6 | ... = ... | parameters.rb:53:1:53:1 | x |
| parameters.rb:54:9:57:3 | <captured> | parameters.rb:53:1:53:1 | x |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y |
| parameters.rb:54:19:54:23 | ... = ... | parameters.rb:53:1:53:1 | x |
| parameters.rb:55:4:55:7 | phi | parameters.rb:53:1:53:1 | x |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a |
| scopes.rb:11:4:11:9 | ... += ... | scopes.rb:7:1:7:1 | a |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:7:1:7:1 | a |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:7:13:7 | b |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:11:13:11 | c |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:14:13:14 | d |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b |
| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i |
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i |
| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i |
| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i |
| ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x |
| ssa.rb:21:5:21:10 | ... -= ... | ssa.rb:18:8:18:8 | x |
| ssa.rb:25:1:30:3 | <uninitialized> | ssa.rb:26:7:26:10 | elem |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements |
| ssa.rb:26:3:28:5 | phi | ssa.rb:26:7:26:10 | elem |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x |
| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 |
| ssa.rb:44:1:47:3 | <uninitialized> | ssa.rb:45:3:45:3 | x |
| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b |
| ssa.rb:45:3:45:7 | ... = ... | ssa.rb:45:3:45:3 | x |
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x |
| ssa.rb:49:1:51:3 | <uninitialized> | ssa.rb:49:14:49:14 | y |
| ssa.rb:49:14:49:19 | ... = ... | ssa.rb:49:14:49:14 | y |
| ssa.rb:50:3:50:6 | phi | ssa.rb:49:14:49:14 | y |
| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo |
| ssa.rb:54:3:54:11 | ... = ... | ssa.rb:54:3:54:3 | x |
| ssa.rb:59:3:59:8 | ... = ... | ssa.rb:59:3:59:3 | x |
| ssa.rb:60:3:60:9 | ... += ... | ssa.rb:59:3:59:3 | x |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a |
| ssa.rb:65:3:65:15 | ... = ... | ssa.rb:65:3:65:10 | captured |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a |
| ssa.rb:69:5:69:17 | ... += ... | ssa.rb:65:3:65:10 | captured |
| ssa.rb:75:3:75:14 | ... = ... | ssa.rb:75:3:75:10 | captured |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:75:3:75:10 | captured |
| ssa.rb:82:3:82:14 | ... = ... | ssa.rb:82:3:82:10 | captured |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:82:3:82:10 | captured |
read
| nested_scopes.rb:5:3:5:7 | ... = ... | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:38:8:38:8 | a |
| nested_scopes.rb:7:5:7:9 | ... = ... | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:36:10:36:10 | a |
| nested_scopes.rb:9:7:9:11 | ... = ... | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:34:12:34:12 | a |
| nested_scopes.rb:11:9:11:13 | ... = ... | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:25:14:25:14 | a |
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:14:16:14:16 | a |
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:15:11:15:11 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a |
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y |
| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:9:25:9:30 | client |
| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block |
| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name |
| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name |
| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:27:8:27:11 | size |
| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | parameters.rb:31:11:31:15 | first |
| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:31:20:31:25 | middle |
| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | parameters.rb:31:30:31:33 | last |
| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:37:11:37:11 | a |
| parameters.rb:37:3:37:6 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:37:16:37:16 | b |
| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | parameters.rb:42:11:42:11 | d |
| parameters.rb:42:3:42:6 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:42:16:42:16 | e |
| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:46:8:46:8 | _ |
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y |
| parameters.rb:55:4:55:7 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a |
| scopes.rb:11:4:11:9 | ... += ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:11:13:11 | c | scopes.rb:16:9:16:9 | c |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:14:13:14 | d | scopes.rb:17:9:17:9 | d |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:5:6:5:6 | b |
| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:3:8:3:8 | i |
| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:4:8:4:8 | i |
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:15:8:15:8 | i |
| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:7:10:7:10 | i |
| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:8:10:8:10 | i |
| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:11:10:11:10 | i |
| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:12:10:12:10 | i |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:20:10:20:10 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 | ssa.rb:41:8:41:9 | m3 |
| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | ssa.rb:45:12:45:12 | b |
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:46:8:46:8 | x |
| ssa.rb:50:3:50:6 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:50:8:50:8 | y |
| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:54:7:54:9 | foo |
| ssa.rb:54:3:54:11 | ... = ... | ssa.rb:54:3:54:3 | x | ssa.rb:55:8:55:8 | x |
| ssa.rb:59:3:59:8 | ... = ... | ssa.rb:59:3:59:3 | x | ssa.rb:60:3:60:3 | x |
| ssa.rb:60:3:60:9 | ... += ... | ssa.rb:59:3:59:3 | x | ssa.rb:61:8:61:8 | x |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured | ssa.rb:69:5:69:12 | captured |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
firstRead
| nested_scopes.rb:5:3:5:7 | ... = ... | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:38:8:38:8 | a |
| nested_scopes.rb:7:5:7:9 | ... = ... | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:36:10:36:10 | a |
| nested_scopes.rb:9:7:9:11 | ... = ... | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:34:12:34:12 | a |
| nested_scopes.rb:11:9:11:13 | ... = ... | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:25:14:25:14 | a |
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:14:16:14:16 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a |
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y |
| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:9:25:9:30 | client |
| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block |
| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name |
| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name |
| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:27:8:27:11 | size |
| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | parameters.rb:31:11:31:15 | first |
| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:31:20:31:25 | middle |
| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | parameters.rb:31:30:31:33 | last |
| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:37:11:37:11 | a |
| parameters.rb:37:3:37:6 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:37:16:37:16 | b |
| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | parameters.rb:42:11:42:11 | d |
| parameters.rb:42:3:42:6 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:42:16:42:16 | e |
| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:46:8:46:8 | _ |
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y |
| parameters.rb:55:4:55:7 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
| scopes.rb:11:4:11:9 | ... += ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:11:13:11 | c | scopes.rb:16:9:16:9 | c |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:14:13:14 | d | scopes.rb:17:9:17:9 | d |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:5:6:5:6 | b |
| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:3:8:3:8 | i |
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:15:8:15:8 | i |
| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:7:10:7:10 | i |
| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:11:10:11:10 | i |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 | ssa.rb:41:8:41:9 | m3 |
| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | ssa.rb:45:12:45:12 | b |
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:46:8:46:8 | x |
| ssa.rb:50:3:50:6 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:50:8:50:8 | y |
| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:54:7:54:9 | foo |
| ssa.rb:54:3:54:11 | ... = ... | ssa.rb:54:3:54:3 | x | ssa.rb:55:8:55:8 | x |
| ssa.rb:59:3:59:8 | ... = ... | ssa.rb:59:3:59:3 | x | ssa.rb:60:3:60:3 | x |
| ssa.rb:60:3:60:9 | ... += ... | ssa.rb:59:3:59:3 | x | ssa.rb:61:8:61:8 | x |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
lastRead
| nested_scopes.rb:5:3:5:7 | ... = ... | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:38:8:38:8 | a |
| nested_scopes.rb:7:5:7:9 | ... = ... | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:36:10:36:10 | a |
| nested_scopes.rb:9:7:9:11 | ... = ... | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:34:12:34:12 | a |
| nested_scopes.rb:11:9:11:13 | ... = ... | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:25:14:25:14 | a |
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:15:11:15:11 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a |
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y |
| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:9:25:9:30 | client |
| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block |
| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name |
| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:27:8:27:11 | size |
| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | parameters.rb:31:11:31:15 | first |
| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:31:20:31:25 | middle |
| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | parameters.rb:31:30:31:33 | last |
| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:37:11:37:11 | a |
| parameters.rb:37:3:37:6 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:37:16:37:16 | b |
| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | parameters.rb:42:11:42:11 | d |
| parameters.rb:42:3:42:6 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:42:16:42:16 | e |
| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:46:8:46:8 | _ |
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y |
| parameters.rb:55:4:55:7 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a |
| scopes.rb:11:4:11:9 | ... += ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:11:13:11 | c | scopes.rb:16:9:16:9 | c |
| scopes.rb:13:4:13:32 | ... = ... | scopes.rb:13:14:13:14 | d | scopes.rb:17:9:17:9 | d |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:5:6:5:6 | b |
| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:4:8:4:8 | i |
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:15:8:15:8 | i |
| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:8:10:8:10 | i |
| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:12:10:12:10 | i |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 | ssa.rb:41:8:41:9 | m3 |
| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | ssa.rb:45:12:45:12 | b |
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:46:8:46:8 | x |
| ssa.rb:50:3:50:6 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:50:8:50:8 | y |
| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:54:7:54:9 | foo |
| ssa.rb:54:3:54:11 | ... = ... | ssa.rb:54:3:54:3 | x | ssa.rb:55:8:55:8 | x |
| ssa.rb:59:3:59:8 | ... = ... | ssa.rb:59:3:59:3 | x | ssa.rb:60:3:60:3 | x |
| ssa.rb:60:3:60:9 | ... += ... | ssa.rb:59:3:59:3 | x | ssa.rb:61:8:61:8 | x |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured | ssa.rb:69:5:69:12 | captured |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
adjacentReads
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:14:16:14:16 | a | nested_scopes.rb:15:11:15:11 | a |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | parameters.rb:11:14:11:19 | pizzas |
| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name | parameters.rb:26:8:26:11 | name |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | scopes.rb:11:4:11:4 | a |
| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:3:8:3:8 | i | ssa.rb:4:8:4:8 | i |
| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:7:10:7:10 | i | ssa.rb:8:10:8:10 | i |
| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:11:10:11:10 | i | ssa.rb:12:10:12:10 | i |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x | ssa.rb:20:10:20:10 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:20:10:20:10 | x | ssa.rb:21:5:21:5 | x |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured | ssa.rb:69:5:69:12 | captured |
phi
| parameters.rb:37:3:37:6 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:35:1:38:3 | <uninitialized> |
| parameters.rb:37:3:37:6 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:35:16:35:20 | ... = ... |
| parameters.rb:42:3:42:6 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:40:1:43:3 | <uninitialized> |
| parameters.rb:42:3:42:6 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:40:15:40:19 | ... = ... |
| parameters.rb:55:4:55:7 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:54:9:57:3 | <captured> |
| parameters.rb:55:4:55:7 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:54:19:54:23 | ... = ... |
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:6:5:6:9 | ... = ... |
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:10:5:10:9 | ... = ... |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:10 | ... -= ... |
| ssa.rb:26:3:28:5 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | <uninitialized> |
| ssa.rb:26:3:28:5 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | <uninitialized> |
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:7 | ... = ... |
| ssa.rb:50:3:50:6 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | <uninitialized> |
| ssa.rb:50:3:50:6 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:19 | ... = ... |

View File

@@ -0,0 +1,27 @@
import codeql_ruby.AST
import codeql_ruby.CFG
import codeql_ruby.ast.Variable
import codeql_ruby.dataflow.SSA
query predicate definition(Ssa::Definition def, Variable v) { def.getSourceVariable() = v }
query predicate read(Ssa::Definition def, Variable v, CfgNode read) {
def.getSourceVariable() = v and read = def.getARead()
}
query predicate firstRead(Ssa::Definition def, Variable v, CfgNode read) {
def.getSourceVariable() = v and read = def.getAFirstRead()
}
query predicate lastRead(Ssa::Definition def, Variable v, CfgNode read) {
def.getSourceVariable() = v and read = def.getALastRead()
}
query predicate adjacentReads(Ssa::Definition def, Variable v, CfgNode read1, CfgNode read2) {
def.getSourceVariable() = v and
def.hasAdjacentReads(read1, read2)
}
query predicate phi(Ssa::PhiNode phi, Variable v, Ssa::Definition input) {
phi.getSourceVariable() = v and input = phi.getAnInput()
}

View File

@@ -0,0 +1,88 @@
def m b # defines b_0
i = 0 # defines i_0
puts i # reads i_0 (first read)
puts i + 1 # reads i_0 (last read)
if b # reads b_0
i = 1 # defines i_1
puts i # reads i_1 (first read)
puts i + 1 # reads i_1 (last read)
else
i = 2 # defines i_2
puts i # reads i_2 (first read)
puts i + 1 # reads i_2 (last read)
end
# defines i_3 = phi(i_1, i_2)
puts i # reads i3 (first read and last read)
end
def m1 x
while x >= 0
puts x
x -= 1
end
end
def m2 elements
for elem in elements do
puts elem
end
puts elem
end
def m3
[1,2,3].each do |x|
puts x
end
end
def m4
puts m3
m3 = 10
puts m3 + 1
end
def m5 b
x = 0 if b
puts x
end
def m6 (x = (y = 10))
puts y
end
def m7 foo
x = foo.x
puts x
end
def m8
x = 10
x += 10
puts x
end
def m9 a
captured = 10
a.times do |a|
puts a
puts captured
captured += 1
end
puts captured
end
def m10
captured = 0
foo do
bar(baz: captured)
end
end
def m11
captured = 0
foo do
bar do
puts captured
end
end
end

View File

@@ -94,6 +94,57 @@ variableAccess
| scopes.rb:21:1:21:7 | $global | file://:0:0:0:0 | $global | file://:0:0:0:0 | global scope |
| scopes.rb:24:1:24:6 | script | scopes.rb:24:1:24:6 | script | scopes.rb:1:1:24:12 | top-level scope |
| scopes.rb:24:10:24:11 | $0 | file://:0:0:0:0 | $0 | file://:0:0:0:0 | global scope |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:3:8:3:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:4:8:4:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:5:6:5:6 | b | ssa.rb:1:7:1:7 | b | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:6:5:6:5 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:7:10:7:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:8:10:8:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:10:5:10:5 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:11:10:11:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:12:10:12:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:15:8:15:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope |
| ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | method scope |
| ssa.rb:19:9:19:9 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | method scope |
| ssa.rb:20:10:20:10 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | method scope |
| ssa.rb:21:5:21:5 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | method scope |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | method scope |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | method scope |
| ssa.rb:26:15:26:22 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | method scope |
| ssa.rb:27:10:27:13 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | method scope |
| ssa.rb:29:8:29:11 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | method scope |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:33:16:35:5 | block scope |
| ssa.rb:34:10:34:10 | x | ssa.rb:33:20:33:20 | x | ssa.rb:33:16:35:5 | block scope |
| ssa.rb:40:3:40:4 | m3 | ssa.rb:40:3:40:4 | m3 | ssa.rb:38:1:42:3 | method scope |
| ssa.rb:41:8:41:9 | m3 | ssa.rb:40:3:40:4 | m3 | ssa.rb:38:1:42:3 | method scope |
| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | ssa.rb:44:1:47:3 | method scope |
| ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | method scope |
| ssa.rb:45:12:45:12 | b | ssa.rb:44:8:44:8 | b | ssa.rb:44:1:47:3 | method scope |
| ssa.rb:46:8:46:8 | x | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | method scope |
| ssa.rb:49:9:49:9 | x | ssa.rb:49:9:49:9 | x | ssa.rb:49:1:51:3 | method scope |
| ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | method scope |
| ssa.rb:50:8:50:8 | y | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | method scope |
| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:53:1:56:3 | method scope |
| ssa.rb:54:3:54:3 | x | ssa.rb:54:3:54:3 | x | ssa.rb:53:1:56:3 | method scope |
| ssa.rb:54:7:54:9 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:53:1:56:3 | method scope |
| ssa.rb:55:8:55:8 | x | ssa.rb:54:3:54:3 | x | ssa.rb:53:1:56:3 | method scope |
| ssa.rb:59:3:59:3 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | method scope |
| ssa.rb:60:3:60:3 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | method scope |
| ssa.rb:61:8:61:8 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | method scope |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:64:1:72:3 | method scope |
| ssa.rb:65:3:65:10 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | method scope |
| ssa.rb:66:3:66:3 | a | ssa.rb:64:8:64:8 | a | ssa.rb:64:1:72:3 | method scope |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:66:11:70:5 | block scope |
| ssa.rb:67:10:67:10 | a | ssa.rb:66:15:66:15 | a | ssa.rb:66:11:70:5 | block scope |
| ssa.rb:68:10:68:17 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | method scope |
| ssa.rb:69:5:69:12 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | method scope |
| ssa.rb:71:8:71:15 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | method scope |
| ssa.rb:75:3:75:10 | captured | ssa.rb:75:3:75:10 | captured | ssa.rb:74:1:79:3 | method scope |
| ssa.rb:77:15:77:22 | captured | ssa.rb:75:3:75:10 | captured | ssa.rb:74:1:79:3 | method scope |
| ssa.rb:82:3:82:10 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:81:1:88:3 | method scope |
| ssa.rb:85:15:85:22 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:81:1:88:3 | method scope |
explicitWrite
| nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:5:3:5:7 | ... = ... |
| nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:7:5:7:9 | ... = ... |
@@ -118,6 +169,20 @@ explicitWrite
| scopes.rb:13:14:13:14 | d | scopes.rb:13:4:13:32 | ... = ... |
| scopes.rb:21:1:21:7 | $global | scopes.rb:21:1:21:12 | ... = ... |
| scopes.rb:24:1:24:6 | script | scopes.rb:24:1:24:11 | ... = ... |
| ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:7 | ... = ... |
| ssa.rb:6:5:6:5 | i | ssa.rb:6:5:6:9 | ... = ... |
| ssa.rb:10:5:10:5 | i | ssa.rb:10:5:10:9 | ... = ... |
| ssa.rb:21:5:21:5 | x | ssa.rb:21:5:21:10 | ... -= ... |
| ssa.rb:40:3:40:4 | m3 | ssa.rb:40:3:40:9 | ... = ... |
| ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:7 | ... = ... |
| ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:19 | ... = ... |
| ssa.rb:54:3:54:3 | x | ssa.rb:54:3:54:11 | ... = ... |
| ssa.rb:59:3:59:3 | x | ssa.rb:59:3:59:8 | ... = ... |
| ssa.rb:60:3:60:3 | x | ssa.rb:60:3:60:9 | ... += ... |
| ssa.rb:65:3:65:10 | captured | ssa.rb:65:3:65:15 | ... = ... |
| ssa.rb:69:5:69:12 | captured | ssa.rb:69:5:69:17 | ... += ... |
| ssa.rb:75:3:75:10 | captured | ssa.rb:75:3:75:14 | ... = ... |
| ssa.rb:82:3:82:10 | captured | ssa.rb:82:3:82:14 | ... = ... |
implicitWrite
| nested_scopes.rb:15:23:15:23 | a |
| nested_scopes.rb:16:26:16:26 | x |
@@ -145,6 +210,16 @@ implicitWrite
| parameters.rb:54:14:54:14 | y |
| scopes.rb:2:14:2:14 | x |
| scopes.rb:9:14:9:14 | x |
| ssa.rb:1:7:1:7 | b |
| ssa.rb:18:8:18:8 | x |
| ssa.rb:25:8:25:15 | elements |
| ssa.rb:26:7:26:10 | elem |
| ssa.rb:33:20:33:20 | x |
| ssa.rb:44:8:44:8 | b |
| ssa.rb:49:9:49:9 | x |
| ssa.rb:53:8:53:10 | foo |
| ssa.rb:64:8:64:8 | a |
| ssa.rb:66:15:66:15 | a |
readAccess
| nested_scopes.rb:14:16:14:16 | a |
| nested_scopes.rb:15:11:15:11 | a |
@@ -193,3 +268,33 @@ readAccess
| scopes.rb:16:9:16:9 | c |
| scopes.rb:17:9:17:9 | d |
| scopes.rb:24:10:24:11 | $0 |
| ssa.rb:3:8:3:8 | i |
| ssa.rb:4:8:4:8 | i |
| ssa.rb:5:6:5:6 | b |
| ssa.rb:7:10:7:10 | i |
| ssa.rb:8:10:8:10 | i |
| ssa.rb:11:10:11:10 | i |
| ssa.rb:12:10:12:10 | i |
| ssa.rb:15:8:15:8 | i |
| ssa.rb:19:9:19:9 | x |
| ssa.rb:20:10:20:10 | x |
| ssa.rb:21:5:21:5 | x |
| ssa.rb:26:15:26:22 | elements |
| ssa.rb:27:10:27:13 | elem |
| ssa.rb:29:8:29:11 | elem |
| ssa.rb:34:10:34:10 | x |
| ssa.rb:41:8:41:9 | m3 |
| ssa.rb:45:12:45:12 | b |
| ssa.rb:46:8:46:8 | x |
| ssa.rb:50:8:50:8 | y |
| ssa.rb:54:7:54:9 | foo |
| ssa.rb:55:8:55:8 | x |
| ssa.rb:60:3:60:3 | x |
| ssa.rb:61:8:61:8 | x |
| ssa.rb:66:3:66:3 | a |
| ssa.rb:67:10:67:10 | a |
| ssa.rb:68:10:68:17 | captured |
| ssa.rb:69:5:69:12 | captured |
| ssa.rb:71:8:71:15 | captured |
| ssa.rb:77:15:77:22 | captured |
| ssa.rb:85:15:85:22 | captured |

View File

@@ -43,3 +43,22 @@
| scopes.rb:13:11:13:11 | c |
| scopes.rb:13:14:13:14 | d |
| scopes.rb:24:1:24:6 | script |
| ssa.rb:1:7:1:7 | b |
| ssa.rb:2:3:2:3 | i |
| ssa.rb:18:8:18:8 | x |
| ssa.rb:25:8:25:15 | elements |
| ssa.rb:26:7:26:10 | elem |
| ssa.rb:33:20:33:20 | x |
| ssa.rb:40:3:40:4 | m3 |
| ssa.rb:44:8:44:8 | b |
| ssa.rb:45:3:45:3 | x |
| ssa.rb:49:9:49:9 | x |
| ssa.rb:49:14:49:14 | y |
| ssa.rb:53:8:53:10 | foo |
| ssa.rb:54:3:54:3 | x |
| ssa.rb:59:3:59:3 | x |
| ssa.rb:64:8:64:8 | a |
| ssa.rb:65:3:65:10 | captured |
| ssa.rb:66:15:66:15 | a |
| ssa.rb:75:3:75:10 | captured |
| ssa.rb:82:3:82:10 | captured |

View File

@@ -29,3 +29,21 @@
| scopes.rb:1:1:24:12 | top-level scope |
| scopes.rb:2:9:6:3 | block scope |
| scopes.rb:9:9:18:3 | block scope |
| ssa.rb:1:1:16:3 | method scope |
| ssa.rb:1:1:88:3 | top-level scope |
| ssa.rb:18:1:23:3 | method scope |
| ssa.rb:25:1:30:3 | method scope |
| ssa.rb:32:1:36:3 | method scope |
| ssa.rb:33:16:35:5 | block scope |
| ssa.rb:38:1:42:3 | method scope |
| ssa.rb:44:1:47:3 | method scope |
| ssa.rb:49:1:51:3 | method scope |
| ssa.rb:53:1:56:3 | method scope |
| ssa.rb:58:1:62:3 | method scope |
| ssa.rb:64:1:72:3 | method scope |
| ssa.rb:66:11:70:5 | block scope |
| ssa.rb:74:1:79:3 | method scope |
| ssa.rb:76:7:78:5 | block scope |
| ssa.rb:81:1:88:3 | method scope |
| ssa.rb:83:7:87:5 | block scope |
| ssa.rb:84:10:86:8 | block scope |

View File

@@ -1 +1,6 @@
{}
{
"SSA": [
"codeql/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
"ql/src/codeql_ruby/dataflow/internal/SsaImplCommon.qll"
]
}