mirror of
https://github.com/github/codeql.git
synced 2026-05-27 01:21:23 +02:00
Python: model from X import * as uncertain SSA writes
Add a 4th disjunct to `SsaImplInput::variableWrite` in the shared-SSA adapter that mirrors legacy ESSA's `ImportStarRefinement`: every variable whose scope is the import-star's scope, OR which is used in the import-star's scope, gets an uncertain write at the `import *` position. Uncertain writes do not kill prior definitions; shared SSA's `SsaUncertainWrite` joins the new value with the immediately-preceding definition via `uncertainWriteDefinitionInput`. This is the equivalent of legacy ESSA's two-input refinement. Cannot depend on `ImportStar` / `ImportResolution` (those modules import `SsaImpl`), so the predicate uses the structural heuristic on `Cfg::ImportStarNode` directly. This closes the two remaining failing dataflow library-tests: - `import-star/global` — `module_export` chains via `from X import *` re-exports now resolve: the importing module has an SSA def of every re-exported name, so `lastUseVar` finds the read at the use site. - `typetracking_imports/highlight_problem` — a direct `from .foo import foo` immediately followed by `from .other import *` is now correctly marked as dead at the direct import. Two scope-entry-def noise rows in `highlight_problem.expected` are also dropped — legacy ESSA needed them as refinement inputs, but shared SSA handles uncertain writes without an explicit prior def. They were always tagged `no use to normal exit` (dead). Dataflow library-tests: 62/64 → 64/64 passing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -48,14 +48,6 @@ private module CfgForSsa implements BB::CfgSig<Py::Location> {
|
||||
predicate dominatingEdge = CfgImpl::Cfg::dominatingEdge/2;
|
||||
}
|
||||
|
||||
/**
|
||||
* A source variable for SSA. Wraps a Python `Variable` (the AST-level
|
||||
* notion of a named binding within a scope) so that the shared SSA
|
||||
* implementation can use it as a `SourceVariable`.
|
||||
*
|
||||
* We only track variables that are read at least once in their scope —
|
||||
* tracking write-only variables is unnecessary work.
|
||||
*/
|
||||
/**
|
||||
* A source variable for SSA, wrapping a Python AST `Variable`.
|
||||
*
|
||||
@@ -113,7 +105,9 @@ class SsaSourceVariable extends TSsaSourceVariable {
|
||||
* `SsaSourceVariable.getASourceUse()`.
|
||||
*/
|
||||
Cfg::ControlFlowNode getASourceUse() {
|
||||
exists(Cfg::NameNode n | result = n | n.uses(this.getVariable()) or n.deletes(this.getVariable()))
|
||||
exists(Cfg::NameNode n | result = n |
|
||||
n.uses(this.getVariable()) or n.deletes(this.getVariable())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,6 +217,31 @@ private module SsaImplInput implements SsaImplCommon::InputSig<Py::Location, Cfg
|
||||
hasEntryDefIn(v, bb) and
|
||||
i = -1 and
|
||||
certain = true
|
||||
or
|
||||
// `from X import *` — possibly rebinds every name in the importing
|
||||
// scope. Modelled as an uncertain write at the import-star's CFG
|
||||
// position for every variable that lives in (or is referenced
|
||||
// from) the same scope as the import-star. Mirrors legacy ESSA's
|
||||
// `ImportStarRefinement` (see `essa/SsaDefinitions.qll`'s
|
||||
// `import_star_refinement` predicate). The write is uncertain so
|
||||
// that prior definitions of the variable remain available — the
|
||||
// shared-SSA `SsaUncertainWrite` merges the new value with the
|
||||
// immediately preceding definition.
|
||||
exists(Cfg::ImportStarNode imp |
|
||||
bb.getNode(i) = imp and
|
||||
certain = false and
|
||||
(
|
||||
v.getVariable().getScope() = imp.getScope()
|
||||
or
|
||||
// Variable is defined in some other scope but referenced in
|
||||
// the same scope as the import-star (matches legacy clause 2:
|
||||
// `other.uses(v) and def.getScope() = other.getScope()`).
|
||||
exists(Cfg::NameNode other |
|
||||
other.uses(v.getVariable()) and
|
||||
imp.getScope() = other.getScope()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate variableRead(CfgImpl::BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
@@ -400,13 +419,17 @@ class MultiAssignmentDefinition extends EssaNodeDefinition {
|
||||
|
||||
override Cfg::ControlFlowNode getDefiningNode() {
|
||||
// Default: the underlying `Name` CFG node (where the SSA def lives).
|
||||
not exists(Cfg::StarredNode s | s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()) and
|
||||
not exists(Cfg::StarredNode s |
|
||||
s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()
|
||||
) and
|
||||
result = super.getDefiningNode()
|
||||
or
|
||||
// Exception: for `*rest`, expose the enclosing `Starred` CFG node
|
||||
// so that `IterableUnpacking::iterableUnpackingStarredElementStoreStep`
|
||||
// can attach the rest-list to it.
|
||||
exists(Cfg::StarredNode s | s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode() |
|
||||
exists(Cfg::StarredNode s |
|
||||
s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()
|
||||
|
|
||||
result = s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
| pkg/alias_only_direct.py:0:0:0:0 | Module pkg.alias_only_direct | pkg/alias_only_direct.py:1:22:1:24 | GSSA Variable foo | use to normal exit |
|
||||
| pkg/alias_problem.py:0:0:0:0 | Module pkg.alias_problem | pkg/alias_problem.py:1:22:1:24 | GSSA Variable foo | no use to normal exit |
|
||||
| pkg/alias_problem.py:0:0:0:0 | Module pkg.alias_problem | pkg/alias_problem.py:2:1:2:20 | GSSA Variable foo | use to normal exit |
|
||||
| pkg/alias_problem_fixed.py:0:0:0:0 | Module pkg.alias_problem_fixed | pkg/alias_problem_fixed.py:0:0:0:0 | GSSA Variable foo | no use to normal exit |
|
||||
| pkg/alias_problem_fixed.py:0:0:0:0 | Module pkg.alias_problem_fixed | pkg/alias_problem_fixed.py:3:22:3:24 | GSSA Variable foo | use to normal exit |
|
||||
| pkg/problem_absolute_import.py:0:0:0:0 | Module pkg.problem_absolute_import | pkg/problem_absolute_import.py:1:25:1:27 | GSSA Variable foo | no use to normal exit |
|
||||
| pkg/problem_absolute_import.py:0:0:0:0 | Module pkg.problem_absolute_import | pkg/problem_absolute_import.py:2:1:2:23 | GSSA Variable foo | use to normal exit |
|
||||
| pkg/works_absolute_import.py:0:0:0:0 | Module pkg.works_absolute_import | pkg/works_absolute_import.py:0:0:0:0 | GSSA Variable foo | no use to normal exit |
|
||||
| pkg/works_absolute_import.py:0:0:0:0 | Module pkg.works_absolute_import | pkg/works_absolute_import.py:2:25:2:27 | GSSA Variable foo | use to normal exit |
|
||||
| pkg/alias_only_direct.py:0:0:0:0 | Module pkg.alias_only_direct | pkg/alias_only_direct.py:1:22:1:24 | SSA def(Global Variable foo) | use to normal exit |
|
||||
| pkg/alias_problem.py:0:0:0:0 | Module pkg.alias_problem | pkg/alias_problem.py:1:22:1:24 | SSA def(Global Variable foo) | no use to normal exit |
|
||||
| pkg/alias_problem.py:0:0:0:0 | Module pkg.alias_problem | pkg/alias_problem.py:2:1:2:20 | SSA def(Global Variable foo) | use to normal exit |
|
||||
| pkg/alias_problem_fixed.py:0:0:0:0 | Module pkg.alias_problem_fixed | pkg/alias_problem_fixed.py:3:22:3:24 | SSA def(Global Variable foo) | use to normal exit |
|
||||
| pkg/problem_absolute_import.py:0:0:0:0 | Module pkg.problem_absolute_import | pkg/problem_absolute_import.py:1:25:1:27 | SSA def(Global Variable foo) | no use to normal exit |
|
||||
| pkg/problem_absolute_import.py:0:0:0:0 | Module pkg.problem_absolute_import | pkg/problem_absolute_import.py:2:1:2:23 | SSA def(Global Variable foo) | use to normal exit |
|
||||
| pkg/works_absolute_import.py:0:0:0:0 | Module pkg.works_absolute_import | pkg/works_absolute_import.py:2:25:2:27 | SSA def(Global Variable foo) | use to normal exit |
|
||||
|
||||
Reference in New Issue
Block a user