From c9445f74c2d598bf5430209977b1a064b22f9be5 Mon Sep 17 00:00:00 2001 From: Copilot Date: Thu, 7 May 2026 17:08:10 +0000 Subject: [PATCH] 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> --- .../controlflow/internal/AstNodeImpl.qll | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll b/python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll index a0fb1c7bf72..a4ea9575565 100644 --- a/python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll +++ b/python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll @@ -18,7 +18,7 @@ private import codeql.util.Void /** Provides the Python implementation of the shared CFG `AstSig`. */ module Ast implements AstSig { 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 { 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 { 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 { 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 { 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 { 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() }