Merge pull request #108 from github/hvitved/ssa

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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