mirror of
https://github.com/github/codeql.git
synced 2026-05-30 19:11:23 +02:00
Python: wire AnnAssign into the shared CFG (green)
Adds an `AnnAssignStmt` wrapper in `AstNodeImpl.qll` so that PEP 526 annotated assignments (`x: int = 1`, `x: int`) participate in the control flow graph. Evaluation order follows CPython: annotation, optional value, target binding. Without this, `x: int = 1` had no CFG node for `x` even though `Name.defines(v)` returns true for it on the AST side. SSA built on the new CFG would therefore miss every annotated-assignment write. Removes the corresponding MISSING: annotations from the CFG-binding gap test: - annassign.py — all four cases now green. - match_pattern.py — class-body annotated fields (`x: int`, `y: int`). - type_params.py — `item: T` inside class. Verified: all 24 ControlFlow/evaluation-order tests still pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -265,6 +265,31 @@ module Ast implements AstSig<Py::Location> {
|
||||
override AstNode getChild(int index) { index = 0 and result = this.getOperation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An annotated assignment statement (`x: T = expr`, or `x: T` without
|
||||
* value). The evaluation order follows CPython: annotation first, then
|
||||
* the optional value, then the target binding.
|
||||
*/
|
||||
additional class AnnAssignStmt extends Stmt {
|
||||
private Py::AnnAssign annAssign;
|
||||
|
||||
AnnAssignStmt() { this = TPyStmt(annAssign) }
|
||||
|
||||
Expr getAnnotation() { result.asExpr() = annAssign.getAnnotation() }
|
||||
|
||||
Expr getValue() { result.asExpr() = annAssign.getValue() }
|
||||
|
||||
Expr getTarget() { result.asExpr() = annAssign.getTarget() }
|
||||
|
||||
override AstNode getChild(int index) {
|
||||
index = 0 and result = this.getAnnotation()
|
||||
or
|
||||
index = 1 and result = this.getValue()
|
||||
or
|
||||
index = 2 and result = this.getTarget()
|
||||
}
|
||||
}
|
||||
|
||||
/** An assignment expression / walrus operator (`x := expr`). */
|
||||
additional class NamedExpr extends Expr {
|
||||
private Py::AssignExpr assignExpr;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# Annotated assignment (PEP 526). Both with and without an initializer.
|
||||
|
||||
a: int = 1 # $ MISSING: cfgdefines=a
|
||||
b: str = "hi" # $ MISSING: cfgdefines=b
|
||||
a: int = 1 # $ cfgdefines=a
|
||||
b: str = "hi" # $ cfgdefines=b
|
||||
|
||||
# Annotation without value: the AST records `c` as defined,
|
||||
# but currently the new CFG has no node for it.
|
||||
c: int # $ MISSING: cfgdefines=c
|
||||
# and the new CFG now visits it via the AnnAssignStmt wrapper.
|
||||
c: int # $ cfgdefines=c
|
||||
|
||||
class K: # $ cfgdefines=K
|
||||
field: int = 0 # $ MISSING: cfgdefines=field
|
||||
field: int = 0 # $ cfgdefines=field
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,6 @@ def f(subject): # $ cfgdefines=f
|
||||
|
||||
class Point: # $ cfgdefines=Point
|
||||
__match_args__ = ("x", "y") # $ cfgdefines=__match_args__
|
||||
x: int # $ MISSING: cfgdefines=x
|
||||
y: int # $ MISSING: cfgdefines=y
|
||||
x: int # $ cfgdefines=x
|
||||
y: int # $ cfgdefines=y
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ def func[T](x: T) -> T: # $ cfgdefines=func MISSING: cfgdefines=T
|
||||
|
||||
|
||||
class Box[T]: # $ cfgdefines=Box MISSING: cfgdefines=T
|
||||
item: T # $ MISSING: cfgdefines=item
|
||||
item: T # $ cfgdefines=item
|
||||
|
||||
|
||||
# Multi-parameter, with bound and variadics.
|
||||
|
||||
Reference in New Issue
Block a user