Skip to content

Commit f8790c8

Browse files
authored
Merge pull request #108 from github/hvitved/ssa
Add SSA library
2 parents 623ee59 + 47fdee4 commit f8790c8

33 files changed

+2378
-241
lines changed

codeql

Submodule codeql updated 395 files
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import ruby
2+
import codeql_ruby.dataflow.SSA
3+
import codeql_ruby.controlflow.ControlFlowGraph
4+
5+
query predicate nonUniqueDef(CfgNode read, Ssa::Definition def) {
6+
read = def.getARead() and
7+
exists(Ssa::Definition other | read = other.getARead() and other != def)
8+
}
9+
10+
query predicate readWithoutDef(LocalVariableReadAccess read) {
11+
exists(CfgNode node |
12+
node = read.getAControlFlowNode() and
13+
not node = any(Ssa::Definition def).getARead()
14+
)
15+
}
16+
17+
query predicate deadDef(Ssa::Definition def, LocalVariable v) {
18+
v = def.getSourceVariable() and
19+
not v.isCaptured() and
20+
not exists(def.getARead()) and
21+
not def = any(Ssa::PhiNode phi).getAnInput()
22+
}

ql/src/codeql_ruby/AST.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class AstNode extends @ast_node {
2929
string getAPrimaryQlClass() { result = "???" }
3030

3131
/** Gets a textual representation of this node. */
32+
cached
3233
string toString() { result = "AstNode" }
3334

3435
/** Gets the location of this node. */

ql/src/codeql_ruby/CFG.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/** Provides classes representing the control flow graph. */
2+
3+
import controlflow.ControlFlowGraph
4+
import controlflow.CfgNodes as CfgNodes
5+
import controlflow.BasicBlocks

ql/src/codeql_ruby/ast/Expr.qll

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
private import codeql_ruby.AST
2+
private import codeql_ruby.CFG
23
private import internal.Expr
4+
private import internal.Variable
5+
private import codeql_ruby.controlflow.internal.ControlFlowGraphImpl
36

47
/**
58
* An expression.
@@ -10,6 +13,18 @@ class Expr extends AstNode {
1013
Expr::Range range;
1114

1215
Expr() { this = range }
16+
17+
/** Gets a control-flow node for this expression, if any. */
18+
CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this }
19+
20+
/** Gets the control-flow scope of this expression, if any. */
21+
CfgScope getCfgScope() { result = getCfgScope(this) }
22+
23+
/** Gets the variable scope that this expression belongs to. */
24+
VariableScope getVariableScope() { result = enclosingScope(this) }
25+
26+
/** Gets the enclosing callable, if any. */
27+
Callable getEnclosingCallable() { result = this.getCfgScope() }
1328
}
1429

1530
/**

ql/src/codeql_ruby/ast/Method.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
private import codeql_ruby.AST
2+
private import codeql_ruby.controlflow.ControlFlowGraph
23
private import internal.TreeSitter
34
private import internal.Method
45

56
/** A callable. */
6-
class Callable extends AstNode {
7+
class Callable extends AstNode, CfgScope {
78
Callable::Range range;
89

910
Callable() { range = this }

ql/src/codeql_ruby/ast/Parameter.qll

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ class Parameter extends AstNode {
2222
final int getPosition() { result = pos }
2323

2424
/** Gets a variable introduced by this parameter. */
25-
Variable getAVariable() { none() }
25+
LocalVariable getAVariable() { none() }
2626

2727
/** Gets the variable named `name` introduced by this parameter. */
28-
final Variable getVariable(string name) {
28+
final LocalVariable getVariable(string name) {
2929
result = this.getAVariable() and
3030
result.getName() = name
3131
}
@@ -37,7 +37,7 @@ class Parameter extends AstNode {
3737
* This includes both simple parameters and tuple parameters.
3838
*/
3939
class PatternParameter extends Parameter, Pattern {
40-
override Variable getAVariable() { result = Pattern.super.getAVariable() }
40+
override LocalVariable getAVariable() { result = Pattern.super.getAVariable() }
4141
}
4242

4343
/** A parameter defined using a tuple pattern. */
@@ -53,9 +53,9 @@ class NamedParameter extends Parameter {
5353
string getName() { none() }
5454

5555
/** Gets the variable introduced by this parameter. */
56-
Variable getVariable() { none() }
56+
LocalVariable getVariable() { none() }
5757

58-
override Variable getAVariable() { result = this.getVariable() }
58+
override LocalVariable getAVariable() { result = this.getVariable() }
5959

6060
/** Gets an access to this parameter. */
6161
final VariableAccess getAnAccess() { result = this.getVariable().getAnAccess() }
@@ -65,9 +65,9 @@ class NamedParameter extends Parameter {
6565
class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern {
6666
final override string getName() { result = range.getVariableName() }
6767

68-
final override Variable getVariable() { result = TLocalVariable(_, _, this) }
68+
final override LocalVariable getVariable() { result = TLocalVariable(_, _, this) }
6969

70-
final override Variable getAVariable() { result = this.getVariable() }
70+
final override LocalVariable getAVariable() { result = this.getVariable() }
7171

7272
final override string getAPrimaryQlClass() { result = "SimpleParameter" }
7373

@@ -85,7 +85,7 @@ class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern
8585
class BlockParameter extends @block_parameter, NamedParameter {
8686
final override Generated::BlockParameter generated;
8787

88-
final override Variable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
88+
final override LocalVariable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
8989

9090
final override string getAPrimaryQlClass() { result = "BlockParameter" }
9191

@@ -106,7 +106,7 @@ class BlockParameter extends @block_parameter, NamedParameter {
106106
class HashSplatParameter extends @hash_splat_parameter, NamedParameter {
107107
final override Generated::HashSplatParameter generated;
108108

109-
final override Variable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
109+
final override LocalVariable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
110110

111111
final override string getAPrimaryQlClass() { result = "HashSplatParameter" }
112112

@@ -129,7 +129,7 @@ class HashSplatParameter extends @hash_splat_parameter, NamedParameter {
129129
class KeywordParameter extends @keyword_parameter, NamedParameter {
130130
final override Generated::KeywordParameter generated;
131131

132-
final override Variable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
132+
final override LocalVariable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
133133

134134
final override string getAPrimaryQlClass() { result = "KeywordParameter" }
135135

@@ -164,7 +164,7 @@ class KeywordParameter extends @keyword_parameter, NamedParameter {
164164
class OptionalParameter extends @optional_parameter, NamedParameter {
165165
final override Generated::OptionalParameter generated;
166166

167-
final override Variable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
167+
final override LocalVariable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
168168

169169
final override string getAPrimaryQlClass() { result = "OptionalParameter" }
170170

@@ -191,7 +191,7 @@ class OptionalParameter extends @optional_parameter, NamedParameter {
191191
class SplatParameter extends @splat_parameter, NamedParameter {
192192
final override Generated::SplatParameter generated;
193193

194-
final override Variable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
194+
final override LocalVariable getVariable() { result = TLocalVariable(_, _, generated.getName()) }
195195

196196
final override string getAPrimaryQlClass() { result = "SplatParameter" }
197197

ql/src/codeql_ruby/ast/Variable.qll

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,25 @@ class LocalVariable extends Variable, TLocalVariable {
5757
override LocalVariable::Range range;
5858

5959
final override LocalVariableAccess getAnAccess() { result.getVariable() = this }
60+
61+
/** Gets the access where this local variable is first introduced. */
62+
VariableAccess getDefiningAccess() { result = range.getDefiningAccess() }
63+
64+
/**
65+
* Holds if this variable is captured. For example in
66+
*
67+
* ```rb
68+
* def m x
69+
* x.times do |y|
70+
* puts x
71+
* end
72+
* puts x
73+
* end
74+
* ```
75+
*
76+
* `x` is a captured variable, whereas `y` is not.
77+
*/
78+
predicate isCaptured() { this.getAnAccess().isCapturedAccess() }
6079
}
6180

6281
/** A global variable. */
@@ -133,6 +152,23 @@ class LocalVariableAccess extends VariableAccess, @token_identifier {
133152
final override string getAPrimaryQlClass() {
134153
not this instanceof SimpleParameter and result = "LocalVariableAccess"
135154
}
155+
156+
/**
157+
* Holds if this access is a captured variable access. For example in
158+
*
159+
* ```rb
160+
* def m x
161+
* x.times do |y|
162+
* puts x
163+
* end
164+
* puts x
165+
* end
166+
* ```
167+
*
168+
* the access to `x` in the first `puts x` is a captured access, while
169+
* the access to `x` in the second `puts x` is not.
170+
*/
171+
final predicate isCapturedAccess() { isCapturedAccess(this) }
136172
}
137173

138174
/** An access to a local variable where the value is updated. */

ql/src/codeql_ruby/ast/internal/Pattern.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ predicate implicitParameterAssignmentNode(Generated::AstNode n, Callable c) {
3939

4040
module Pattern {
4141
abstract class Range extends AstNode {
42+
cached
4243
Range() {
4344
explicitAssignmentNode(this, _)
4445
or

ql/src/codeql_ruby/ast/internal/Variable.qll

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@ private Generated::AstNode parent(Generated::AstNode n) {
99
not n = any(VariableScope s).getScopeElement()
1010
}
1111

12-
/** Gets the enclosing scope for `node`. */
13-
private VariableScope enclosingScope(Generated::AstNode node) {
14-
result.getScopeElement() = parent*(node.getParent())
15-
}
16-
1712
private predicate parameterAssignment(
1813
CallableScope::Range scope, string name, Generated::Identifier i
1914
) {
@@ -98,6 +93,12 @@ private class CapturingScope extends VariableScope {
9893

9994
cached
10095
private module Cached {
96+
/** Gets the enclosing scope for `node`. */
97+
cached
98+
VariableScope enclosingScope(Generated::AstNode node) {
99+
result.getScopeElement() = parent*(node.getParent())
100+
}
101+
101102
cached
102103
newtype TScope =
103104
TGlobalScope() or
@@ -301,6 +302,11 @@ private module Cached {
301302
or
302303
scopeDefinesParameterVariable(_, _, access)
303304
}
305+
306+
cached
307+
predicate isCapturedAccess(LocalVariableAccess::Range access) {
308+
access.getVariable().getDeclaringScope() != enclosingScope(access)
309+
}
304310
}
305311

306312
import Cached
@@ -391,6 +397,8 @@ module LocalVariable {
391397
final override Location getLocation() { result = i.getLocation() }
392398

393399
final override VariableScope getDeclaringScope() { result = scope }
400+
401+
final VariableAccess getDefiningAccess() { result = i }
394402
}
395403
}
396404

ql/src/codeql_ruby/controlflow/BasicBlocks.qll

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ private import codeql.Locations
44
private import codeql_ruby.ast.internal.TreeSitter::Generated
55
private import codeql_ruby.controlflow.ControlFlowGraph
66
private import internal.ControlFlowGraphImpl
7+
private import CfgNodes
78
private import SuccessorTypes
89

910
/**
@@ -286,9 +287,7 @@ private module Cached {
286287
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
287288

288289
/** Holds if `bb` is an exit basic block that represents normal exit. */
289-
private predicate normalExitBB(BasicBlock bb) {
290-
bb.getANode().(CfgNodes::AnnotatedExitNode).isNormal()
291-
}
290+
private predicate normalExitBB(BasicBlock bb) { bb.getANode().(AnnotatedExitNode).isNormal() }
292291

293292
/** Holds if `dom` is an immediate post-dominator of `bb`. */
294293
cached
@@ -313,7 +312,7 @@ private module Cached {
313312
private import Cached
314313

315314
/** Holds if `bb` is an entry basic block. */
316-
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof CfgNodes::EntryNode }
315+
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryNode }
317316

318317
/**
319318
* An entry basic block, that is, a basic block whose first node is
@@ -330,20 +329,28 @@ class EntryBasicBlock extends BasicBlock {
330329
* an annotated exit node.
331330
*/
332331
class AnnotatedExitBasicBlock extends BasicBlock {
333-
AnnotatedExitBasicBlock() { this.getANode() instanceof CfgNodes::AnnotatedExitNode }
332+
private boolean normal;
333+
334+
AnnotatedExitBasicBlock() {
335+
exists(AnnotatedExitNode n |
336+
n = this.getANode() and
337+
if n.isNormal() then normal = true else normal = false
338+
)
339+
}
340+
341+
/** Holds if this block represent a normal exit. */
342+
final predicate isNormal() { normal = true }
334343
}
335344

336345
/**
337346
* An exit basic block, that is, a basic block whose last node is
338347
* an exit node.
339348
*/
340349
class ExitBasicBlock extends BasicBlock {
341-
ExitBasicBlock() { this.getLastNode() instanceof CfgNodes::ExitNode }
350+
ExitBasicBlock() { this.getLastNode() instanceof ExitNode }
342351
}
343352

344353
private module JoinBlockPredecessors {
345-
private import CfgNodes
346-
347354
private predicate id(AstNode x, AstNode y) { x = y }
348355

349356
private predicate idOf(AstNode x, int y) = equivalenceRelation(id/2)(x, y)

0 commit comments

Comments
 (0)