mirror of
https://github.com/github/codeql.git
synced 2026-05-27 01:21:23 +02:00
Python: omit PEP 695 type-param names from FunctionDefExpr/ClassDefExpr children
PEP 695 type-param names (e.g. `T` in `def func[T]:` or `class Box[T]:`) bind in an annotation scope that nests the function/class body, so their AST scope is the inner function/class — not the enclosing scope where the FunctionDefExpr/ClassDefExpr CFG node lives. Visiting them as children created scope-crossing CFG edges (nonLocalStep violations: 96 across CPython). Drop them from the children list; the legacy CFG omitted them too. TypeAliasStmt is unaffected (its type-params share scope with the alias's enclosing scope). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1423,49 +1423,34 @@ module Ast implements AstSig<Py::Location> {
|
||||
override AstNode getChild(int index) { index = 0 and result = this.getValue() }
|
||||
}
|
||||
|
||||
/** A class definition expression (has base classes evaluated at definition time). */
|
||||
/**
|
||||
* A class definition expression (visits bases, but NOT PEP 695 type
|
||||
* parameters — those bind in an annotation scope that nests the class
|
||||
* body, so they belong to the inner scope's CFG, not the enclosing
|
||||
* scope's; the legacy CFG also omitted them).
|
||||
*/
|
||||
additional class ClassDefExpr extends Expr {
|
||||
private Py::ClassExpr classExpr;
|
||||
|
||||
ClassDefExpr() { this = TPyExpr(classExpr) }
|
||||
|
||||
/**
|
||||
* Gets the `n`th PEP 695 type-parameter name (a `Name` in store
|
||||
* context), in declaration order. These bind in the enclosing scope
|
||||
* at class-definition time, so the CFG must visit them.
|
||||
*/
|
||||
Expr getTypeParamName(int n) {
|
||||
result.asExpr() = typeParameterName(classExpr.getTypeParameter(n))
|
||||
}
|
||||
|
||||
int getNumberOfTypeParams() { result = count(classExpr.getATypeParameter()) }
|
||||
|
||||
Expr getBase(int n) { result.asExpr() = classExpr.getBase(n) }
|
||||
|
||||
override AstNode getChild(int index) {
|
||||
result = this.getTypeParamName(index)
|
||||
or
|
||||
result = this.getBase(index - this.getNumberOfTypeParams())
|
||||
}
|
||||
override AstNode getChild(int index) { result = this.getBase(index) }
|
||||
}
|
||||
|
||||
/** A function definition expression (has default args evaluated at definition time). */
|
||||
/**
|
||||
* A function definition expression (visits positional and keyword
|
||||
* defaults, but NOT PEP 695 type parameters — those bind in an
|
||||
* annotation scope that nests the function body, so they belong to
|
||||
* the inner scope's CFG, not the enclosing scope's; the legacy CFG
|
||||
* also omitted them).
|
||||
*/
|
||||
additional class FunctionDefExpr extends Expr {
|
||||
private Py::FunctionExpr funcExpr;
|
||||
|
||||
FunctionDefExpr() { this = TPyExpr(funcExpr) }
|
||||
|
||||
/**
|
||||
* Gets the `n`th PEP 695 type-parameter name (a `Name` in store
|
||||
* context), in declaration order. These bind in the enclosing scope
|
||||
* at function-definition time, so the CFG must visit them.
|
||||
*/
|
||||
Expr getTypeParamName(int n) {
|
||||
result.asExpr() = typeParameterName(funcExpr.getInnerScope().getTypeParameter(n))
|
||||
}
|
||||
|
||||
int getNumberOfTypeParams() { result = count(funcExpr.getInnerScope().getATypeParameter()) }
|
||||
|
||||
/**
|
||||
* Gets the `n`th default for a positional argument, in evaluation
|
||||
* order. Note that `Args.getDefault(int)` is indexed by argument
|
||||
@@ -1486,11 +1471,9 @@ module Ast implements AstSig<Py::Location> {
|
||||
int getNumberOfDefaults() { result = count(funcExpr.getArgs().getADefault()) }
|
||||
|
||||
override AstNode getChild(int index) {
|
||||
result = this.getTypeParamName(index)
|
||||
result = this.getDefault(index)
|
||||
or
|
||||
result = this.getDefault(index - this.getNumberOfTypeParams())
|
||||
or
|
||||
result = this.getKwDefault(index - this.getNumberOfTypeParams() - this.getNumberOfDefaults())
|
||||
result = this.getKwDefault(index - this.getNumberOfDefaults())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
# PEP 695 type parameters (Python 3.12+).
|
||||
|
||||
def func[T](x: T) -> T: # $ cfgdefines=func cfgdefines=x cfgdefines=T
|
||||
# PEP 695 type-param names on `def`/`class` bind in an annotation scope
|
||||
# that nests the function/class body — they have no CFG node in the
|
||||
# enclosing scope (matching the legacy CFG).
|
||||
def func[T](x: T) -> T: # $ cfgdefines=func cfgdefines=x
|
||||
return x
|
||||
|
||||
|
||||
class Box[T]: # $ cfgdefines=Box cfgdefines=T
|
||||
class Box[T]: # $ cfgdefines=Box
|
||||
item: T # $ cfgdefines=item
|
||||
|
||||
|
||||
# Multi-parameter, with bound and variadics.
|
||||
def multi[T: int, *Ts, **P](x: T, *args: *Ts, **kwargs: P.kwargs) -> T: # $ cfgdefines=multi cfgdefines=x cfgdefines=args cfgdefines=kwargs cfgdefines=T cfgdefines=Ts cfgdefines=P
|
||||
def multi[T: int, *Ts, **P](x: T, *args: *Ts, **kwargs: P.kwargs) -> T: # $ cfgdefines=multi cfgdefines=x cfgdefines=args cfgdefines=kwargs
|
||||
return x
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user