mirror of
https://github.com/github/codeql.git
synced 2026-05-03 04:39:29 +02:00
Python: Autoformat everything using qlformat.
Will need subsequent PRs fixing up test failures (due to deprecated methods moving around), but other than that everything should be straight-forward.
This commit is contained in:
@@ -3,84 +3,84 @@ import python
|
||||
predicate is_import_time(Stmt s) { not s.getScope+() instanceof Function }
|
||||
|
||||
ModuleValue module_imported_by(ModuleValue m) {
|
||||
exists(Stmt imp |
|
||||
result = stmt_imports(imp) and
|
||||
imp.getEnclosingModule() = m.getScope() and
|
||||
// Import must reach exit to be part of a cycle
|
||||
imp.getAnEntryNode().getBasicBlock().reachesExit()
|
||||
)
|
||||
exists(Stmt imp |
|
||||
result = stmt_imports(imp) and
|
||||
imp.getEnclosingModule() = m.getScope() and
|
||||
// Import must reach exit to be part of a cycle
|
||||
imp.getAnEntryNode().getBasicBlock().reachesExit()
|
||||
)
|
||||
}
|
||||
|
||||
/** Is there a circular import of 'm1' beginning with 'm2'? */
|
||||
predicate circular_import(ModuleValue m1, ModuleValue m2) {
|
||||
m1 != m2 and
|
||||
m2 = module_imported_by(m1) and
|
||||
m1 = module_imported_by+(m2)
|
||||
m1 != m2 and
|
||||
m2 = module_imported_by(m1) and
|
||||
m1 = module_imported_by+(m2)
|
||||
}
|
||||
|
||||
ModuleValue stmt_imports(ImportingStmt s) {
|
||||
exists(string name | result.importedAs(name) and not name = "__main__" |
|
||||
name = s.getAnImportedModuleName() and
|
||||
s.getASubExpression().pointsTo(result) and
|
||||
not result.isPackage()
|
||||
)
|
||||
exists(string name | result.importedAs(name) and not name = "__main__" |
|
||||
name = s.getAnImportedModuleName() and
|
||||
s.getASubExpression().pointsTo(result) and
|
||||
not result.isPackage()
|
||||
)
|
||||
}
|
||||
|
||||
predicate import_time_imported_module(ModuleValue m1, ModuleValue m2, Stmt imp) {
|
||||
imp.getEnclosingModule() = m1.getScope() and
|
||||
is_import_time(imp) and
|
||||
m2 = stmt_imports(imp)
|
||||
imp.getEnclosingModule() = m1.getScope() and
|
||||
is_import_time(imp) and
|
||||
m2 = stmt_imports(imp)
|
||||
}
|
||||
|
||||
/** Is there a cyclic import of 'm1' beginning with an import 'm2' at 'imp' where all the imports are top-level? */
|
||||
predicate import_time_circular_import(ModuleValue m1, ModuleValue m2, Stmt imp) {
|
||||
m1 != m2 and
|
||||
import_time_imported_module(m1, m2, imp) and
|
||||
import_time_transitive_import(m2, _, m1)
|
||||
m1 != m2 and
|
||||
import_time_imported_module(m1, m2, imp) and
|
||||
import_time_transitive_import(m2, _, m1)
|
||||
}
|
||||
|
||||
predicate import_time_transitive_import(ModuleValue base, Stmt imp, ModuleValue last) {
|
||||
last != base and
|
||||
(
|
||||
import_time_imported_module(base, last, imp)
|
||||
or
|
||||
exists(ModuleValue mid |
|
||||
import_time_transitive_import(base, imp, mid) and
|
||||
import_time_imported_module(mid, last, _)
|
||||
)
|
||||
) and
|
||||
// Import must reach exit to be part of a cycle
|
||||
imp.getAnEntryNode().getBasicBlock().reachesExit()
|
||||
last != base and
|
||||
(
|
||||
import_time_imported_module(base, last, imp)
|
||||
or
|
||||
exists(ModuleValue mid |
|
||||
import_time_transitive_import(base, imp, mid) and
|
||||
import_time_imported_module(mid, last, _)
|
||||
)
|
||||
) and
|
||||
// Import must reach exit to be part of a cycle
|
||||
imp.getAnEntryNode().getBasicBlock().reachesExit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns import-time usages of module 'm' in module 'enclosing'
|
||||
*/
|
||||
predicate import_time_module_use(ModuleValue m, ModuleValue enclosing, Expr use, string attr) {
|
||||
exists(Expr mod |
|
||||
use.getEnclosingModule() = enclosing.getScope() and
|
||||
not use.getScope+() instanceof Function and
|
||||
mod.pointsTo(m) and
|
||||
not is_annotation_with_from_future_import_annotations(use)
|
||||
|
|
||||
// either 'M.foo'
|
||||
use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr
|
||||
or
|
||||
// or 'from M import foo'
|
||||
use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr
|
||||
)
|
||||
exists(Expr mod |
|
||||
use.getEnclosingModule() = enclosing.getScope() and
|
||||
not use.getScope+() instanceof Function and
|
||||
mod.pointsTo(m) and
|
||||
not is_annotation_with_from_future_import_annotations(use)
|
||||
|
|
||||
// either 'M.foo'
|
||||
use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr
|
||||
or
|
||||
// or 'from M import foo'
|
||||
use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` appears inside an annotation.
|
||||
*/
|
||||
predicate is_used_in_annotation(Expr use) {
|
||||
exists(FunctionExpr f |
|
||||
f.getReturns().getASubExpression*() = use or
|
||||
f.getArgs().getAnAnnotation().getASubExpression*() = use
|
||||
)
|
||||
or
|
||||
exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use)
|
||||
exists(FunctionExpr f |
|
||||
f.getReturns().getASubExpression*() = use or
|
||||
f.getArgs().getAnAnnotation().getASubExpression*() = use
|
||||
)
|
||||
or
|
||||
exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,10 +89,10 @@ predicate is_used_in_annotation(Expr use) {
|
||||
* See https://www.python.org/dev/peps/pep-0563/
|
||||
*/
|
||||
predicate is_annotation_with_from_future_import_annotations(Expr use) {
|
||||
exists(ImportMember i | i.getScope() = use.getEnclosingModule() |
|
||||
i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations"
|
||||
) and
|
||||
is_used_in_annotation(use)
|
||||
exists(ImportMember i | i.getScope() = use.getEnclosingModule() |
|
||||
i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations"
|
||||
) and
|
||||
is_used_in_annotation(use)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,18 +101,18 @@ predicate is_annotation_with_from_future_import_annotations(Expr use) {
|
||||
* occur after the import 'other' in 'first'.
|
||||
*/
|
||||
predicate failing_import_due_to_cycle(
|
||||
ModuleValue first, ModuleValue other, Stmt imp, ControlFlowNode defn, Expr use, string attr
|
||||
ModuleValue first, ModuleValue other, Stmt imp, ControlFlowNode defn, Expr use, string attr
|
||||
) {
|
||||
import_time_imported_module(other, first, _) and
|
||||
import_time_transitive_import(first, imp, other) and
|
||||
import_time_module_use(first, other, use, attr) and
|
||||
exists(ImportTimeScope n, SsaVariable v |
|
||||
defn = v.getDefinition() and
|
||||
n = first.getScope() and
|
||||
v.getVariable().getScope() = n and
|
||||
v.getId() = attr
|
||||
|
|
||||
not defn.strictlyDominates(imp.getAnEntryNode())
|
||||
) and
|
||||
not exists(If i | i.isNameEqMain() and i.contains(use))
|
||||
import_time_imported_module(other, first, _) and
|
||||
import_time_transitive_import(first, imp, other) and
|
||||
import_time_module_use(first, other, use, attr) and
|
||||
exists(ImportTimeScope n, SsaVariable v |
|
||||
defn = v.getDefinition() and
|
||||
n = first.getScope() and
|
||||
v.getVariable().getScope() = n and
|
||||
v.getId() = attr
|
||||
|
|
||||
not defn.strictlyDominates(imp.getAnEntryNode())
|
||||
) and
|
||||
not exists(If i | i.isNameEqMain() and i.contains(use))
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ import Cyclic
|
||||
|
||||
from ModuleValue m1, ModuleValue m2, Stmt imp
|
||||
where
|
||||
imp.getEnclosingModule() = m1.getScope() and
|
||||
stmt_imports(imp) = m2 and
|
||||
circular_import(m1, m2) and
|
||||
m1 != m2 and
|
||||
// this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport
|
||||
not failing_import_due_to_cycle(m2, m1, _, _, _, _) and
|
||||
not exists(If i | i.isNameEqMain() and i.contains(imp))
|
||||
imp.getEnclosingModule() = m1.getScope() and
|
||||
stmt_imports(imp) = m2 and
|
||||
circular_import(m1, m2) and
|
||||
m1 != m2 and
|
||||
// this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport
|
||||
not failing_import_due_to_cycle(m2, m1, _, _, _, _) and
|
||||
not exists(If i | i.isNameEqMain() and i.contains(imp))
|
||||
select imp, "Import of module $@ begins an import cycle.", m2, m2.getName()
|
||||
|
||||
@@ -17,69 +17,69 @@ import python
|
||||
* and module `instead` should be used instead (or `instead = "no replacement"`)
|
||||
*/
|
||||
predicate deprecated_module(string name, string instead, int major, int minor) {
|
||||
name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5
|
||||
or
|
||||
name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5
|
||||
or
|
||||
name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5
|
||||
or
|
||||
name = "pre" and instead = "re" and major = 1 and minor = 5
|
||||
or
|
||||
name = "whrandom" and instead = "random" and major = 2 and minor = 1
|
||||
or
|
||||
name = "rfc822" and instead = "email" and major = 2 and minor = 3
|
||||
or
|
||||
name = "mimetools" and instead = "email" and major = 2 and minor = 3
|
||||
or
|
||||
name = "MimeWriter" and instead = "email" and major = 2 and minor = 3
|
||||
or
|
||||
name = "mimify" and instead = "email" and major = 2 and minor = 3
|
||||
or
|
||||
name = "rotor" and instead = "no replacement" and major = 2 and minor = 4
|
||||
or
|
||||
name = "statcache" and instead = "no replacement" and major = 2 and minor = 2
|
||||
or
|
||||
name = "mpz" and instead = "a third party" and major = 2 and minor = 2
|
||||
or
|
||||
name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3
|
||||
or
|
||||
name = "multifile" and instead = "email" and major = 2 and minor = 5
|
||||
or
|
||||
name = "sets" and instead = "builtins" and major = 2 and minor = 6
|
||||
or
|
||||
name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3
|
||||
or
|
||||
name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4
|
||||
or
|
||||
name = "macfs" and instead = "no replacement" and major = 2 and minor = 3
|
||||
or
|
||||
name = "md5" and instead = "hashlib" and major = 2 and minor = 5
|
||||
or
|
||||
name = "sha" and instead = "hashlib" and major = 2 and minor = 5
|
||||
name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5
|
||||
or
|
||||
name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5
|
||||
or
|
||||
name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5
|
||||
or
|
||||
name = "pre" and instead = "re" and major = 1 and minor = 5
|
||||
or
|
||||
name = "whrandom" and instead = "random" and major = 2 and minor = 1
|
||||
or
|
||||
name = "rfc822" and instead = "email" and major = 2 and minor = 3
|
||||
or
|
||||
name = "mimetools" and instead = "email" and major = 2 and minor = 3
|
||||
or
|
||||
name = "MimeWriter" and instead = "email" and major = 2 and minor = 3
|
||||
or
|
||||
name = "mimify" and instead = "email" and major = 2 and minor = 3
|
||||
or
|
||||
name = "rotor" and instead = "no replacement" and major = 2 and minor = 4
|
||||
or
|
||||
name = "statcache" and instead = "no replacement" and major = 2 and minor = 2
|
||||
or
|
||||
name = "mpz" and instead = "a third party" and major = 2 and minor = 2
|
||||
or
|
||||
name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3
|
||||
or
|
||||
name = "multifile" and instead = "email" and major = 2 and minor = 5
|
||||
or
|
||||
name = "sets" and instead = "builtins" and major = 2 and minor = 6
|
||||
or
|
||||
name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3
|
||||
or
|
||||
name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4
|
||||
or
|
||||
name = "macfs" and instead = "no replacement" and major = 2 and minor = 3
|
||||
or
|
||||
name = "md5" and instead = "hashlib" and major = 2 and minor = 5
|
||||
or
|
||||
name = "sha" and instead = "hashlib" and major = 2 and minor = 5
|
||||
}
|
||||
|
||||
string deprecation_message(string mod) {
|
||||
exists(int major, int minor | deprecated_module(mod, _, major, minor) |
|
||||
result =
|
||||
"The " + mod + " module was deprecated in version " + major.toString() + "." +
|
||||
minor.toString() + "."
|
||||
)
|
||||
exists(int major, int minor | deprecated_module(mod, _, major, minor) |
|
||||
result =
|
||||
"The " + mod + " module was deprecated in version " + major.toString() + "." +
|
||||
minor.toString() + "."
|
||||
)
|
||||
}
|
||||
|
||||
string replacement_message(string mod) {
|
||||
exists(string instead | deprecated_module(mod, instead, _, _) |
|
||||
result = " Use " + instead + " module instead." and not instead = "no replacement"
|
||||
or
|
||||
result = "" and instead = "no replacement"
|
||||
)
|
||||
exists(string instead | deprecated_module(mod, instead, _, _) |
|
||||
result = " Use " + instead + " module instead." and not instead = "no replacement"
|
||||
or
|
||||
result = "" and instead = "no replacement"
|
||||
)
|
||||
}
|
||||
|
||||
from ImportExpr imp, string name, string instead
|
||||
where
|
||||
name = imp.getName() and
|
||||
deprecated_module(name, instead, _, _) and
|
||||
not exists(Try try, ExceptStmt except | except = try.getAHandler() |
|
||||
except.getType().pointsTo(ClassValue::importError()) and
|
||||
except.containsInScope(imp)
|
||||
)
|
||||
name = imp.getName() and
|
||||
deprecated_module(name, instead, _, _) and
|
||||
not exists(Try try, ExceptStmt except | except = try.getAHandler() |
|
||||
except.getType().pointsTo(ClassValue::importError()) and
|
||||
except.containsInScope(imp)
|
||||
)
|
||||
select imp, deprecation_message(name) + replacement_message(name)
|
||||
|
||||
@@ -16,19 +16,19 @@ import semmle.python.filters.Tests
|
||||
|
||||
from ImportMember im, ModuleValue m, AttrNode store_attr, string name
|
||||
where
|
||||
m.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and
|
||||
im.getName() = name and
|
||||
/* Modification must be in a function, so it can occur during lifetime of the import value */
|
||||
store_attr.getScope() instanceof Function and
|
||||
/* variable resulting from import must have a long lifetime */
|
||||
not im.getScope() instanceof Function and
|
||||
store_attr.isStore() and
|
||||
store_attr.getObject(name).pointsTo(m) and
|
||||
/* Import not in same module as modification. */
|
||||
not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and
|
||||
/* Modification is not in a test */
|
||||
not store_attr.getScope().getScope*() instanceof TestScope
|
||||
m.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and
|
||||
im.getName() = name and
|
||||
/* Modification must be in a function, so it can occur during lifetime of the import value */
|
||||
store_attr.getScope() instanceof Function and
|
||||
/* variable resulting from import must have a long lifetime */
|
||||
not im.getScope() instanceof Function and
|
||||
store_attr.isStore() and
|
||||
store_attr.getObject(name).pointsTo(m) and
|
||||
/* Import not in same module as modification. */
|
||||
not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and
|
||||
/* Modification is not in a test */
|
||||
not store_attr.getScope().getScope*() instanceof TestScope
|
||||
select im,
|
||||
"Importing the value of '" + name +
|
||||
"' from $@ means that any change made to $@ will be not be observed locally.", m,
|
||||
"module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName()
|
||||
"Importing the value of '" + name +
|
||||
"' from $@ means that any change made to $@ will be not be observed locally.", m,
|
||||
"module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName()
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
import python
|
||||
|
||||
predicate shadowsImport(Variable l) {
|
||||
exists(Import i, Name shadow |
|
||||
shadow = i.getAName().getAsname() and
|
||||
shadow.getId() = l.getId() and
|
||||
i.getScope() = l.getScope().getScope*()
|
||||
)
|
||||
exists(Import i, Name shadow |
|
||||
shadow = i.getAName().getAsname() and
|
||||
shadow.getId() = l.getId() and
|
||||
i.getScope() = l.getScope().getScope*()
|
||||
)
|
||||
}
|
||||
|
||||
from Variable l, Name defn
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
import python
|
||||
|
||||
predicate import_and_import_from(Import i1, Import i2, Module m) {
|
||||
i1.getEnclosingModule() = i2.getEnclosingModule() and
|
||||
exists(ImportExpr e1, ImportExpr e2, ImportMember im |
|
||||
e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule()
|
||||
|
|
||||
e1.getName() = m.getName() and e2.getName() = m.getName()
|
||||
)
|
||||
i1.getEnclosingModule() = i2.getEnclosingModule() and
|
||||
exists(ImportExpr e1, ImportExpr e2, ImportMember im |
|
||||
e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule()
|
||||
|
|
||||
e1.getName() = m.getName() and e2.getName() = m.getName()
|
||||
)
|
||||
}
|
||||
|
||||
from Stmt i1, Stmt i2, Module m
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
import python
|
||||
|
||||
predicate modules_imports_itself(ImportingStmt i, ModuleValue m) {
|
||||
i.getEnclosingModule() = m.getScope() and
|
||||
m =
|
||||
max(string s, ModuleValue m_ |
|
||||
s = i.getAnImportedModuleName() and
|
||||
m_.importedAs(s)
|
||||
|
|
||||
m_ order by s.length()
|
||||
)
|
||||
i.getEnclosingModule() = m.getScope() and
|
||||
m =
|
||||
max(string s, ModuleValue m_ |
|
||||
s = i.getAnImportedModuleName() and
|
||||
m_.importedAs(s)
|
||||
|
|
||||
m_ order by s.length()
|
||||
)
|
||||
}
|
||||
|
||||
from ImportingStmt i, ModuleValue m
|
||||
|
||||
@@ -24,7 +24,7 @@ import Cyclic
|
||||
from ModuleValue m1, Stmt imp, ModuleValue m2, string attr, Expr use, ControlFlowNode defn
|
||||
where failing_import_due_to_cycle(m1, m2, imp, defn, use, attr)
|
||||
select use,
|
||||
"'" + attr + "' may not be defined if module $@ is imported before module $@, as the $@ of " +
|
||||
attr + " occurs after the cyclic $@ of " + m2.getName() + ".",
|
||||
// Arguments for the placeholders in the above message:
|
||||
m1, m1.getName(), m2, m2.getName(), defn, "definition", imp, "import"
|
||||
"'" + attr + "' may not be defined if module $@ is imported before module $@, as the $@ of " +
|
||||
attr + " occurs after the cyclic $@ of " + m2.getName() + ".",
|
||||
// Arguments for the placeholders in the above message:
|
||||
m1, m1.getName(), m2, m2.getName(), defn, "definition", imp, "import"
|
||||
|
||||
@@ -15,32 +15,32 @@ import python
|
||||
predicate is_simple_import(Import imp) { not exists(Attribute a | imp.contains(a)) }
|
||||
|
||||
predicate double_import(Import original, Import duplicate, Module m) {
|
||||
original != duplicate and
|
||||
is_simple_import(original) and
|
||||
is_simple_import(duplicate) and
|
||||
/* Imports import the same thing */
|
||||
exists(ImportExpr e1, ImportExpr e2 |
|
||||
e1.getName() = m.getName() and
|
||||
e2.getName() = m.getName() and
|
||||
e1 = original.getAName().getValue() and
|
||||
e2 = duplicate.getAName().getValue()
|
||||
) and
|
||||
original.getAName().getAsname().(Name).getId() = duplicate.getAName().getAsname().(Name).getId() and
|
||||
exists(Module enclosing |
|
||||
original.getScope() = enclosing and
|
||||
duplicate.getEnclosingModule() = enclosing and
|
||||
(
|
||||
/* Duplicate is not at top level scope */
|
||||
duplicate.getScope() != enclosing
|
||||
or
|
||||
/* Original dominates duplicate */
|
||||
original.getAnEntryNode().dominates(duplicate.getAnEntryNode())
|
||||
)
|
||||
original != duplicate and
|
||||
is_simple_import(original) and
|
||||
is_simple_import(duplicate) and
|
||||
/* Imports import the same thing */
|
||||
exists(ImportExpr e1, ImportExpr e2 |
|
||||
e1.getName() = m.getName() and
|
||||
e2.getName() = m.getName() and
|
||||
e1 = original.getAName().getValue() and
|
||||
e2 = duplicate.getAName().getValue()
|
||||
) and
|
||||
original.getAName().getAsname().(Name).getId() = duplicate.getAName().getAsname().(Name).getId() and
|
||||
exists(Module enclosing |
|
||||
original.getScope() = enclosing and
|
||||
duplicate.getEnclosingModule() = enclosing and
|
||||
(
|
||||
/* Duplicate is not at top level scope */
|
||||
duplicate.getScope() != enclosing
|
||||
or
|
||||
/* Original dominates duplicate */
|
||||
original.getAnEntryNode().dominates(duplicate.getAnEntryNode())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from Import original, Import duplicate, Module m
|
||||
where double_import(original, duplicate, m)
|
||||
select duplicate,
|
||||
"This import of module " + m.getName() + " is redundant, as it was previously imported $@.",
|
||||
original, "on line " + original.getLocation().getStartLine().toString()
|
||||
"This import of module " + m.getName() + " is redundant, as it was previously imported $@.",
|
||||
original, "on line " + original.getLocation().getStartLine().toString()
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
import python
|
||||
|
||||
predicate import_star(ImportStar imp, ModuleValue exporter) {
|
||||
exporter.importedAs(imp.getImportedModuleName())
|
||||
exporter.importedAs(imp.getImportedModuleName())
|
||||
}
|
||||
|
||||
predicate all_defined(ModuleValue exporter) {
|
||||
exporter.isBuiltin()
|
||||
or
|
||||
exporter.getScope().(ImportTimeScope).definesName("__all__")
|
||||
or
|
||||
exporter.getScope().getInitModule().(ImportTimeScope).definesName("__all__")
|
||||
exporter.isBuiltin()
|
||||
or
|
||||
exporter.getScope().(ImportTimeScope).definesName("__all__")
|
||||
or
|
||||
exporter.getScope().getInitModule().(ImportTimeScope).definesName("__all__")
|
||||
}
|
||||
|
||||
from ImportStar imp, ModuleValue exporter
|
||||
where import_star(imp, exporter) and not all_defined(exporter)
|
||||
select imp,
|
||||
"Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.",
|
||||
exporter, exporter.getName()
|
||||
"Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.",
|
||||
exporter, exporter.getName()
|
||||
|
||||
@@ -14,112 +14,112 @@ import python
|
||||
import Variables.Definition
|
||||
|
||||
predicate global_name_used(Module m, string name) {
|
||||
exists(Name u, GlobalVariable v |
|
||||
u.uses(v) and
|
||||
v.getId() = name and
|
||||
u.getEnclosingModule() = m
|
||||
)
|
||||
or
|
||||
// A use of an undefined class local variable, will use the global variable
|
||||
exists(Name u, LocalVariable v |
|
||||
u.uses(v) and
|
||||
v.getId() = name and
|
||||
u.getEnclosingModule() = m and
|
||||
not v.getScope().getEnclosingScope*() instanceof Function
|
||||
)
|
||||
exists(Name u, GlobalVariable v |
|
||||
u.uses(v) and
|
||||
v.getId() = name and
|
||||
u.getEnclosingModule() = m
|
||||
)
|
||||
or
|
||||
// A use of an undefined class local variable, will use the global variable
|
||||
exists(Name u, LocalVariable v |
|
||||
u.uses(v) and
|
||||
v.getId() = name and
|
||||
u.getEnclosingModule() = m and
|
||||
not v.getScope().getEnclosingScope*() instanceof Function
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a module has `__all__` but we don't understand it */
|
||||
predicate all_not_understood(Module m) {
|
||||
exists(GlobalVariable a | a.getId() = "__all__" and a.getScope() = m |
|
||||
// `__all__` is not defined as a simple list
|
||||
not m.declaredInAll(_)
|
||||
or
|
||||
// `__all__` is modified
|
||||
exists(Call c | c.getFunc().(Attribute).getObject() = a.getALoad())
|
||||
)
|
||||
exists(GlobalVariable a | a.getId() = "__all__" and a.getScope() = m |
|
||||
// `__all__` is not defined as a simple list
|
||||
not m.declaredInAll(_)
|
||||
or
|
||||
// `__all__` is modified
|
||||
exists(Call c | c.getFunc().(Attribute).getObject() = a.getALoad())
|
||||
)
|
||||
}
|
||||
|
||||
predicate imported_module_used_in_doctest(Import imp) {
|
||||
exists(string modname, string docstring |
|
||||
imp.getAName().getAsname().(Name).getId() = modname and
|
||||
// Look for doctests containing the patterns:
|
||||
// >>> …name…
|
||||
// ... …name…
|
||||
docstring = doctest_in_scope(imp.getScope()) and
|
||||
docstring.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.).*" + modname + "[\\s\\S]*")
|
||||
)
|
||||
exists(string modname, string docstring |
|
||||
imp.getAName().getAsname().(Name).getId() = modname and
|
||||
// Look for doctests containing the patterns:
|
||||
// >>> …name…
|
||||
// ... …name…
|
||||
docstring = doctest_in_scope(imp.getScope()) and
|
||||
docstring.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.).*" + modname + "[\\s\\S]*")
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string doctest_in_scope(Scope scope) {
|
||||
exists(StrConst doc |
|
||||
doc.getEnclosingModule() = scope and
|
||||
doc.isDocString() and
|
||||
result = doc.getText() and
|
||||
result.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.)[\\s\\S]*")
|
||||
)
|
||||
exists(StrConst doc |
|
||||
doc.getEnclosingModule() = scope and
|
||||
doc.isDocString() and
|
||||
result = doc.getText() and
|
||||
result.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.)[\\s\\S]*")
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string typehint_annotation_in_module(Module module_scope) {
|
||||
exists(StrConst annotation |
|
||||
annotation = any(Arguments a).getAnAnnotation().getASubExpression*()
|
||||
or
|
||||
annotation = any(AnnAssign a).getAnnotation().getASubExpression*()
|
||||
or
|
||||
annotation = any(FunctionExpr f).getReturns().getASubExpression*()
|
||||
|
|
||||
annotation.pointsTo(Value::forString(result)) and
|
||||
annotation.getEnclosingModule() = module_scope
|
||||
)
|
||||
exists(StrConst annotation |
|
||||
annotation = any(Arguments a).getAnAnnotation().getASubExpression*()
|
||||
or
|
||||
annotation = any(AnnAssign a).getAnnotation().getASubExpression*()
|
||||
or
|
||||
annotation = any(FunctionExpr f).getReturns().getASubExpression*()
|
||||
|
|
||||
annotation.pointsTo(Value::forString(result)) and
|
||||
annotation.getEnclosingModule() = module_scope
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string typehint_comment_in_file(File file) {
|
||||
exists(Comment typehint |
|
||||
file = typehint.getLocation().getFile() and
|
||||
result = typehint.getText() and
|
||||
result.matches("# type:%")
|
||||
)
|
||||
exists(Comment typehint |
|
||||
file = typehint.getLocation().getFile() and
|
||||
result = typehint.getText() and
|
||||
result.matches("# type:%")
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the imported alias `name` from `imp` is used in a typehint (in the same file as `imp`) */
|
||||
predicate imported_alias_used_in_typehint(Import imp, Variable name) {
|
||||
imp.getAName().getAsname().(Name).getVariable() = name and
|
||||
exists(File file, Module module_scope |
|
||||
module_scope = imp.getEnclosingModule() and
|
||||
file = module_scope.getFile()
|
||||
|
|
||||
// Look for type hints containing the patterns:
|
||||
// # type: …name…
|
||||
typehint_comment_in_file(file).regexpMatch("# type:.*" + name.getId() + ".*")
|
||||
or
|
||||
// Type hint is inside a string annotation, as needed for forward references
|
||||
typehint_annotation_in_module(module_scope).regexpMatch(".*\\b" + name.getId() + "\\b.*")
|
||||
)
|
||||
imp.getAName().getAsname().(Name).getVariable() = name and
|
||||
exists(File file, Module module_scope |
|
||||
module_scope = imp.getEnclosingModule() and
|
||||
file = module_scope.getFile()
|
||||
|
|
||||
// Look for type hints containing the patterns:
|
||||
// # type: …name…
|
||||
typehint_comment_in_file(file).regexpMatch("# type:.*" + name.getId() + ".*")
|
||||
or
|
||||
// Type hint is inside a string annotation, as needed for forward references
|
||||
typehint_annotation_in_module(module_scope).regexpMatch(".*\\b" + name.getId() + "\\b.*")
|
||||
)
|
||||
}
|
||||
|
||||
predicate unused_import(Import imp, Variable name) {
|
||||
imp.getAName().getAsname().(Name).getVariable() = name and
|
||||
not imp.getAnImportedModuleName() = "__future__" and
|
||||
not imp.getEnclosingModule().declaredInAll(name.getId()) and
|
||||
imp.getScope() = imp.getEnclosingModule() and
|
||||
not global_name_used(imp.getScope(), name.getId()) and
|
||||
// Imports in `__init__.py` are used to force module loading
|
||||
not imp.getEnclosingModule().isPackageInit() and
|
||||
// Name may be imported for use in epytext documentation
|
||||
not exists(Comment cmt | cmt.getText().matches("%L{" + name.getId() + "}%") |
|
||||
cmt.getLocation().getFile() = imp.getLocation().getFile()
|
||||
) and
|
||||
not name_acceptable_for_unused_variable(name) and
|
||||
// Assume that opaque `__all__` includes imported module
|
||||
not all_not_understood(imp.getEnclosingModule()) and
|
||||
not imported_module_used_in_doctest(imp) and
|
||||
not imported_alias_used_in_typehint(imp, name) and
|
||||
// Only consider import statements that actually point-to something (possibly an unknown module).
|
||||
// If this is not the case, it's likely that the import statement never gets executed.
|
||||
imp.getAName().getValue().pointsTo(_)
|
||||
imp.getAName().getAsname().(Name).getVariable() = name and
|
||||
not imp.getAnImportedModuleName() = "__future__" and
|
||||
not imp.getEnclosingModule().declaredInAll(name.getId()) and
|
||||
imp.getScope() = imp.getEnclosingModule() and
|
||||
not global_name_used(imp.getScope(), name.getId()) and
|
||||
// Imports in `__init__.py` are used to force module loading
|
||||
not imp.getEnclosingModule().isPackageInit() and
|
||||
// Name may be imported for use in epytext documentation
|
||||
not exists(Comment cmt | cmt.getText().matches("%L{" + name.getId() + "}%") |
|
||||
cmt.getLocation().getFile() = imp.getLocation().getFile()
|
||||
) and
|
||||
not name_acceptable_for_unused_variable(name) and
|
||||
// Assume that opaque `__all__` includes imported module
|
||||
not all_not_understood(imp.getEnclosingModule()) and
|
||||
not imported_module_used_in_doctest(imp) and
|
||||
not imported_alias_used_in_typehint(imp, name) and
|
||||
// Only consider import statements that actually point-to something (possibly an unknown module).
|
||||
// If this is not the case, it's likely that the import statement never gets executed.
|
||||
imp.getAName().getValue().pointsTo(_)
|
||||
}
|
||||
|
||||
from Stmt s, Variable name
|
||||
|
||||
Reference in New Issue
Block a user