Python: compact-renumber FunctionExpr/Lambda defaults

`Args.getDefault(int)` and `Args.getKwDefault(int)` are indexed by
argument position (with gaps for args without defaults), not by
default position. The CFG `getChild` predicate for FunctionDefExpr
and LambdaExpr therefore had gaps at low indices and collisions
where defaults and kwdefaults overlapped, producing parallel
edges before the FunctionExpr.

Use `rank` to compact-renumber `getDefault(n)` and `getKwDefault(n)`
in source order. Verified on a CPython database: removes ~536
`multipleSuccessors` consistency results (1340 -> 804); the rest are
`for/else` and `while/else`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Copilot
2026-05-05 14:25:43 +00:00
committed by yoff
parent 58cda914db
commit edfe91832b

View File

@@ -835,9 +835,22 @@ module Ast implements AstSig<Py::Location> {
FunctionDefExpr() { funcExpr = this.asExpr() }
Expr getDefault(int n) { result = TExpr(funcExpr.getArgs().getDefault(n)) }
/**
* Gets the `n`th default for a positional argument, in evaluation
* order. Note that `Args.getDefault(int)` is indexed by argument
* position (with gaps for arguments without defaults), so we must
* renumber here to obtain contiguous indices.
*/
Expr getDefault(int n) {
result =
TExpr(rank[n + 1](Py::Expr d, int i | d = funcExpr.getArgs().getDefault(i) | d order by i))
}
Expr getKwDefault(int n) { result = TExpr(funcExpr.getArgs().getKwDefault(n)) }
/** Gets the `n`th default for a keyword-only argument, in evaluation order. */
Expr getKwDefault(int n) {
result =
TExpr(rank[n + 1](Py::Expr d, int i | d = funcExpr.getArgs().getKwDefault(i) | d order by i))
}
int getNumberOfDefaults() { result = count(funcExpr.getArgs().getADefault()) }
}
@@ -848,9 +861,17 @@ module Ast implements AstSig<Py::Location> {
LambdaExpr() { lambda = this.asExpr() }
Expr getDefault(int n) { result = TExpr(lambda.getArgs().getDefault(n)) }
/** Gets the `n`th default for a positional argument, in evaluation order. */
Expr getDefault(int n) {
result =
TExpr(rank[n + 1](Py::Expr d, int i | d = lambda.getArgs().getDefault(i) | d order by i))
}
Expr getKwDefault(int n) { result = TExpr(lambda.getArgs().getKwDefault(n)) }
/** Gets the `n`th default for a keyword-only argument, in evaluation order. */
Expr getKwDefault(int n) {
result =
TExpr(rank[n + 1](Py::Expr d, int i | d = lambda.getArgs().getKwDefault(i) | d order by i))
}
int getNumberOfDefaults() { result = count(lambda.getArgs().getADefault()) }
}