Skip to content

Commit d61614d

Browse files
authored
Merge pull request #22815 from aschackmull/unified/cfg-updates
Unified: Add CFG support for loop guards and defer
2 parents f94db6b + 6b5fb7a commit d61614d

5 files changed

Lines changed: 634 additions & 510 deletions

File tree

‎shared/controlflow/codeql/controlflow/ControlFlowGraph.qll‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ signature module AstSig<LocationSig Location> {
161161
Expr getCollection();
162162
}
163163

164+
/** Gets the loop guard of the given `loop`, if any. */
165+
default AstNode getLoopGuard(ForEachStmt loop) { none() }
166+
164167
/**
165168
* A `break` statement.
166169
*
@@ -656,6 +659,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
656659
any(DoStmt dostmt).getCondition() = n or
657660
any(UntilStmt untilstmt).getCondition() = n or
658661
any(ForStmt forstmt).getCondition() = n or
662+
getLoopGuard(_) = n or
659663
any(ConditionalExpr condexpr).getCondition() = n or
660664
any(CatchClause catch).getCondition() = n or
661665
any(Case case).getGuard() = n
@@ -1701,8 +1705,19 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
17011705
n2.isBefore(foreachstmt.getVariable())
17021706
or
17031707
n1.isAfter(foreachstmt.getVariable()) and
1708+
(
1709+
n2.isBefore(getLoopGuard(foreachstmt))
1710+
or
1711+
not exists(getLoopGuard(foreachstmt)) and
1712+
n2.isBefore(foreachstmt.getBody())
1713+
)
1714+
or
1715+
n1.isAfterTrue(getLoopGuard(foreachstmt)) and
17041716
n2.isBefore(foreachstmt.getBody())
17051717
or
1718+
n1.isAfterFalse(getLoopGuard(foreachstmt)) and
1719+
n2.isAdditional(foreachstmt, loopHeaderTag())
1720+
or
17061721
n1.isAfter(foreachstmt.getBody()) and
17071722
n2.isAdditional(foreachstmt, loopHeaderTag())
17081723
or

‎unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll‎

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,17 @@ module Ast implements AstSig<Location> {
3535
not e.fromSource()
3636
}
3737

38+
private AstNode adjustedGetChild(AstNode n, int index) { deferScope(n, index, result) }
39+
3840
AstNode getChild(AstNode n, int index) {
3941
result.getParent() = n and
4042
result.getParentIndex() = index and
4143
not n instanceof Callable and
4244
not skipControlFlow(n) and
43-
not skipControlFlow(result)
45+
not skipControlFlow(result) and
46+
not adjustedGetChild(_, _) = result
47+
or
48+
adjustedGetChild(n, index) = result
4449
or
4550
n.(FunctionExpr).getCaptureDeclaration(index) = result
4651
or
@@ -76,7 +81,56 @@ module Ast implements AstSig<Location> {
7681

7782
class Expr = U::Expr;
7883

79-
class BlockStmt = U::Block;
84+
/**
85+
* Holds if `s` is the `n`th statement in the constructed `try` block of
86+
* `defer`.
87+
*
88+
* For example, the following `defer` statements
89+
* ```
90+
* {
91+
* s1
92+
* defer { s5 }
93+
* s2
94+
* defer { s4 }
95+
* s3
96+
* }
97+
* ```
98+
* are transformed into these `try`-`finally` blocks:
99+
* ```
100+
* {
101+
* s1
102+
* try {
103+
* s2
104+
* try {
105+
* s3
106+
* } finally {
107+
* s4
108+
* }
109+
* } finally {
110+
* s5
111+
* }
112+
* }
113+
* ```
114+
*/
115+
private predicate deferScope(U::DeferStmt defer, int n, Stmt s) {
116+
exists(U::Block block, int deferIdx | block.getStmt(deferIdx) = defer |
117+
block.getStmt(deferIdx + 1) = s and n = 0
118+
or
119+
exists(Stmt mid |
120+
deferScope(defer, n - 1, mid) and
121+
not mid instanceof U::DeferStmt and
122+
block.getStmt(deferIdx + 1 + n) = s
123+
)
124+
)
125+
}
126+
127+
class BlockStmt extends Stmt instanceof U::Block {
128+
Stmt getStmt(int n) { result = super.getStmt(n) and not deferScope(_, _, result) }
129+
130+
Stmt getLastStmt() {
131+
exists(int i | result = this.getStmt(i) and not exists(this.getStmt(i + 1)))
132+
}
133+
}
80134

81135
class ExprStmt extends Stmt {
82136
ExprStmt() { none() }
@@ -127,13 +181,13 @@ module Ast implements AstSig<Location> {
127181
class ForEachStmt extends LoopStmt instanceof U::ForEachStmt {
128182
override Stmt getBody() { result = U::ForEachStmt.super.getBody() }
129183

130-
// TODO support foreach guard
131-
//
132184
Expr getVariable() { result = super.getPattern() }
133185

134186
Expr getCollection() { result = super.getIterable() }
135187
}
136188

189+
AstNode getLoopGuard(ForEachStmt foreachstmt) { result = foreachstmt.(U::ForEachStmt).getGuard() }
190+
137191
class BreakStmt = U::BreakExpr;
138192

139193
class ContinueStmt = U::ContinueExpr;
@@ -152,12 +206,18 @@ module Ast implements AstSig<Location> {
152206
Expr getExpr() { result = super.getValue() }
153207
}
154208

155-
class TryStmt extends U::TryExpr {
156-
AstNode getBody(int index) { index = 0 and result = super.getBody() }
209+
class TryStmt extends Stmt {
210+
TryStmt() { this instanceof U::TryExpr or this instanceof U::DeferStmt }
211+
212+
AstNode getBody(int index) {
213+
index = 0 and result = this.(U::TryExpr).getBody()
214+
or
215+
deferScope(this, index, result)
216+
}
157217

158-
CatchClause getCatch(int index) { result = super.getCatchClause(index) }
218+
CatchClause getCatch(int index) { result = this.(U::TryExpr).getCatchClause(index) }
159219

160-
Stmt getFinally() { none() }
220+
Stmt getFinally() { result = this.(U::DeferStmt).getBody() }
161221
}
162222

163223
class CatchClause extends U::CatchClause {

0 commit comments

Comments
 (0)