Skip to content

Commit ea0b2e9

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 9c41238 commit ea0b2e9

1 file changed

Lines changed: 59 additions & 4 deletions

File tree

shared/controlflow/codeql/controlflow/ControlFlowGraph.qll

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,31 @@ signature module AstSig<LocationSig Location> {
224224
*/
225225
default AstNode getTryElse(TryStmt try) { none() }
226226

227+
/**
228+
* Gets the `else` block of this `while` loop statement, if any.
229+
*
230+
* Only some languages (e.g. Python) support `while-else` constructs.
231+
*/
232+
default AstNode getWhileElse(WhileStmt loop) { none() }
233+
234+
/**
235+
* Gets the `else` block of this `foreach` loop statement, if any.
236+
*
237+
* Only some languages (e.g. Python) support `for-else` constructs.
238+
*/
239+
default AstNode getForeachElse(ForeachStmt loop) { none() }
240+
241+
/**
242+
* Gets the type expression of this catch clause, if any.
243+
*
244+
* In Python, the catch type is a runtime-evaluated expression
245+
* (e.g. `except SomeException:` where `SomeException` is an
246+
* arbitrary expression). For languages where the catch type is
247+
* statically resolved, this defaults to `none()` and no CFG node
248+
* is created.
249+
*/
250+
default Expr getCatchType(CatchClause catch) { none() }
251+
227252
/** A catch clause in a try statement. */
228253
class CatchClause extends AstNode {
229254
/** Gets the variable declared by this catch clause. */
@@ -1577,20 +1602,33 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
15771602
n1.isAfterValue(cond, any(BooleanSuccessor b | b.getValue() = while)) and
15781603
n2.isBefore(loopstmt.getBody())
15791604
or
1580-
n1.isAfterValue(cond, any(BooleanSuccessor b | b.getValue() = while.booleanNot())) and
1581-
n2.isAfter(loopstmt)
1605+
n1.isAfterFalse(cond) and
1606+
(
1607+
n2.isBefore(getWhileElse(loopstmt))
1608+
or
1609+
not exists(getWhileElse(loopstmt)) and n2.isAfter(loopstmt)
1610+
)
15821611
or
15831612
n1.isAfter(loopstmt.getBody()) and
15841613
n2.isAdditional(loopstmt, loopHeaderTag())
15851614
)
15861615
or
1616+
exists(WhileStmt whilestmt |
1617+
n1.isAfter(getWhileElse(whilestmt)) and
1618+
n2.isAfter(whilestmt)
1619+
)
1620+
or
15871621
exists(ForeachStmt foreachstmt |
15881622
n1.isBefore(foreachstmt) and
15891623
n2.isBefore(foreachstmt.getCollection())
15901624
or
15911625
n1.isAfterValue(foreachstmt.getCollection(),
15921626
any(EmptinessSuccessor t | t.getValue() = true)) and
1593-
n2.isAfter(foreachstmt)
1627+
(
1628+
n2.isBefore(getForeachElse(foreachstmt))
1629+
or
1630+
not exists(getForeachElse(foreachstmt)) and n2.isAfter(foreachstmt)
1631+
)
15941632
or
15951633
n1.isAfterValue(foreachstmt.getCollection(),
15961634
any(EmptinessSuccessor t | t.getValue() = false)) and
@@ -1603,10 +1641,17 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
16031641
n2.isAdditional(foreachstmt, loopHeaderTag())
16041642
or
16051643
n1.isAdditional(foreachstmt, loopHeaderTag()) and
1606-
n2.isAfter(foreachstmt)
1644+
(
1645+
n2.isBefore(getForeachElse(foreachstmt))
1646+
or
1647+
not exists(getForeachElse(foreachstmt)) and n2.isAfter(foreachstmt)
1648+
)
16071649
or
16081650
n1.isAdditional(foreachstmt, loopHeaderTag()) and
16091651
n2.isBefore(foreachstmt.getVariable())
1652+
or
1653+
n1.isAfter(getForeachElse(foreachstmt)) and
1654+
n2.isAfter(foreachstmt)
16101655
)
16111656
or
16121657
exists(ForStmt forstmt, PreControlFlowNode condentry |
@@ -1698,6 +1743,16 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
16981743
exists(CatchClause catchclause |
16991744
exists(MatchingSuccessor t |
17001745
n1.isBefore(catchclause) and
1746+
(
1747+
n2.isBefore(getCatchType(catchclause))
1748+
or
1749+
not exists(getCatchType(catchclause)) and n2.isAfterValue(catchclause, t)
1750+
) and
1751+
if Input1::catchAll(catchclause) then t.getValue() = true else any()
1752+
)
1753+
or
1754+
exists(MatchingSuccessor t |
1755+
n1.isAfter(getCatchType(catchclause)) and
17011756
n2.isAfterValue(catchclause, t) and
17021757
if Input1::catchAll(catchclause) then t.getValue() = true else any()
17031758
)

0 commit comments

Comments
 (0)