Python: introduce TStmt union via newtype-branch alias

Rename the TStmt newtype branch to TPyStmt, and add a private union
type alias

  private class TStmt = TPyStmt or TBlockStmt;

This lets the public Stmt class use TStmt directly in its extends
clause:

  class Stmt extends AstNodeImpl, TStmt { ... }

instead of the previous

  class Stmt extends AstNodeImpl {
    Stmt() { this instanceof TStmt or this instanceof TBlockStmt }
    ...
  }

The same pattern is used in cpp/.../TInstruction.qll and
rust/.../Synth.qll.

No behaviour change: all 24 NewCfg evaluation-order tests pass; all
11 shared-CFG consistency queries report 0 violations on CPython.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Copilot
2026-05-07 17:08:10 +00:00
parent ed1709eb4a
commit c9445f74c2

View File

@@ -18,7 +18,7 @@ private import codeql.util.Void
/** Provides the Python implementation of the shared CFG `AstSig`. */
module Ast implements AstSig<Py::Location> {
private newtype TAstNode =
TStmt(Py::Stmt s) or
TPyStmt(Py::Stmt s) or
TExpr(Py::Expr e) { not e instanceof Py::BoolExpr } or
TScope(Py::Scope sc) or
TPattern(Py::Pattern p) or
@@ -69,6 +69,13 @@ module Ast implements AstSig<Py::Location> {
sl = any(Py::ExceptGroupStmt p).getBody()
}
/**
* The union of `TPyStmt` (wrapping `Py::Stmt`) and `TBlockStmt` (wrapping
* `Py::StmtList`). Both represent the kinds of node that can appear in
* a `Stmt` position in the CFG.
*/
private class TStmt = TPyStmt or TBlockStmt;
/**
* An AST node visible to the shared CFG.
*
@@ -89,7 +96,7 @@ module Ast implements AstSig<Py::Location> {
abstract Callable getEnclosingCallable();
/** Gets the underlying Python `Stmt`, if this node wraps one. */
Py::Stmt asStmt() { this = TStmt(result) }
Py::Stmt asStmt() { this = TPyStmt(result) }
/**
* Gets the underlying Python `Expr`, if this node wraps one. Boolean
@@ -164,10 +171,8 @@ module Ast implements AstSig<Py::Location> {
Parameter callableGetParameter(Callable c, int index) { none() }
/** A statement. */
class Stmt extends AstNodeImpl {
Stmt() { this instanceof TStmt or this instanceof TBlockStmt }
// For `TStmt` instances, delegate to the wrapped Python statement.
class Stmt extends AstNodeImpl, TStmt {
// For `TPyStmt` instances, delegate to the wrapped Python statement.
// `BlockStmt` (the only `TBlockStmt` subclass) provides its own overrides.
override string toString() { result = this.asStmt().toString() }
@@ -513,7 +518,7 @@ module Ast implements AstSig<Py::Location> {
Stmt getFinally() { result = TBlockStmt(tryStmt.getFinalbody()) }
CatchClause getCatch(int index) { result = TStmt(tryStmt.getHandler(index)) }
CatchClause getCatch(int index) { result = TPyStmt(tryStmt.getHandler(index)) }
override AstNode getChild(int index) {
index = 0 and result = this.getBody()
@@ -580,7 +585,7 @@ module Ast implements AstSig<Py::Location> {
Expr getExpr() { result.asExpr() = matchStmt.getSubject() }
Case getCase(int index) { result = TStmt(matchStmt.getCase(index)) }
Case getCase(int index) { result = TPyStmt(matchStmt.getCase(index)) }
Stmt getStmt(int index) { none() }