Shared CFG: add defaulted getWhileElse/getForeachElse to AstSig#21931
Open
yoff wants to merge 1 commit into
Open
Shared CFG: add defaulted getWhileElse/getForeachElse to AstSig#21931yoff wants to merge 1 commit into
yoff wants to merge 1 commit into
Conversation
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>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds shared-CFG signature hooks for loop else blocks so languages that support while-else / for-else (notably Python) can expose that structure to the shared control-flow graph without impacting existing languages.
Changes:
- Added defaulted
AstSig.getWhileElse(WhileStmt)andAstSig.getForeachElse(ForeachStmt)predicates (defaultnone()). - Updated shared CFG construction (
Make0) to route certain loop-exit edges through anelseblock when provided by a language implementation. - Added a shared/controlflow change note documenting the new signature predicates.
Show a summary per file
| File | Description |
|---|---|
| shared/controlflow/codeql/controlflow/ControlFlowGraph.qll | Extends the shared CFG AST signature with defaulted loop-else accessors and threads them into loop edge construction. |
| shared/controlflow/change-notes/2026-05-19-loop-else.md | Documents the new defaulted AstSig predicates for loop else blocks. |
Copilot's findings
- Files reviewed: 2/2 changed files
- Comments generated: 1
| --- | ||
| category: feature | ||
| --- | ||
| * 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. |
owen-mc
reviewed
Jun 2, 2026
Contributor
There was a problem hiding this comment.
I wouldn't add a change note. This isn't a user-facing change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lifts the shared-controlflow changes out of #21921 so they can be reviewed independently. No behavioural change for existing languages (Java, C#, Ruby, Swift, Go, …) — both new predicates default to
none()and theMake0loop-edge case extensions only fire when a language overrides them.Motivation
Python's upcoming new CFG library (#21921) needs to model
while-else/for-else, where theelseblock runs when the loop condition becomes false (rather than via abreak). The shared CFG didn't previously have predicates exposing those else blocks.Changes
getWhileElse(WhileStmt)andgetForeachElse(ForeachStmt)toAstSig(shared/controlflow/codeql/controlflow/ControlFlowGraph.qll).Make0in three places (whileexit,foreachempty-collection exit,foreachpost-body exit) to route through the else block if one exists.Verification
java/ql/test/library-tests/controlflow/— all 12 tests pass (CFG output unchanged).