Skip to content

Commit d558371

Browse files
nischalCopilot
authored andcommitted
Shared CFG: add defaulted getWhileElse/getForeachElse to AstSig
Lifts the shared-controlflow changes out of the larger Python new-CFG PR so they can be reviewed independently. No behavioural change for existing languages (Java, C#, Ruby, Swift, Go, ...) — both new predicates default to `none()` and the Make0 loop-edge case extensions only fire when a language overrides them. Python's upcoming AstNodeImpl wiring uses these predicates to model `while-else` and `for-else`, where the `else` block runs when the loop condition becomes false (rather than via a `break`). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent aaa3b36 commit d558371

2 files changed

Lines changed: 41 additions & 3 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: feature
3+
---
4+
* The `AstSig` signature gains two new defaulted predicates `getWhileElse` and `getForeachElse`, allowing languages (like Python) to model `while-else` / `for-else` constructs where the `else` branch is taken when the loop condition becomes false (rather than via a `break`). Existing languages that do not provide these predicates retain the previous behaviour.

shared/controlflow/codeql/controlflow/ControlFlowGraph.qll

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,20 @@ 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+
214228
/** A catch clause in a try statement. */
215229
class CatchClause extends AstNode {
216230
/** Gets the variable declared by this catch clause. */
@@ -1549,19 +1563,32 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
15491563
n2.isBefore(loopstmt.getBody())
15501564
or
15511565
n1.isAfterFalse(cond) and
1552-
n2.isAfter(loopstmt)
1566+
(
1567+
n2.isBefore(getWhileElse(loopstmt))
1568+
or
1569+
not exists(getWhileElse(loopstmt)) and n2.isAfter(loopstmt)
1570+
)
15531571
or
15541572
n1.isAfter(loopstmt.getBody()) and
15551573
n2.isAdditional(loopstmt, loopHeaderTag())
15561574
)
15571575
or
1576+
exists(WhileStmt whilestmt |
1577+
n1.isAfter(getWhileElse(whilestmt)) and
1578+
n2.isAfter(whilestmt)
1579+
)
1580+
or
15581581
exists(ForeachStmt foreachstmt |
15591582
n1.isBefore(foreachstmt) and
15601583
n2.isBefore(foreachstmt.getCollection())
15611584
or
15621585
n1.isAfterValue(foreachstmt.getCollection(),
15631586
any(EmptinessSuccessor t | t.getValue() = true)) and
1564-
n2.isAfter(foreachstmt)
1587+
(
1588+
n2.isBefore(getForeachElse(foreachstmt))
1589+
or
1590+
not exists(getForeachElse(foreachstmt)) and n2.isAfter(foreachstmt)
1591+
)
15651592
or
15661593
n1.isAfterValue(foreachstmt.getCollection(),
15671594
any(EmptinessSuccessor t | t.getValue() = false)) and
@@ -1574,10 +1601,17 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
15741601
n2.isAdditional(foreachstmt, loopHeaderTag())
15751602
or
15761603
n1.isAdditional(foreachstmt, loopHeaderTag()) and
1577-
n2.isAfter(foreachstmt)
1604+
(
1605+
n2.isBefore(getForeachElse(foreachstmt))
1606+
or
1607+
not exists(getForeachElse(foreachstmt)) and n2.isAfter(foreachstmt)
1608+
)
15781609
or
15791610
n1.isAdditional(foreachstmt, loopHeaderTag()) and
15801611
n2.isBefore(foreachstmt.getVariable())
1612+
or
1613+
n1.isAfter(getForeachElse(foreachstmt)) and
1614+
n2.isAfter(foreachstmt)
15811615
)
15821616
or
15831617
exists(ForStmt forstmt, PreControlFlowNode condentry |

0 commit comments

Comments
 (0)