mirror of
https://github.com/github/codeql.git
synced 2026-04-25 08:45:14 +02:00
2
codeql
2
codeql
Submodule codeql updated: fc2fe6cccb...65ea01e145
22
ql/consistency-queries/SsaConsistency.ql
Normal file
22
ql/consistency-queries/SsaConsistency.ql
Normal 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()
|
||||
}
|
||||
@@ -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. */
|
||||
|
||||
5
ql/src/codeql_ruby/CFG.qll
Normal file
5
ql/src/codeql_ruby/CFG.qll
Normal file
@@ -0,0 +1,5 @@
|
||||
/** Provides classes representing the control flow graph. */
|
||||
|
||||
import controlflow.ControlFlowGraph
|
||||
import controlflow.CfgNodes as CfgNodes
|
||||
import controlflow.BasicBlocks
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -39,6 +39,7 @@ predicate implicitParameterAssignmentNode(Generated::AstNode n, Callable c) {
|
||||
|
||||
module Pattern {
|
||||
abstract class Range extends AstNode {
|
||||
cached
|
||||
Range() {
|
||||
explicitAssignmentNode(this, _)
|
||||
or
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
202
ql/src/codeql_ruby/controlflow/CfgNodes.qll
Normal file
202
ql/src/codeql_ruby/controlflow/CfgNodes.qll
Normal 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() }
|
||||
}
|
||||
}
|
||||
@@ -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. */
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @kind graph
|
||||
*/
|
||||
|
||||
import codeql_ruby.controlflow.ControlFlowGraph
|
||||
import codeql_ruby.CFG
|
||||
|
||||
query predicate nodes(CfgNode n) { any() }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
362
ql/src/codeql_ruby/dataflow/SSA.qll
Normal file
362
ql/src/codeql_ruby/dataflow/SSA.qll
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
272
ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll
Normal file
272
ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll
Normal 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
|
||||
590
ql/src/codeql_ruby/dataflow/internal/SsaImplCommon.qll
Normal file
590
ql/src/codeql_ruby/dataflow/internal/SsaImplCommon.qll
Normal 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) }
|
||||
}
|
||||
38
ql/src/codeql_ruby/dataflow/internal/SsaImplSpecific.qll
Normal file
38
ql/src/codeql_ruby/dataflow/internal/SsaImplSpecific.qll
Normal 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)
|
||||
}
|
||||
28
ql/src/queries/variables/DeadStoreOfLocal.ql
Normal file
28
ql/src/queries/variables/DeadStoreOfLocal.ql
Normal 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()
|
||||
32
ql/src/queries/variables/UninitializedLocal.ql
Normal file
32
ql/src/queries/variables/UninitializedLocal.ql
Normal 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()
|
||||
27
ql/src/queries/variables/UnusedParameter.ql
Normal file
27
ql/src/queries/variables/UnusedParameter.ql
Normal 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."
|
||||
@@ -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 { ... }
|
||||
|
||||
@@ -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 |
|
||||
|
||||
326
ql/test/library-tests/variables/ssa.expected
Normal file
326
ql/test/library-tests/variables/ssa.expected
Normal 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 | ... = ... |
|
||||
27
ql/test/library-tests/variables/ssa.ql
Normal file
27
ql/test/library-tests/variables/ssa.ql
Normal 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()
|
||||
}
|
||||
88
ql/test/library-tests/variables/ssa.rb
Normal file
88
ql/test/library-tests/variables/ssa.rb
Normal 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
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
{}
|
||||
{
|
||||
"SSA": [
|
||||
"codeql/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
|
||||
"ql/src/codeql_ruby/dataflow/internal/SsaImplCommon.qll"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user