Skip to content

Commit 0740fca

Browse files
committed
Python: Port ModuleImportsItself.ql
Uses the existing machinery in ImportResolution.qll, after adding a few convenience predicates. The new modelling actually manages to find a result that the old points-to analysis did not. Apart from that there are no test changes.
1 parent 679f920 commit 0740fca

File tree

3 files changed

+39
-12
lines changed

3 files changed

+39
-12
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,4 +377,30 @@ module ImportResolution {
377377
}
378378

379379
Module getModule(DataFlow::CfgNode node) { node = getModuleReference(result) }
380+
381+
/** Holds if module `importer` directly imports module `imported`. */
382+
predicate imports(Module importer, Module imported) {
383+
getImmediateModuleReference(imported).getScope() = importer
384+
}
385+
386+
/**
387+
* Holds if the import statement `i` causes module `imported` to be imported.
388+
* For `from pkg import submodule`, both `pkg` and `pkg.submodule` are considered imported.
389+
*/
390+
predicate importedBy(ImportingStmt i, Module imported) {
391+
exists(Alias a | a = i.(Import).getAName() |
392+
getImmediateModuleReference(imported).asExpr() = a.getAsname()
393+
)
394+
or
395+
exists(ImportMember im | im = i.(Import).getAName().getValue() |
396+
getImmediateModuleReference(imported).asExpr() = im.getModule()
397+
)
398+
or
399+
getImmediateModuleReference(imported).asExpr() = i.(ImportStar).getModule().(ImportExpr)
400+
}
401+
402+
/** Gets a user-friendly name for module `m`, using the package name for `__init__` modules. */
403+
string moduleName(Module m) {
404+
if m.isPackageInit() then result = m.getPackageName() else result = m.getName()
405+
}
380406
}

python/ql/src/Imports/ModuleImportsItself.ql

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@
1212
*/
1313

1414
import python
15-
private import LegacyPointsTo
15+
import semmle.python.dataflow.new.DataFlow
16+
private import semmle.python.dataflow.new.internal.ImportResolution
1617

17-
predicate modules_imports_itself(ImportingStmt i, ModuleValue m) {
18-
i.getEnclosingModule() = m.getScope() and
19-
m =
20-
max(string s, ModuleValue m_ |
21-
s = i.getAnImportedModuleName() and
22-
m_.importedAs(s)
23-
|
24-
m_ order by s.length()
25-
)
18+
predicate modules_imports_itself(ImportingStmt i, Module m) {
19+
m = i.getEnclosingModule() and
20+
ImportResolution::importedBy(i, m) and
21+
// Exclude `from m import submodule` where the imported member is a submodule of m
22+
not exists(ImportMember im | im = i.(Import).getAName().getValue() |
23+
ImportResolution::getImmediateModuleReference(m).asExpr() = im.getModule() and
24+
ImportResolution::importedBy(i, any(Module sub | sub != m))
25+
)
2626
}
2727

28-
from ImportingStmt i, ModuleValue m
28+
from ImportingStmt i, Module m
2929
where modules_imports_itself(i, m)
30-
select i, "The module '" + m.getName() + "' imports itself."
30+
select i, "The module '" + ImportResolution::moduleName(m) + "' imports itself."
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
| imports_test.py:8:1:8:19 | Import | The module 'imports_test' imports itself. |
22
| pkg_notok/__init__.py:4:1:4:16 | Import | The module 'pkg_notok' imports itself. |
3+
| pkg_notok/__init__.py:10:1:10:20 | Import | The module 'pkg_notok' imports itself. |
34
| pkg_notok/__init__.py:12:1:12:25 | Import | The module 'pkg_notok' imports itself. |
45
| pkg_notok/__init__.py:13:1:13:37 | Import | The module 'pkg_notok' imports itself. |
56
| pkg_notok/__init__.py:14:1:14:23 | from pkg_notok import * | The module 'pkg_notok' imports itself. |

0 commit comments

Comments
 (0)