From 091479cd6b3e2b5436b16c0f850947b27bf0d4ce Mon Sep 17 00:00:00 2001 From: yoff Date: Tue, 26 May 2026 13:10:00 +0000 Subject: [PATCH] Python: drop legacy essa import from ImportResolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ImportResolution.qll` was the last new-dataflow file with a direct `import semmle.python.essa.SsaDefinitions`, used only for the `SsaSource::init_module_submodule_defn` helper. Inline the 5-line body as a local private predicate. No functional change — the inlined predicate is clause-for-clause equivalent (the `f = init.getEntryNode()` join only constrained `package = init`, since `Scope.getEntryNode()` is unique per scope; we now express that constraint directly). All 70 dataflow + ApiGraphs library-tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../new/internal/ImportResolution.qll | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll index c9f2c79aebc..735a7e831bd 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll @@ -11,7 +11,18 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.internal.ImportStar private import semmle.python.dataflow.new.TypeTracking private import semmle.python.dataflow.new.internal.DataFlowPrivate -private import semmle.python.essa.SsaDefinitions + +/** + * Holds if the name of `var` refers to a submodule of a package and `init` is + * the `__init__` module of that package. Locally inlined replacement for the + * legacy `SsaSource::init_module_submodule_defn` so that this module has no + * direct dependency on `semmle.python.essa.SsaDefinitions`. + */ +private predicate initModuleSubmoduleDefn(GlobalVariable var, Module init) { + init.isPackageInit() and + exists(init.getPackage().getSubModule(var.getId())) and + var.getScope() = init +} /** * Python modules and the way imports are resolved are... complicated. Here's a crash course in how @@ -71,7 +82,9 @@ module ImportResolution { * Holds if there is an ESSA step from `defFrom` to `defTo`, which should be allowed * for import resolution. */ - private predicate allowedEssaImportStep(SsaImpl::EssaDefinition defFrom, SsaImpl::EssaDefinition defTo) { + private predicate allowedEssaImportStep( + SsaImpl::EssaDefinition defFrom, SsaImpl::EssaDefinition defTo + ) { // to handle definitions guarded by if-then-else defFrom = defTo.(SsaImpl::PhiFunction).getAnInput() // Note: legacy ESSA refinement-step (e.g. for `foo.bar = X`) is @@ -160,7 +173,9 @@ module ImportResolution { */ private predicate no_or_complicated_all(Module m) { // No mention of `__all__` in the module - not exists(Cfg::DefinitionNode def | def.getScope() = m and def.(Cfg::NameNode).getId() = "__all__") + not exists(Cfg::DefinitionNode def | + def.getScope() = m and def.(Cfg::NameNode).getId() = "__all__" + ) or // `__all__` is set to a non-sequence value exists(Cfg::DefinitionNode def | @@ -328,7 +343,7 @@ module ImportResolution { // imported yet. exists(string submodule, Module package, SsaImpl::EssaVariable var | submodule = var.getName() and - SsaSource::init_module_submodule_defn(var.getSourceVariable().getVariable(), package.getEntryNode()) and + initModuleSubmoduleDefn(var.getSourceVariable().getVariable(), package) and m = getModuleFromName(package.getPackageName() + "." + submodule) and result.asCfgNode() = var.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode() )