mirror of
https://github.com/github/codeql.git
synced 2026-05-03 04:39:29 +02:00
Python: Autoformat Variables/*
This commit is contained in:
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
Reference in New Issue
Block a user