Python: Autoformat Variables/*

This commit is contained in:
Rasmus Wriedt Larsen
2019-12-20 15:08:20 +01:00
parent 25ab0ed20f
commit b8a9a353b8
17 changed files with 231 additions and 260 deletions

View File

@@ -1,26 +1,20 @@
import python
/**
* A control-flow node that defines a variable
*/
class Definition extends NameNode, DefinitionNode {
/**
* The variable defined by this control-flow node.
*/
Variable getVariable() {
this.defines(result)
}
Variable getVariable() { this.defines(result) }
/**
* The SSA variable corresponding to the current definition. Since SSA variables
* are only generated for definitions with at least one use, not all definitions
* will have an SSA variable.
*/
SsaVariable getSsaVariable() {
result.getDefinition() = this
}
SsaVariable getSsaVariable() { result.getDefinition() = this }
/**
* The index of this definition in its basic block.
@@ -42,9 +36,7 @@ class Definition extends NameNode, DefinitionNode {
}
/** Is this definition the first in its basic block for its variable? */
predicate isFirst() {
this.rankInBB(_, _) = 1
}
predicate isFirst() { this.rankInBB(_, _) = 1 }
/** Is this definition the last in its basic block for its variable? */
predicate isLast() {
@@ -66,8 +58,10 @@ class Definition extends NameNode, DefinitionNode {
this.getVariable().getScope() = this.getScope() and
// A call to locals() or vars() in the variable scope counts as a use
not exists(Function f, Call c, string locals_or_vars |
c.getScope() = f and this.getScope() = f and
c.getFunc().(Name).getId() = locals_or_vars |
c.getScope() = f and
this.getScope() = f and
c.getFunc().(Name).getId() = locals_or_vars
|
locals_or_vars = "locals" or locals_or_vars = "vars"
)
}
@@ -96,19 +90,14 @@ class Definition extends NameNode, DefinitionNode {
* We also ignore anything named "_", "empty", "unused" or "dummy"
*/
predicate isRelevant() {
exists(AstNode p |
p = this.getNode().getParentNode() |
exists(AstNode p | p = this.getNode().getParentNode() |
p instanceof Assign or p instanceof AugAssign or p instanceof Tuple
)
and
not name_acceptable_for_unused_variable(this.getVariable())
and
) and
not name_acceptable_for_unused_variable(this.getVariable()) and
/* Decorated classes and functions are used */
not exists(this.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator())
and
not exists(this.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and
not exists(this.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator())
}
}
/**
@@ -116,35 +105,30 @@ class Definition extends NameNode, DefinitionNode {
* definition of variable `v`. The relation is not transitive by default, so any
* observed transitivity will be caused by loops in the control-flow graph.
*/
private
predicate reaches_without_redef(Variable v, BasicBlock a, BasicBlock b) {
private predicate reaches_without_redef(Variable v, BasicBlock a, BasicBlock b) {
exists(Definition def | a.getASuccessor() = b |
def.getBasicBlock() = a and def.getVariable() = v and maybe_redefined(v)
) or
)
or
exists(BasicBlock mid | reaches_without_redef(v, a, mid) |
not exists(NameNode cfn | cfn.defines(v) |
cfn.getBasicBlock() = mid
) and
not exists(NameNode cfn | cfn.defines(v) | cfn.getBasicBlock() = mid) and
mid.getASuccessor() = b
)
}
private predicate maybe_redefined(Variable v) {
strictcount(Definition d | d.defines(v)) > 1
}
private predicate maybe_redefined(Variable v) { strictcount(Definition d | d.defines(v)) > 1 }
predicate name_acceptable_for_unused_variable(Variable var) {
exists(string name |
var.getId() = name |
name.regexpMatch("_+") or name = "empty" or
name.matches("%unused%") or name = "dummy" or
exists(string name | var.getId() = name |
name.regexpMatch("_+") or
name = "empty" or
name.matches("%unused%") or
name = "dummy" or
name.regexpMatch("__.*")
)
}
class ListComprehensionDeclaration extends ListComp {
Name getALeakedVariableUse() {
major_version() = 2 and
this.getIterationVariable(_).getId() = result.getId() and
@@ -153,8 +137,5 @@ class ListComprehensionDeclaration extends ListComp {
result.isUse()
}
Name getDefinition() {
result = this.getIterationVariable(0).getAStore()
}
Name getDefinition() { result = this.getIterationVariable(0).getAStore() }
}

View File

@@ -14,5 +14,3 @@ import python
from Global g
where not g.getScope() instanceof Module
select g, "Updating global variables except at module initialization is discouraged"

View File

@@ -14,4 +14,4 @@ import python
from Global g
where g.getScope() instanceof Module
select g, "Declaring '" + g.getAName() + "' as global at module-level is redundant."
select g, "Declaring '" + g.getAName() + "' as global at module-level is redundant."

View File

@@ -15,16 +15,17 @@ import Definition
from ListComprehensionDeclaration l, Name use, Name defn
where
use = l.getALeakedVariableUse() and
defn = l.getDefinition() and
l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and
/* Make sure we aren't in a loop, as the variable may be redefined */
not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and
not l.contains(use) and
not use.deletes(_) and
not exists(SsaVariable v |
v.getAUse() = use.getAFlowNode() and
not v.getDefinition().strictlyDominates(l.getAFlowNode())
)
select use, use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn, "list comprehension variable"
use = l.getALeakedVariableUse() and
defn = l.getDefinition() and
l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and
/* Make sure we aren't in a loop, as the variable may be redefined */
not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and
not l.contains(use) and
not use.deletes(_) and
not exists(SsaVariable v |
v.getAUse() = use.getAFlowNode() and
not v.getDefinition().strictlyDominates(l.getAFlowNode())
)
select use,
use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn,
"list comprehension variable"

View File

@@ -1,17 +1,19 @@
import python
private predicate empty_sequence(Expr e) {
exists(SsaVariable var | var.getAUse().getNode() = e | empty_sequence(var.getDefinition().getNode())) or
e instanceof List and not exists(e.(List).getAnElt()) or
e instanceof Tuple and not exists(e.(Tuple).getAnElt()) or
exists(SsaVariable var | var.getAUse().getNode() = e |
empty_sequence(var.getDefinition().getNode())
)
or
e instanceof List and not exists(e.(List).getAnElt())
or
e instanceof Tuple and not exists(e.(Tuple).getAnElt())
or
e.(StrConst).getText().length() = 0
}
/* This has the potential for refinement, but we err on the side of fewer false positives for now. */
private predicate probably_non_empty_sequence(Expr e) {
not empty_sequence(e)
}
private predicate probably_non_empty_sequence(Expr e) { not empty_sequence(e) }
/** A loop which probably defines v */
private Stmt loop_probably_defines(Variable v) {
@@ -24,8 +26,7 @@ private Stmt loop_probably_defines(Variable v) {
/** Holds if the variable used by `use` is probably defined in a loop */
predicate probably_defined_in_loop(Name use) {
exists(Stmt loop |
loop = loop_probably_defines(use.getVariable()) |
exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) |
loop.getAFlowNode().strictlyReaches(use.getAFlowNode())
)
}

View File

@@ -30,11 +30,12 @@ predicate capturing_looping_construct(CallableExpr capturing, AstNode loop, Vari
}
predicate escaping_capturing_looping_construct(CallableExpr capturing, AstNode loop, Variable var) {
capturing_looping_construct(capturing, loop, var)
and
capturing_looping_construct(capturing, loop, var) and
// Escapes if used out side of for loop or is a lambda in a comprehension
(
exists(Expr e, For forloop | forloop = loop and e.pointsTo(_, _, capturing) | not forloop.contains(e))
exists(Expr e, For forloop | forloop = loop and e.pointsTo(_, _, capturing) |
not forloop.contains(e)
)
or
loop.(Comp).getElt() = capturing
or

View File

@@ -15,13 +15,17 @@ import python
import Definition
predicate multiply_defined(AstNode asgn1, AstNode asgn2, Variable v) {
/* Must be redefined on all possible paths in the CFG corresponding to the original source.
/*
* Must be redefined on all possible paths in the CFG corresponding to the original source.
* For example, splitting may create a path where `def` is unconditionally redefined, even though
* it is not in the original source. */
* it is not in the original source.
*/
forex(Definition def, Definition redef |
def.getVariable() = v and
def = asgn1.getAFlowNode() and
redef = asgn2.getAFlowNode() |
redef = asgn2.getAFlowNode()
|
def.isUnused() and
def.getARedef() = redef and
def.isRelevant()
@@ -29,15 +33,21 @@ predicate multiply_defined(AstNode asgn1, AstNode asgn2, Variable v) {
}
predicate simple_literal(Expr e) {
e.(Num).getN() = "0" or
e instanceof NameConstant or
e instanceof List and not exists(e.(List).getAnElt()) or
e instanceof Tuple and not exists(e.(Tuple).getAnElt()) or
e instanceof Dict and not exists(e.(Dict).getAKey()) or
e.(Num).getN() = "0"
or
e instanceof NameConstant
or
e instanceof List and not exists(e.(List).getAnElt())
or
e instanceof Tuple and not exists(e.(Tuple).getAnElt())
or
e instanceof Dict and not exists(e.(Dict).getAKey())
or
e.(StrConst).getText() = ""
}
/** A multiple definition is 'uninteresting' if it sets a variable to a
/**
* A multiple definition is 'uninteresting' if it sets a variable to a
* simple literal before reassigning it.
* x = None
* if cond:
@@ -46,16 +56,14 @@ predicate simple_literal(Expr e) {
* x = value2
*/
predicate uninteresting_definition(AstNode asgn1) {
exists(AssignStmt a |
a.getATarget() = asgn1 |
simple_literal(a.getValue())
)
exists(AssignStmt a | a.getATarget() = asgn1 | simple_literal(a.getValue()))
}
from AstNode asgn1, AstNode asgn2, Variable v
where
multiply_defined(asgn1, asgn2, v) and
forall(Name el | el = asgn1.getParentNode().(Tuple).getAnElt() | multiply_defined(el, _, _)) and
not uninteresting_definition(asgn1)
select asgn1, "This assignment to '" + v.getId() + "' is unnecessary as it is redefined $@ before this value is used.", asgn2 as t, "here"
select asgn1,
"This assignment to '" + v.getId() +
"' is unnecessary as it is redefined $@ before this value is used.", asgn2 as t, "here"

View File

@@ -44,9 +44,10 @@ predicate white_list(string name) {
}
predicate shadows(Name d, string name, Scope scope, int line) {
exists(LocalVariable l | d.defines(l) and
l.getId() = name and
exists(Builtin::builtin(l.getId()))
exists(LocalVariable l |
d.defines(l) and
l.getId() = name and
exists(Builtin::builtin(l.getId()))
) and
scope instanceof Function and
d.getScope() = scope and
@@ -58,7 +59,8 @@ predicate shadows(Name d, string name, Scope scope, int line) {
predicate first_shadowing_definition(Name d, string name) {
exists(int first, Scope scope |
shadows(d, name, scope, first) and
first = min(int line | shadows(_, name, scope, line)))
first = min(int line | shadows(_, name, scope, line))
)
}
from Name d, string name

View File

@@ -17,27 +17,24 @@ import Shadowing
import semmle.python.types.Builtins
predicate shadows(Name d, GlobalVariable g, Scope scope, int line) {
exists(LocalVariable l | d.defines(l) and l.getId() = g.getId() and
scope instanceof Function and g.getScope() = scope.getScope() and
exists(LocalVariable l |
d.defines(l) and
l.getId() = g.getId() and
scope instanceof Function and
g.getScope() = scope.getScope() and
not exists(Import il, Import ig, Name gd | il.contains(d) and gd.defines(g) and ig.contains(gd)) and
not exists(Assign a | a.getATarget() = d and a.getValue() = g.getAnAccess())
) and
not exists(Builtin::builtin(g.getId())) and
d.getScope() = scope and
d.getLocation().getStartLine() = line and
exists(Name defn | defn.defines(g) |
not exists(If i | i.isNameEqMain() |
i.contains(defn)
)
) and
exists(Name defn | defn.defines(g) | not exists(If i | i.isNameEqMain() | i.contains(defn))) and
not optimizing_parameter(d)
}
/* pytest dynamically populates its namespace so, we cannot look directly for the pytest.fixture function */
AttrNode pytest_fixture_attr() {
exists(ModuleValue pytest |
result.getObject("fixture").pointsTo(pytest)
)
exists(ModuleValue pytest | result.getObject("fixture").pointsTo(pytest))
}
Value pytest_fixture() {
@@ -45,23 +42,30 @@ Value pytest_fixture() {
call.getFunction() = pytest_fixture_attr()
or
call.getFunction().(CallNode).getFunction() = pytest_fixture_attr()
|
|
call.pointsTo(result)
)
}
/* pytest fixtures require that the parameter name is also a global */
predicate assigned_pytest_fixture(GlobalVariable v) {
exists(NameNode def | def.defines(v) and def.(DefinitionNode).getValue().pointsTo(pytest_fixture()))
exists(NameNode def |
def.defines(v) and def.(DefinitionNode).getValue().pointsTo(pytest_fixture())
)
}
predicate first_shadowing_definition(Name d, GlobalVariable g) {
exists(int first, Scope scope |
shadows(d, g, scope, first) and
first = min(int line | shadows(_, g, scope, line)))
first = min(int line | shadows(_, g, scope, line))
)
}
from Name d, GlobalVariable g, Name def
where first_shadowing_definition(d, g) and not exists(Name n | n.deletes(g)) and
def.defines(g) and not assigned_pytest_fixture(g) and not g.getId() = "_"
where
first_shadowing_definition(d, g) and
not exists(Name n | n.deletes(g)) and
def.defines(g) and
not assigned_pytest_fixture(g) and
not g.getId() = "_"
select d, "Local variable '" + g.getId() + "' shadows a global variable defined $@.", def, "here"

View File

@@ -1,13 +1,14 @@
import python
/* Parameters with defaults that are used as an optimization.
/*
* Parameters with defaults that are used as an optimization.
* E.g. def f(x, len=len): ...
* (In general, this kind of optimization is not recommended.)
*/
predicate optimizing_parameter(Parameter p) {
exists(string name, Name glob |
p.getDefault() = glob |
glob.getId() = name and
exists(string name, Name glob | p.getDefault() = glob |
glob.getId() = name and
p.asName().getId() = name
)
}

View File

@@ -26,18 +26,13 @@ predicate is_increment(Stmt s) {
)
}
predicate counting_loop(For f) {
is_increment(f.getAStmt())
}
predicate counting_loop(For f) { is_increment(f.getAStmt()) }
predicate empty_loop(For f) {
not exists(f.getStmt(1)) and f.getStmt(0) instanceof Pass
}
predicate empty_loop(For f) { not exists(f.getStmt(1)) and f.getStmt(0) instanceof Pass }
predicate one_item_only(For f) {
not exists(Continue c | f.contains(c)) and
exists(Stmt s |
s = f.getBody().getLastItem() |
exists(Stmt s | s = f.getBody().getLastItem() |
s instanceof Return
or
s instanceof Break
@@ -55,16 +50,13 @@ predicate points_to_call_to_range(ControlFlowNode f) {
)
or
/* In case points-to fails due to 'from six.moves import range' or similar. */
exists(string range |
f.getNode().(Call).getFunc().(Name).getId() = range |
exists(string range | f.getNode().(Call).getFunc().(Name).getId() = range |
range = "range" or range = "xrange"
)
or
/* Handle list(range(...)) and list(list(range(...))) */
(
f.(CallNode).pointsTo().getClass() = ClassValue::list() and
points_to_call_to_range(f.(CallNode).getArg(0))
)
f.(CallNode).pointsTo().getClass() = ClassValue::list() and
points_to_call_to_range(f.(CallNode).getArg(0))
}
/** Whether n is a use of a variable that is a not effectively a constant. */
@@ -74,52 +66,50 @@ predicate use_of_non_constant(Name n) {
/* use is local */
not n.getScope() instanceof Module and
/* variable is not global */
not var.getScope() instanceof Module |
not var.getScope() instanceof Module
|
/* Defined more than once (dynamically) */
strictcount(Name def | def.defines(var)) > 1 or
exists(For f, Name def | f.contains(def) and def.defines(var)) or
strictcount(Name def | def.defines(var)) > 1
or
exists(For f, Name def | f.contains(def) and def.defines(var))
or
exists(While w, Name def | w.contains(def) and def.defines(var))
)
}
/** Whether loop body is implicitly repeating something N times.
/**
* Whether loop body is implicitly repeating something N times.
* E.g. queue.add(None)
*/
predicate implicit_repeat(For f) {
not exists(f.getStmt(1)) and
exists(ImmutableLiteral imm |
f.getStmt(0).contains(imm)
) and
exists(ImmutableLiteral imm | f.getStmt(0).contains(imm)) and
not exists(Name n | f.getBody().contains(n) and use_of_non_constant(n))
}
/** Get the CFG node for the iterable relating to the for-statement `f` in a comprehension.
/**
* Get the CFG node for the iterable relating to the for-statement `f` in a comprehension.
* The for-statement `f` is the artificial for-statement in a comprehension
* and the result is the iterable in that comprehension.
* E.g. gets `x` from `{ y for y in x }`.
*/
ControlFlowNode get_comp_iterable(For f) {
exists(Comp c |
c.getFunction().getStmt(0) = f |
c.getAFlowNode().getAPredecessor() = result
)
exists(Comp c | c.getFunction().getStmt(0) = f | c.getAFlowNode().getAPredecessor() = result)
}
from For f, Variable v, string msg
where f.getTarget() = v.getAnAccess() and
not f.getAStmt().contains(v.getAnAccess()) and
not points_to_call_to_range(f.getIter().getAFlowNode()) and
not points_to_call_to_range(get_comp_iterable(f)) and
not name_acceptable_for_unused_variable(v) and
not f.getScope().getName() = "genexpr" and
not empty_loop(f) and
not one_item_only(f) and
not counting_loop(f) and
not implicit_repeat(f) and
if exists(Name del | del.deletes(v) and f.getAStmt().contains(del)) then
msg = "' is deleted, but not used, in the loop body."
else
msg = "' is not used in the loop body."
where
f.getTarget() = v.getAnAccess() and
not f.getAStmt().contains(v.getAnAccess()) and
not points_to_call_to_range(f.getIter().getAFlowNode()) and
not points_to_call_to_range(get_comp_iterable(f)) and
not name_acceptable_for_unused_variable(v) and
not f.getScope().getName() = "genexpr" and
not empty_loop(f) and
not one_item_only(f) and
not counting_loop(f) and
not implicit_repeat(f) and
if exists(Name del | del.deletes(v) and f.getAStmt().contains(del))
then msg = "' is deleted, but not used, in the loop body."
else msg = "' is not used in the loop body."
select f, "For loop variable '" + v.getId() + msg

View File

@@ -4,9 +4,7 @@ import semmle.python.security.TaintTracking
/** Marker for "uninitialized". */
class Uninitialized extends TaintKind {
Uninitialized() { this = "undefined" }
}
private predicate loop_entry_variables(EssaVariable pred, EssaVariable succ) {
@@ -26,7 +24,8 @@ private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) {
)
}
/** Since any use of a local will raise if it is uninitialized, then
/**
* Since any use of a local will raise if it is uninitialized, then
* any use dominated by another use of the same variable must be defined, or is unreachable.
*/
private predicate first_use(NameNode u, EssaVariable v) {
@@ -37,19 +36,17 @@ private predicate first_use(NameNode u, EssaVariable v) {
)
}
/* Holds if `call` is a call of the form obj.method_name(...) and
/**
* Holds if `call` is a call of the form obj.method_name(...) and
* there is a function called `method_name` that can exit the program.
*/
private predicate maybe_call_to_exiting_function(CallNode call) {
exists(FunctionValue exits, string name |
exits.neverReturns() and exits.getName() = name
|
exists(FunctionValue exits, string name | exits.neverReturns() and exits.getName() = name |
call.getFunction().(NameNode).getId() = name or
call.getFunction().(AttrNode).getName() = name
)
}
predicate exitFunctionGuardedEdge(EssaVariable pred, EssaVariable succ) {
exists(CallNode exit_call |
succ.(PhiFunction).getInput(exit_call.getBasicBlock()) = pred and
@@ -58,17 +55,15 @@ predicate exitFunctionGuardedEdge(EssaVariable pred, EssaVariable succ) {
}
class UninitializedConfig extends TaintTracking::Configuration {
UninitializedConfig() {
this = "Unitialized local config"
}
UninitializedConfig() { this = "Unitialized local config" }
override predicate isSource(DataFlow::Node source, TaintKind kind) {
kind instanceof Uninitialized and
exists(EssaVariable var |
source.asVariable() = var and
var.getSourceVariable() instanceof FastLocalVariable and
not var.getSourceVariable().(Variable).escapes() |
not var.getSourceVariable().(Variable).escapes()
|
var instanceof ScopeEntryDefinition
or
var instanceof DeletionDefinition
@@ -110,12 +105,13 @@ class UninitializedConfig extends TaintTracking::Configuration {
}
override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) {
/* If we are guaranteed to iterate over a loop at least once, then we can prune any edges that
/*
* If we are guaranteed to iterate over a loop at least once, then we can prune any edges that
* don't pass through the body.
*/
loop_entry_variables(src.asVariable(), dest.asVariable())
or
exitFunctionGuardedEdge(src.asVariable(), dest.asVariable())
}
}

View File

@@ -17,12 +17,13 @@ import semmle.python.pointsto.PointsTo
predicate guarded_against_name_error(Name u) {
exists(Try t | t.getBody().getAnItem().contains(u) |
t.getAHandler().getType().(Name).getId() = "NameError"
)
t.getAHandler().getType().(Name).getId() = "NameError"
)
or
exists(ConditionBlock guard, BasicBlock controlled, Call globals |
guard.getLastNode().getNode().contains(globals) or
guard.getLastNode().getNode() = globals |
guard.getLastNode().getNode() = globals
|
globals.getFunc().(Name).getId() = "globals" and
guard.controls(controlled, _) and
controlled.contains(u.getAFlowNode())
@@ -30,66 +31,59 @@ predicate guarded_against_name_error(Name u) {
}
predicate contains_unknown_import_star(Module m) {
exists(ImportStar imp | imp.getScope() = m |
exists(ModuleValue imported | imported.importedAs(imp.getImportedModuleName()) and imported.isAbsent())
or
exists(ImportStar imp | imp.getScope() = m |
exists(ModuleValue imported |
imported.importedAs(imp.getImportedModuleName()) |
imported.importedAs(imp.getImportedModuleName()) and imported.isAbsent()
)
or
exists(ModuleValue imported | imported.importedAs(imp.getImportedModuleName()) |
not imported.hasCompleteExportInfo()
)
)
}
predicate undefined_use_in_function(Name u) {
exists(Function f | u.getScope().getScope*() = f and
exists(Function f |
u.getScope().getScope*() = f and
// Either function is a method or inner function or it is live at the end of the module scope
(not f.getScope() = u.getEnclosingModule() or u.getEnclosingModule().(ImportTimeScope).definesName(f.getName()))
and
(
not f.getScope() = u.getEnclosingModule() or
u.getEnclosingModule().(ImportTimeScope).definesName(f.getName())
) and
// There is a use, but not a definition of this global variable in the function or enclosing scope
exists(GlobalVariable v | u.uses(v) |
not exists(Assign a, Scope defnScope |
a.getATarget() = v.getAnAccess() and a.getScope() = defnScope |
defnScope = f or
a.getATarget() = v.getAnAccess() and a.getScope() = defnScope
|
defnScope = f
or
// Exclude modules as that case is handled more precisely below.
(defnScope = f.getScope().getScope*() and not defnScope instanceof Module)
defnScope = f.getScope().getScope*() and not defnScope instanceof Module
)
)
)
and
not ((ImportTimeScope)u.getEnclosingModule()).definesName(u.getId())
and
not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId()))
and
not globallyDefinedName(u.getId())
and
not exists(SsaVariable var | var.getAUse().getNode() = u and not var.maybeUndefined())
and
not guarded_against_name_error(u)
and
) and
not u.getEnclosingModule().(ImportTimeScope).definesName(u.getId()) and
not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and
not globallyDefinedName(u.getId()) and
not exists(SsaVariable var | var.getAUse().getNode() = u and not var.maybeUndefined()) and
not guarded_against_name_error(u) and
not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__")
}
predicate undefined_use_in_class_or_module(Name u) {
exists(GlobalVariable v | u.uses(v))
and
not exists(Function f | u.getScope().getScope*() = f)
and
exists(SsaVariable var | var.getAUse().getNode() = u | var.maybeUndefined())
and
not guarded_against_name_error(u)
and
not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId()))
and
not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__")
and
exists(GlobalVariable v | u.uses(v)) and
not exists(Function f | u.getScope().getScope*() = f) and
exists(SsaVariable var | var.getAUse().getNode() = u | var.maybeUndefined()) and
not guarded_against_name_error(u) and
not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and
not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") and
not globallyDefinedName(u.getId())
}
predicate use_of_exec(Module m) {
exists(Exec exec | exec.getScope() = m)
or
exists(CallNode call, FunctionValue exec |
exec.getACall() = call and call.getScope() = m |
exists(CallNode call, FunctionValue exec | exec.getACall() = call and call.getScope() = m |
exec = Value::named("exec") or
exec = Value::named("execfile")
)
@@ -106,7 +100,7 @@ predicate undefined_use(Name u) {
not use_of_exec(u.getEnclosingModule()) and
not exists(u.getVariable().getAStore()) and
not u.pointsTo(_) and
not probably_defined_in_loop(u)
not probably_defined_in_loop(u)
}
private predicate first_use_in_a_block(Name use) {
@@ -117,8 +111,7 @@ private predicate first_use_in_a_block(Name use) {
predicate first_undefined_use(Name use) {
undefined_use(use) and
exists(GlobalVariable v |
v.getALoad() = use |
exists(GlobalVariable v | v.getALoad() = use |
first_use_in_a_block(use) and
not exists(ControlFlowNode other |
other.getNode() = v.getALoad() and
@@ -129,4 +122,4 @@ predicate first_undefined_use(Name use) {
from Name u
where first_undefined_use(u)
select u, "This use of global variable '" + u.getId() + "' may be undefined."
select u, "This use of global variable '" + u.getId() + "' may be undefined."

View File

@@ -14,7 +14,6 @@ import python
import Variables.MonkeyPatched
/* Local variable part */
predicate initialized_as_local(PlaceHolder use) {
exists(SsaVariable l, Function f | f = use.getScope() and l.getAUse() = use.getAFlowNode() |
l.getVariable() instanceof LocalVariable and
@@ -23,34 +22,25 @@ predicate initialized_as_local(PlaceHolder use) {
}
/* Not a template member */
Class enclosing_class(PlaceHolder use) {
result.getAMethod() = use.getScope()
}
Class enclosing_class(PlaceHolder use) { result.getAMethod() = use.getScope() }
predicate template_attribute(PlaceHolder use) {
exists(ImportTimeScope cls |
cls = enclosing_class(use) |
cls.definesName(use.getId())
)
exists(ImportTimeScope cls | cls = enclosing_class(use) | cls.definesName(use.getId()))
}
/* Global Stuff */
predicate not_a_global(PlaceHolder use) {
not exists(PythonModuleObject mo | mo.hasAttribute(use.getId()) and mo.getModule() = use.getEnclosingModule())
and
not exists(PythonModuleObject mo |
mo.hasAttribute(use.getId()) and mo.getModule() = use.getEnclosingModule()
) and
not globallyDefinedName(use.getId()) and
not monkey_patched_builtin(use.getId()) and
not globallyDefinedName(use.getId())
}
from PlaceHolder p
where
not initialized_as_local(p) and
not template_attribute(p) and
not_a_global(p)
select p, "This use of place-holder variable '" + p.getId() + "' may be undefined"
where
not initialized_as_local(p) and
not template_attribute(p) and
not_a_global(p)
select p, "This use of place-holder variable '" + p.getId() + "' may be undefined"

View File

@@ -15,13 +15,10 @@ import Undefined
import semmle.python.pointsto.PointsTo
predicate uninitialized_local(NameNode use) {
exists(FastLocalVariable local |
use.uses(local) or use.deletes(local) |
not local.escapes()
)
and
exists(FastLocalVariable local | use.uses(local) or use.deletes(local) | not local.escapes()) and
(
any(Uninitialized uninit).taints(use) and PointsToInternal::reachableBlock(use.getBasicBlock(), _)
any(Uninitialized uninit).taints(use) and
PointsToInternal::reachableBlock(use.getBasicBlock(), _)
or
not exists(EssaVariable var | var.getASourceUse() = use)
)
@@ -34,7 +31,6 @@ predicate explicitly_guarded(NameNode u) {
)
}
from NameNode u
where uninitialized_local(u) and not explicitly_guarded(u)
select u.getNode(), "Local variable '" + u.getId() + "' may be used before it is initialized."

View File

@@ -14,36 +14,39 @@
import python
import Definition
/** Whether the module contains an __all__ definition,
* but it is more complex than a simple list of strings */
/**
* Whether the module contains an __all__ definition,
* but it is more complex than a simple list of strings
*/
predicate complex_all(Module m) {
exists(Assign a, GlobalVariable all |
a.defines(all) and a.getScope() = m and all.getId() = "__all__" |
not a.getValue() instanceof List or
exists(Expr e |
e = a.getValue().(List).getAnElt() |
not e instanceof StrConst
)
exists(Assign a, GlobalVariable all |
a.defines(all) and a.getScope() = m and all.getId() = "__all__"
|
not a.getValue() instanceof List
or
exists(Expr e | e = a.getValue().(List).getAnElt() | not e instanceof StrConst)
)
or
exists(Call c, GlobalVariable all |
c.getFunc().(Attribute).getObject() = all.getALoad() and
c.getScope() = m and all.getId() = "__all__"
c.getScope() = m and
all.getId() = "__all__"
)
}
predicate unused_global(Name unused, GlobalVariable v) {
not exists(ImportingStmt is | is.contains(unused)) and
forex(DefinitionNode defn |
defn.getNode() = unused |
forex(DefinitionNode defn | defn.getNode() = unused |
not defn.getValue().getNode() instanceof FunctionExpr and
not defn.getValue().getNode() instanceof ClassExpr and
not exists(Name u |
not exists(Name u |
// A use of the variable
u.uses(v) |
u.uses(v)
|
// That is reachable from this definition, directly
defn.strictlyReaches(u.getAFlowNode())
or // indirectly
or
// indirectly
defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope()
) and
not unused.getEnclosingModule().getAnExport() = v.getId() and
@@ -56,7 +59,8 @@ predicate unused_global(Name unused, GlobalVariable v) {
}
from Name unused, GlobalVariable v
where unused_global(unused, v) and
// If unused is part of a tuple, count it as unused if all elements of that tuple are unused.
forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_global(el, _))
where
unused_global(unused, v) and
// If unused is part of a tuple, count it as unused if all elements of that tuple are unused.
forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_global(el, _))
select unused, "The global variable '" + v.getId() + "' is not used."

View File

@@ -12,13 +12,14 @@
import python
import Definition
predicate unused_parameter(FunctionValue f, LocalVariable v) {
v.isParameter() and
v.getScope() = f.getScope() and
not name_acceptable_for_unused_variable(v) and
not exists(NameNode u | u.uses(v)) and
not exists(Name inner, LocalVariable iv | inner.uses(iv) and iv.getId() = v.getId() and inner.getScope().getScope() = v.getScope())
v.isParameter() and
v.getScope() = f.getScope() and
not name_acceptable_for_unused_variable(v) and
not exists(NameNode u | u.uses(v)) and
not exists(Name inner, LocalVariable iv |
inner.uses(iv) and iv.getId() = v.getId() and inner.getScope().getScope() = v.getScope()
)
}
predicate is_abstract(FunctionValue func) {
@@ -26,6 +27,10 @@ predicate is_abstract(FunctionValue func) {
}
from PythonFunctionValue f, LocalVariable v
where v.getId() != "self" and unused_parameter(f, v) and not f.isOverridingMethod() and not f.isOverriddenMethod() and
not is_abstract(f)
where
v.getId() != "self" and
unused_parameter(f, v) and
not f.isOverridingMethod() and
not f.isOverriddenMethod() and
not is_abstract(f)
select f, "The parameter '" + v.getId() + "' is never used."