Skip to content

Commit 51e783f

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 51e783f

1 file changed

Lines changed: 30 additions & 3 deletions

File tree

shared/controlflow/codeql/controlflow/ControlFlowGraph.qll

Lines changed: 30 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,19 +1585,32 @@ 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())
15851596
)
15861597
or
1598+
exists(WhileStmt whilestmt |
1599+
n1.isAfter(getLoopElse(whilestmt)) and
1600+
n2.isAfter(whilestmt)
1601+
)
1602+
or
15871603
exists(ForeachStmt foreachstmt |
15881604
n1.isBefore(foreachstmt) and
15891605
n2.isBefore(foreachstmt.getCollection())
15901606
or
15911607
n1.isAfterValue(foreachstmt.getCollection(),
15921608
any(EmptinessSuccessor t | t.getValue() = true)) and
1593-
n2.isAfter(foreachstmt)
1609+
(
1610+
n2.isBefore(getLoopElse(foreachstmt))
1611+
or
1612+
not exists(getLoopElse(foreachstmt)) and n2.isAfter(foreachstmt)
1613+
)
15941614
or
15951615
n1.isAfterValue(foreachstmt.getCollection(),
15961616
any(EmptinessSuccessor t | t.getValue() = false)) and
@@ -1603,10 +1623,17 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
16031623
n2.isAdditional(foreachstmt, loopHeaderTag())
16041624
or
16051625
n1.isAdditional(foreachstmt, loopHeaderTag()) and
1606-
n2.isAfter(foreachstmt)
1626+
(
1627+
n2.isBefore(getLoopElse(foreachstmt))
1628+
or
1629+
not exists(getLoopElse(foreachstmt)) and n2.isAfter(foreachstmt)
1630+
)
16071631
or
16081632
n1.isAdditional(foreachstmt, loopHeaderTag()) and
16091633
n2.isBefore(foreachstmt.getVariable())
1634+
or
1635+
n1.isAfter(getLoopElse(foreachstmt)) and
1636+
n2.isAfter(foreachstmt)
16101637
)
16111638
or
16121639
exists(ForStmt forstmt, PreControlFlowNode condentry |

0 commit comments

Comments
 (0)