Skip to content

Commit 9e69738

Browse files
nischalCopilot
authored andcommitted
Shared CFG: add defaulted getLoopElse to AstSig
Adds a new defaulted signature predicates to the shared CFG library: - getLoopElse: `else` block of a loop statement, if any (used by Python's `while-else` / `for-else` constructs). The 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. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8179bff commit 9e69738

1 file changed

Lines changed: 25 additions & 3 deletions

File tree

shared/controlflow/codeql/controlflow/ControlFlowGraph.qll

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

227+
/**
228+
* Gets the `else` block of loop statement `loop`, if any.
229+
*
230+
* Only some languages (e.g. Python) support `for-else` constructs.
231+
*/
232+
default AstNode getLoopElse(LoopStmt loop) { none() }
233+
227234
/** A catch clause in a try statement. */
228235
class CatchClause extends AstNode {
229236
/** Gets the variable declared by this catch clause. */
@@ -1578,10 +1585,17 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
15781585
n2.isBefore(loopstmt.getBody())
15791586
or
15801587
n1.isAfterValue(cond, any(BooleanSuccessor b | b.getValue() = while.booleanNot())) and
1581-
n2.isAfter(loopstmt)
1588+
(
1589+
n2.isBefore(getLoopElse(loopstmt))
1590+
or
1591+
not exists(getLoopElse(loopstmt)) and n2.isAfter(loopstmt)
1592+
)
15821593
or
15831594
n1.isAfter(loopstmt.getBody()) and
15841595
n2.isAdditional(loopstmt, loopHeaderTag())
1596+
or
1597+
n1.isAfter(getLoopElse(loopstmt)) and
1598+
n2.isAfter(loopstmt)
15851599
)
15861600
or
15871601
exists(ForeachStmt foreachstmt |
@@ -1590,7 +1604,11 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
15901604
or
15911605
n1.isAfterValue(foreachstmt.getCollection(),
15921606
any(EmptinessSuccessor t | t.getValue() = true)) and
1593-
n2.isAfter(foreachstmt)
1607+
(
1608+
n2.isBefore(getLoopElse(foreachstmt))
1609+
or
1610+
not exists(getLoopElse(foreachstmt)) and n2.isAfter(foreachstmt)
1611+
)
15941612
or
15951613
n1.isAfterValue(foreachstmt.getCollection(),
15961614
any(EmptinessSuccessor t | t.getValue() = false)) and
@@ -1603,7 +1621,11 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
16031621
n2.isAdditional(foreachstmt, loopHeaderTag())
16041622
or
16051623
n1.isAdditional(foreachstmt, loopHeaderTag()) and
1606-
n2.isAfter(foreachstmt)
1624+
(
1625+
n2.isBefore(getLoopElse(foreachstmt))
1626+
or
1627+
not exists(getLoopElse(foreachstmt)) and n2.isAfter(foreachstmt)
1628+
)
16071629
or
16081630
n1.isAdditional(foreachstmt, loopHeaderTag()) and
16091631
n2.isBefore(foreachstmt.getVariable())

0 commit comments

Comments
 (0)