Skip to content

Commit 3284a3f

Browse files
authored
Merge pull request #157 from github/cfg_impl
Port CFG implementation to public AST interface
2 parents c761ab6 + cf7ce91 commit 3284a3f

23 files changed

Lines changed: 1340 additions & 1393 deletions

File tree

ql/src/codeql_ruby/ast/Control.qll

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ class ConditionalLoop extends Loop, TConditionalLoop {
440440
or
441441
pred = "getCondition" and result = this.getCondition()
442442
}
443+
444+
/** Holds if the loop body is entered when the condition is `condValue`. */
445+
predicate entersLoopWhenConditionIs(boolean condValue) { none() }
443446
}
444447

445448
/**
@@ -463,6 +466,12 @@ class WhileExpr extends ConditionalLoop, TWhileExpr {
463466

464467
final override Expr getCondition() { toGenerated(result) = g.getCondition() }
465468

469+
/**
470+
* Holds if the loop body is entered when the condition is `condValue`. For
471+
* `while` loops, this holds when `condValue` is true.
472+
*/
473+
final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = true }
474+
466475
final override string toString() { result = "while ..." }
467476
}
468477

@@ -487,6 +496,12 @@ class UntilExpr extends ConditionalLoop, TUntilExpr {
487496

488497
final override Expr getCondition() { toGenerated(result) = g.getCondition() }
489498

499+
/**
500+
* Holds if the loop body is entered when the condition is `condValue`. For
501+
* `until` loops, this holds when `condValue` is false.
502+
*/
503+
final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = false }
504+
490505
final override string toString() { result = "until ..." }
491506
}
492507

@@ -505,6 +520,12 @@ class WhileModifierExpr extends ConditionalLoop, TWhileModifierExpr {
505520

506521
final override Expr getCondition() { toGenerated(result) = g.getCondition() }
507522

523+
/**
524+
* Holds if the loop body is entered when the condition is `condValue`. For
525+
* `while`-modifier loops, this holds when `condValue` is true.
526+
*/
527+
final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = true }
528+
508529
final override string getAPrimaryQlClass() { result = "WhileModifierExpr" }
509530

510531
final override string toString() { result = "... while ..." }
@@ -525,6 +546,12 @@ class UntilModifierExpr extends ConditionalLoop, TUntilModifierExpr {
525546

526547
final override Expr getCondition() { toGenerated(result) = g.getCondition() }
527548

549+
/**
550+
* Holds if the loop body is entered when the condition is `condValue`. For
551+
* `until`-modifier loops, this holds when `condValue` is false.
552+
*/
553+
final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = false }
554+
528555
final override string getAPrimaryQlClass() { result = "UntilModifierExpr" }
529556

530557
final override string toString() { result = "... until ..." }

ql/src/codeql_ruby/ast/Expr.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ class StmtSequence extends Expr, TStmtSequence {
7373
/** Gets a statement in this sequence. */
7474
final Stmt getAStmt() { result = this.getStmt(_) }
7575

76-
/** Gets the last expression in this sequence, if any. */
77-
final Expr getLastExpr() { result = this.getStmt(this.getNumberOfStatements() - 1) }
76+
/** Gets the last statement in this sequence, if any. */
77+
final Stmt getLastStmt() { result = this.getStmt(this.getNumberOfStatements() - 1) }
7878

7979
/** Gets the number of statements in this sequence. */
8080
final int getNumberOfStatements() { result = count(this.getAStmt()) }

ql/src/codeql_ruby/ast/Method.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ private import internal.AST
44
private import internal.TreeSitter
55

66
/** A callable. */
7-
class Callable extends Expr, CfgScope, TCallable {
7+
class Callable extends Expr, TCallable {
88
/** Gets the number of parameters of this callable. */
99
final int getNumberOfParameters() { result = count(this.getAParameter()) }
1010

ql/src/codeql_ruby/ast/Module.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ class Toplevel extends ModuleBase, TToplevel {
4747
* Gets the `n`th `BEGIN` block.
4848
*/
4949
final BeginBlock getBeginBlock(int n) {
50-
toGenerated(result) = rank[n](int i, Generated::BeginBlock b | b = g.getChild(i) | b order by i)
50+
toGenerated(result) =
51+
rank[n + 1](int i, Generated::BeginBlock b | b = g.getChild(i) | b order by i)
5152
}
5253

5354
/**

ql/src/codeql_ruby/ast/Pattern.qll

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,14 @@ class TuplePattern extends Pattern, TTuplePattern {
6363
or
6464
result = toGenerated(this).(Generated::DestructuredLeftAssignment).getChild(i)
6565
or
66-
result = toGenerated(this).(Generated::LeftAssignmentList).getChild(i)
66+
toGenerated(this) =
67+
any(Generated::LeftAssignmentList lal |
68+
if
69+
strictcount(int j | exists(lal.getChild(j))) = 1 and
70+
lal.getChild(0) instanceof Generated::DestructuredLeftAssignment
71+
then result = lal.getChild(0).(Generated::DestructuredLeftAssignment).getChild(i)
72+
else result = lal.getChild(i)
73+
)
6774
}
6875

6976
/** Gets the `i`th pattern in this tuple pattern. */

ql/src/codeql_ruby/ast/Statement.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Stmt extends AstNode, TStmt {
1515
CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this }
1616

1717
/** Gets the control-flow scope of this statement, if any. */
18-
CfgScope getCfgScope() { result = getCfgScope(toGenerated(this)) }
18+
CfgScope getCfgScope() { result = getCfgScope(this) }
1919

2020
/** Gets the enclosing callable, if any. */
2121
Callable getEnclosingCallable() { result = this.getCfgScope() }

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ private module Cached {
9090
TComplexLiteral(Generated::Complex g) or
9191
TDefinedExpr(Generated::Unary g) { g instanceof @unary_definedquestion } or
9292
TDelimitedSymbolLiteral(Generated::DelimitedSymbol g) or
93-
TDestructuredLeftAssignment(Generated::DestructuredLeftAssignment g) or
93+
TDestructuredLeftAssignment(Generated::DestructuredLeftAssignment g) {
94+
not strictcount(int i | exists(g.getParent().(Generated::LeftAssignmentList).getChild(i))) = 1
95+
} or
9496
TDivExpr(Generated::Binary g) { g instanceof @binary_slash } or
9597
TDo(Generated::Do g) or
9698
TDoBlock(Generated::DoBlock g) { not g.getParent() instanceof Generated::Lambda } or

ql/src/codeql_ruby/controlflow/CfgNodes.qll

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/** Provides classes representing nodes in a control flow graph. */
22

33
private import codeql_ruby.AST
4-
private import codeql_ruby.ast.internal.AST
54
private import codeql_ruby.controlflow.BasicBlocks
65
private import ControlFlowGraph
76
private import internal.ControlFlowGraphImpl
@@ -67,7 +66,7 @@ class AstCfgNode extends CfgNode, TAstCfgNode {
6766
private Splits splits;
6867
private AstNode n;
6968

70-
AstCfgNode() { this = TAstCfgNode(toGenerated(n), splits) }
69+
AstCfgNode() { this = TAstCfgNode(n, splits) }
7170

7271
final override AstNode getNode() { result = n }
7372

@@ -132,7 +131,7 @@ abstract private class ExprChildMapping extends Expr {
132131

133132
pragma[noinline]
134133
private BasicBlock getABasicBlockInScope() {
135-
result.getANode() = TAstCfgNode(toGenerated(this.getAChildStar()), _)
134+
result.getANode() = TAstCfgNode(this.getAChildStar(), _)
136135
}
137136

138137
pragma[nomagic]
@@ -284,7 +283,7 @@ module ExprNodes {
284283
}
285284

286285
private class StmtSequenceChildMapping extends ExprChildMapping, StmtSequence {
287-
override predicate relevantChild(Expr e) { e = this.getLastExpr() }
286+
override predicate relevantChild(Expr e) { e = this.getLastStmt() }
288287
}
289288

290289
/** A control-flow node that wraps a `StmtSequence` AST expression. */
@@ -293,8 +292,8 @@ module ExprNodes {
293292

294293
final override StmtSequence getExpr() { result = ExprCfgNode.super.getExpr() }
295294

296-
/** Gets the last expression in this sequence, if any. */
297-
final ExprCfgNode getLastExpr() { e.hasCfgChild(e.getLastExpr(), this, result) }
295+
/** Gets the last statement in this sequence, if any. */
296+
final ExprCfgNode getLastStmt() { e.hasCfgChild(e.getLastStmt(), this, result) }
298297
}
299298

300299
private class ForExprChildMapping extends ExprChildMapping, ForExpr {

ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
/** Provides classes representing the control flow graph. */
22

33
private import codeql.Locations
4-
private import codeql_ruby.AST as AST
5-
private import codeql_ruby.ast.internal.AST as ASTInternal
4+
private import codeql_ruby.AST
65
private import codeql_ruby.controlflow.BasicBlocks
76
private import SuccessorTypes
87
private import internal.ControlFlowGraphImpl
98
private import internal.Splitting
109
private import internal.Completion
1110

1211
/** An AST node with an associated control-flow graph. */
13-
class CfgScope extends AST::AstNode {
14-
CfgScope() { ASTInternal::toGenerated(this) instanceof CfgScope::Range_ }
12+
class CfgScope extends Scope {
13+
CfgScope() { this instanceof CfgScope::Range_ }
1514

1615
/** Gets the CFG scope that this scope is nested under, if any. */
1716
final CfgScope getOuterCfgScope() {
18-
exists(AST::AstNode parent |
17+
exists(AstNode parent |
1918
parent = this.getParent() and
20-
result = getCfgScope(ASTInternal::toGenerated(parent))
19+
result = getCfgScope(parent)
2120
)
2221
}
2322
}
@@ -35,7 +34,7 @@ class CfgNode extends TCfgNode {
3534
string toString() { none() }
3635

3736
/** Gets the AST node that this node corresponds to, if any. */
38-
AST::AstNode getNode() { none() }
37+
AstNode getNode() { none() }
3938

4039
/** Gets the location of this control flow node. */
4140
Location getLocation() { none() }

ql/src/codeql_ruby/controlflow/internal/AstNodes.qll

Lines changed: 0 additions & 149 deletions
This file was deleted.

0 commit comments

Comments
 (0)