Skip to content

Commit 4d2296d

Browse files
nischalCopilot
authored andcommitted
Shared CFG: add defaulted getWhileElse/getForeachElse/getCatchType to AstSig
Adds three new defaulted signature predicates to the shared CFG library: - getWhileElse / getForeachElse: `else` block of a while/for loop, if any (used by Python's `while-else` / `for-else` constructs). - getCatchType: type expression of a catch clause, if any (used by Python's `except SomeExpr:` where the catch type is a runtime expression that needs CFG evaluation). Each predicate defaults to `none()`, so behaviour is unchanged for any language that doesn't override it (verified by re-running java/ql/test/library-tests/controlflow/). The Make0 succession rules are extended: - WhileStmt/ForeachStmt: route the loop-exit edge through the else block before reaching the after-position. - CatchClause: route the matching-evaluation through the type expression (if present) before reaching the after-value position. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent aaa3b36 commit 4d2296d

1 file changed

Lines changed: 58 additions & 3 deletions

File tree

shared/controlflow/codeql/controlflow/ControlFlowGraph.qll

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,31 @@ signature module AstSig<LocationSig Location> {
211211
*/
212212
default AstNode getTryElse(TryStmt try) { none() }
213213

214+
/**
215+
* Gets the `else` block of this `while` loop statement, if any.
216+
*
217+
* Only some languages (e.g. Python) support `while-else` constructs.
218+
*/
219+
default AstNode getWhileElse(WhileStmt loop) { none() }
220+
221+
/**
222+
* Gets the `else` block of this `foreach` loop statement, if any.
223+
*
224+
* Only some languages (e.g. Python) support `for-else` constructs.
225+
*/
226+
default AstNode getForeachElse(ForeachStmt loop) { none() }
227+
228+
/**
229+
* Gets the type expression of this catch clause, if any.
230+
*
231+
* In Python, the catch type is a runtime-evaluated expression
232+
* (e.g. `except SomeException:` where `SomeException` is an
233+
* arbitrary expression). For languages where the catch type is
234+
* statically resolved, this defaults to `none()` and no CFG node
235+
* is created.
236+
*/
237+
default Expr getCatchType(CatchClause catch) { none() }
238+
214239
/** A catch clause in a try statement. */
215240
class CatchClause extends AstNode {
216241
/** Gets the variable declared by this catch clause. */
@@ -1549,19 +1574,32 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
15491574
n2.isBefore(loopstmt.getBody())
15501575
or
15511576
n1.isAfterFalse(cond) and
1552-
n2.isAfter(loopstmt)
1577+
(
1578+
n2.isBefore(getWhileElse(loopstmt))
1579+
or
1580+
not exists(getWhileElse(loopstmt)) and n2.isAfter(loopstmt)
1581+
)
15531582
or
15541583
n1.isAfter(loopstmt.getBody()) and
15551584
n2.isAdditional(loopstmt, loopHeaderTag())
15561585
)
15571586
or
1587+
exists(WhileStmt whilestmt |
1588+
n1.isAfter(getWhileElse(whilestmt)) and
1589+
n2.isAfter(whilestmt)
1590+
)
1591+
or
15581592
exists(ForeachStmt foreachstmt |
15591593
n1.isBefore(foreachstmt) and
15601594
n2.isBefore(foreachstmt.getCollection())
15611595
or
15621596
n1.isAfterValue(foreachstmt.getCollection(),
15631597
any(EmptinessSuccessor t | t.getValue() = true)) and
1564-
n2.isAfter(foreachstmt)
1598+
(
1599+
n2.isBefore(getForeachElse(foreachstmt))
1600+
or
1601+
not exists(getForeachElse(foreachstmt)) and n2.isAfter(foreachstmt)
1602+
)
15651603
or
15661604
n1.isAfterValue(foreachstmt.getCollection(),
15671605
any(EmptinessSuccessor t | t.getValue() = false)) and
@@ -1574,10 +1612,17 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
15741612
n2.isAdditional(foreachstmt, loopHeaderTag())
15751613
or
15761614
n1.isAdditional(foreachstmt, loopHeaderTag()) and
1577-
n2.isAfter(foreachstmt)
1615+
(
1616+
n2.isBefore(getForeachElse(foreachstmt))
1617+
or
1618+
not exists(getForeachElse(foreachstmt)) and n2.isAfter(foreachstmt)
1619+
)
15781620
or
15791621
n1.isAdditional(foreachstmt, loopHeaderTag()) and
15801622
n2.isBefore(foreachstmt.getVariable())
1623+
or
1624+
n1.isAfter(getForeachElse(foreachstmt)) and
1625+
n2.isAfter(foreachstmt)
15811626
)
15821627
or
15831628
exists(ForStmt forstmt, PreControlFlowNode condentry |
@@ -1671,6 +1716,16 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
16711716
exists(CatchClause catchclause |
16721717
exists(MatchingSuccessor t |
16731718
n1.isBefore(catchclause) and
1719+
(
1720+
n2.isBefore(getCatchType(catchclause))
1721+
or
1722+
not exists(getCatchType(catchclause)) and n2.isAfterValue(catchclause, t)
1723+
) and
1724+
if Input1::catchAll(catchclause) then t.getValue() = true else any()
1725+
)
1726+
or
1727+
exists(MatchingSuccessor t |
1728+
n1.isAfter(getCatchType(catchclause)) and
16741729
n2.isAfterValue(catchclause, t) and
16751730
if Input1::catchAll(catchclause) then t.getValue() = true else any()
16761731
)

0 commit comments

Comments
 (0)