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>
This commit is contained in:
yoff
2026-06-02 13:30:51 +00:00
committed by yoff
parent aaa3b363e1
commit 4d2296d4f0

View File

@@ -211,6 +211,31 @@ signature module AstSig<LocationSig Location> {
*/
default AstNode getTryElse(TryStmt try) { none() }
/**
* Gets the `else` block of this `while` loop statement, if any.
*
* Only some languages (e.g. Python) support `while-else` constructs.
*/
default AstNode getWhileElse(WhileStmt loop) { none() }
/**
* Gets the `else` block of this `foreach` loop statement, if any.
*
* Only some languages (e.g. Python) support `for-else` constructs.
*/
default AstNode getForeachElse(ForeachStmt loop) { none() }
/**
* Gets the type expression of this catch clause, if any.
*
* In Python, the catch type is a runtime-evaluated expression
* (e.g. `except SomeException:` where `SomeException` is an
* arbitrary expression). For languages where the catch type is
* statically resolved, this defaults to `none()` and no CFG node
* is created.
*/
default Expr getCatchType(CatchClause catch) { none() }
/** A catch clause in a try statement. */
class CatchClause extends AstNode {
/** Gets the variable declared by this catch clause. */
@@ -1549,19 +1574,32 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
n2.isBefore(loopstmt.getBody())
or
n1.isAfterFalse(cond) and
n2.isAfter(loopstmt)
(
n2.isBefore(getWhileElse(loopstmt))
or
not exists(getWhileElse(loopstmt)) and n2.isAfter(loopstmt)
)
or
n1.isAfter(loopstmt.getBody()) and
n2.isAdditional(loopstmt, loopHeaderTag())
)
or
exists(WhileStmt whilestmt |
n1.isAfter(getWhileElse(whilestmt)) and
n2.isAfter(whilestmt)
)
or
exists(ForeachStmt foreachstmt |
n1.isBefore(foreachstmt) and
n2.isBefore(foreachstmt.getCollection())
or
n1.isAfterValue(foreachstmt.getCollection(),
any(EmptinessSuccessor t | t.getValue() = true)) and
n2.isAfter(foreachstmt)
(
n2.isBefore(getForeachElse(foreachstmt))
or
not exists(getForeachElse(foreachstmt)) and n2.isAfter(foreachstmt)
)
or
n1.isAfterValue(foreachstmt.getCollection(),
any(EmptinessSuccessor t | t.getValue() = false)) and
@@ -1574,10 +1612,17 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
n2.isAdditional(foreachstmt, loopHeaderTag())
or
n1.isAdditional(foreachstmt, loopHeaderTag()) and
n2.isAfter(foreachstmt)
(
n2.isBefore(getForeachElse(foreachstmt))
or
not exists(getForeachElse(foreachstmt)) and n2.isAfter(foreachstmt)
)
or
n1.isAdditional(foreachstmt, loopHeaderTag()) and
n2.isBefore(foreachstmt.getVariable())
or
n1.isAfter(getForeachElse(foreachstmt)) and
n2.isAfter(foreachstmt)
)
or
exists(ForStmt forstmt, PreControlFlowNode condentry |
@@ -1671,6 +1716,16 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
exists(CatchClause catchclause |
exists(MatchingSuccessor t |
n1.isBefore(catchclause) and
(
n2.isBefore(getCatchType(catchclause))
or
not exists(getCatchType(catchclause)) and n2.isAfterValue(catchclause, t)
) and
if Input1::catchAll(catchclause) then t.getValue() = true else any()
)
or
exists(MatchingSuccessor t |
n1.isAfter(getCatchType(catchclause)) and
n2.isAfterValue(catchclause, t) and
if Input1::catchAll(catchclause) then t.getValue() = true else any()
)