Skip to content

Commit 964a619

Browse files
authored
Merge pull request github#3211 from RasmusWL/python-unused-import-small-fix
Python: Fix FN in unused import
2 parents a92d926 + 75e6470 commit 964a619

File tree

4 files changed

+29
-9
lines changed

4 files changed

+29
-9
lines changed

python/ql/src/Imports/UnusedImport.ql

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ private string doctest_in_scope(Scope scope) {
6262
}
6363

6464
pragma[noinline]
65-
private string typehint_annotation_in_file(File file) {
65+
private string typehint_annotation_in_module(Module module_scope) {
6666
exists(StrConst annotation |
6767
annotation = any(Arguments a).getAnAnnotation().getASubExpression*()
6868
or
@@ -71,7 +71,7 @@ private string typehint_annotation_in_file(File file) {
7171
annotation = any(FunctionExpr f).getReturns().getASubExpression*()
7272
|
7373
annotation.pointsTo(Value::forString(result)) and
74-
file = annotation.getLocation().getFile()
74+
annotation.getEnclosingModule() = module_scope
7575
)
7676
}
7777

@@ -84,17 +84,19 @@ private string typehint_comment_in_file(File file) {
8484
)
8585
}
8686

87-
predicate imported_module_used_in_typehint(Import imp) {
88-
exists(string modname, File file |
89-
imp.getAName().getAsname().(Name).getId() = modname and
90-
file = imp.getScope().(Module).getFile()
87+
/** Holds if the imported alias `name` from `imp` is used in a typehint (in the same file as `imp`) */
88+
predicate imported_alias_used_in_typehint(Import imp, Variable name) {
89+
imp.getAName().getAsname().(Name).getVariable() = name and
90+
exists(File file, Module module_scope |
91+
module_scope = imp.getEnclosingModule() and
92+
file = module_scope.getFile()
9193
|
9294
// Look for type hints containing the patterns:
9395
// # type: …name…
94-
typehint_comment_in_file(file).regexpMatch("# type:.*" + modname + ".*")
96+
typehint_comment_in_file(file).regexpMatch("# type:.*" + name.getId() + ".*")
9597
or
9698
// Type hint is inside a string annotation, as needed for forward references
97-
typehint_annotation_in_file(file).regexpMatch(".*\\b" + modname + "\\b.*")
99+
typehint_annotation_in_module(module_scope).regexpMatch(".*\\b" + name.getId() + "\\b.*")
98100
)
99101
}
100102

@@ -114,7 +116,7 @@ predicate unused_import(Import imp, Variable name) {
114116
// Assume that opaque `__all__` includes imported module
115117
not all_not_understood(imp.getEnclosingModule()) and
116118
not imported_module_used_in_doctest(imp) and
117-
not imported_module_used_in_typehint(imp) and
119+
not imported_alias_used_in_typehint(imp, name) and
118120
// Only consider import statements that actually point-to something (possibly an unknown module).
119121
// If this is not the case, it's likely that the import statement never gets executed.
120122
imp.getAName().getValue().pointsTo(_)

python/ql/test/query-tests/Imports/unused/UnusedImport.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
| import_structure_1.py:5:1:5:28 | Import | Import of 'bar' is not used. |
2+
| import_structure_2.py:6:1:6:23 | Import | Import of 'bar' is not used. |
13
| imports_test.py:2:1:2:23 | Import | Import of 'module2' is not used. |
24
| imports_test.py:6:1:6:12 | Import | Import of 'cycle' is not used. |
35
| imports_test.py:10:1:10:22 | Import | Import of 'top_level_cycle' is not used. |
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# there should be no difference whether you import 2 things on 1 line, or use 2
2+
# lines
3+
from typing import Optional
4+
5+
from unknown import foo, bar
6+
7+
8+
var: Optional['foo'] = None
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# there should be no difference whether you import 2 things on 1 line, or use 2
2+
# lines
3+
from typing import Optional
4+
5+
from unknown import foo
6+
from unknown import bar
7+
8+
var: Optional['foo'] = None

0 commit comments

Comments
 (0)