mirror of
https://github.com/github/codeql.git
synced 2026-05-03 04:39:29 +02:00
Python: Autoformat statements
This commit is contained in:
@@ -26,7 +26,5 @@ where
|
||||
value = test.(NameConstant).toString()
|
||||
) and
|
||||
/* Exclude asserts appearing at the end of a chain of `elif`s */
|
||||
not exists(If i |
|
||||
i.getElif().getAnOrelse() = a
|
||||
)
|
||||
not exists(If i | i.getElif().getAnOrelse() = a)
|
||||
select a, "Assert of literal constant " + value + "."
|
||||
|
||||
@@ -14,11 +14,14 @@
|
||||
import python
|
||||
|
||||
from Assert a, string b, string non
|
||||
where a.getTest() instanceof Tuple and
|
||||
(if exists(((Tuple)a.getTest()).getAnElt()) then
|
||||
(b = "True" and non = "non-")
|
||||
else
|
||||
(b = "False" and non = "")
|
||||
)
|
||||
where
|
||||
a.getTest() instanceof Tuple and
|
||||
(
|
||||
if exists(a.getTest().(Tuple).getAnElt())
|
||||
then (
|
||||
b = "True" and non = "non-"
|
||||
) else (
|
||||
b = "False" and non = ""
|
||||
)
|
||||
)
|
||||
select a, "Assertion of " + non + "empty tuple is always " + b + "."
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
import python
|
||||
|
||||
from Stmt s, string kind
|
||||
where
|
||||
s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s))
|
||||
or
|
||||
s instanceof Break and kind = "break" and
|
||||
exists(Try t | t.getFinalbody().contains(s) |
|
||||
not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop))
|
||||
and
|
||||
not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop))
|
||||
)
|
||||
where
|
||||
s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s))
|
||||
or
|
||||
s instanceof Break and
|
||||
kind = "break" and
|
||||
exists(Try t | t.getFinalbody().contains(s) |
|
||||
not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop)) and
|
||||
not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop))
|
||||
)
|
||||
select s, "'" + kind + "' in a finally block will swallow any exceptions raised."
|
||||
|
||||
@@ -14,19 +14,22 @@
|
||||
import python
|
||||
|
||||
from Expr e, Location l, string kind, string what
|
||||
where e.isParenthesized() and
|
||||
not e instanceof Tuple and
|
||||
(
|
||||
exists(If i | i.getTest() = e) and kind = "if" and what = "condition"
|
||||
or
|
||||
exists(While w | w.getTest() = e) and kind = "while" and what = "condition"
|
||||
or
|
||||
exists(Return r | r.getValue() = e) and kind = "return" and what = "value"
|
||||
or
|
||||
exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and kind = "assert" and what = "test"
|
||||
)
|
||||
and
|
||||
// These require parentheses
|
||||
(not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and
|
||||
l = e.getLocation() and l.getStartLine() = l.getEndLine()
|
||||
where
|
||||
e.isParenthesized() and
|
||||
not e instanceof Tuple and
|
||||
(
|
||||
exists(If i | i.getTest() = e) and kind = "if" and what = "condition"
|
||||
or
|
||||
exists(While w | w.getTest() = e) and kind = "while" and what = "condition"
|
||||
or
|
||||
exists(Return r | r.getValue() = e) and kind = "return" and what = "value"
|
||||
or
|
||||
exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and
|
||||
kind = "assert" and
|
||||
what = "test"
|
||||
) and
|
||||
// These require parentheses
|
||||
(not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and
|
||||
l = e.getLocation() and
|
||||
l.getStartLine() = l.getEndLine()
|
||||
select e, "Parenthesized " + what + " in '" + kind + "' statement."
|
||||
|
||||
@@ -15,16 +15,15 @@
|
||||
|
||||
import python
|
||||
|
||||
|
||||
predicate is_condition(Expr cond) {
|
||||
exists(If i | i.getTest() = cond) or
|
||||
exists(IfExp ie | ie.getTest() = cond)
|
||||
exists(If i | i.getTest() = cond) or
|
||||
exists(IfExp ie | ie.getTest() = cond)
|
||||
}
|
||||
|
||||
/* Treat certain unmodified builtins as constants as well. */
|
||||
predicate effective_constant(Name cond) {
|
||||
exists(GlobalVariable var | var = cond.getVariable() and not exists(NameNode f | f.defines(var)) |
|
||||
var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented"
|
||||
var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -34,9 +33,10 @@ predicate test_makes_code_unreachable(Expr cond) {
|
||||
exists(While w | w.getTest() = cond and w.getStmt(0).isUnreachable())
|
||||
}
|
||||
|
||||
|
||||
from Expr cond
|
||||
where is_condition(cond) and (cond.isConstant() or effective_constant(cond)) and
|
||||
/* Ignore cases where test makes code unreachable, as that is handled in different query */
|
||||
not test_makes_code_unreachable(cond)
|
||||
where
|
||||
is_condition(cond) and
|
||||
(cond.isConstant() or effective_constant(cond)) and
|
||||
/* Ignore cases where test makes code unreachable, as that is handled in different query */
|
||||
not test_makes_code_unreachable(cond)
|
||||
select cond, "Testing a constant will always give the same result."
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
* @precision medium
|
||||
* @id py/missing-docstring
|
||||
*/
|
||||
/* NOTE: precision of 'medium' reflects the lack of precision in the underlying rule.
|
||||
|
||||
/*
|
||||
* NOTE: precision of 'medium' reflects the lack of precision in the underlying rule.
|
||||
* Do we care whether a function has a docstring? That often depends on the reader of that docstring.
|
||||
*/
|
||||
|
||||
@@ -18,25 +20,26 @@ import python
|
||||
predicate needs_docstring(Scope s) {
|
||||
s.isPublic() and
|
||||
(
|
||||
not s instanceof Function
|
||||
or
|
||||
function_needs_docstring(s)
|
||||
not s instanceof Function
|
||||
or
|
||||
function_needs_docstring(s)
|
||||
)
|
||||
}
|
||||
|
||||
predicate function_needs_docstring(Function f) {
|
||||
not exists(FunctionObject fo, FunctionObject base | fo.overrides(base) and fo.getFunction() = f |
|
||||
not function_needs_docstring(base.getFunction())) and
|
||||
not function_needs_docstring(base.getFunction())
|
||||
) and
|
||||
f.getName() != "lambda" and
|
||||
(f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2
|
||||
and not exists(PythonPropertyObject p |
|
||||
(f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 and
|
||||
not exists(PythonPropertyObject p |
|
||||
p.getGetter().getFunction() = f or
|
||||
p.getSetter().getFunction() = f
|
||||
)
|
||||
}
|
||||
|
||||
string scope_type(Scope s) {
|
||||
result = "Module" and s instanceof Module and not ((Module)s).isPackage()
|
||||
result = "Module" and s instanceof Module and not s.(Module).isPackage()
|
||||
or
|
||||
result = "Class" and s instanceof Class
|
||||
or
|
||||
@@ -46,5 +49,3 @@ string scope_type(Scope s) {
|
||||
from Scope s
|
||||
where needs_docstring(s) and not exists(s.getDocString())
|
||||
select s, scope_type(s) + " " + s.getName() + " does not have a docstring"
|
||||
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ string message() {
|
||||
}
|
||||
|
||||
predicate exec_function_call(Call c) {
|
||||
exists(GlobalVariable exec | exec = ((Name)c.getFunc()).getVariable() and exec.getId() = "exec")
|
||||
exists(GlobalVariable exec | exec = c.getFunc().(Name).getVariable() and exec.getId() = "exec")
|
||||
}
|
||||
|
||||
from AstNode exec
|
||||
where exec_function_call(exec) or exec instanceof Exec
|
||||
select exec, message()
|
||||
select exec, message()
|
||||
|
||||
@@ -15,16 +15,19 @@ import python
|
||||
|
||||
predicate is_a_string_type(ClassObject seqtype) {
|
||||
seqtype = theBytesType() and major_version() = 2
|
||||
or
|
||||
seqtype = theUnicodeType()
|
||||
or
|
||||
seqtype = theUnicodeType()
|
||||
}
|
||||
|
||||
from For loop, ControlFlowNode iter, Object str, Object seq, ControlFlowNode seq_origin, ClassObject strtype, ClassObject seqtype, ControlFlowNode str_origin
|
||||
where loop.getIter().getAFlowNode() = iter and
|
||||
iter.refersTo(str, strtype, str_origin) and
|
||||
iter.refersTo(seq, seqtype, seq_origin) and
|
||||
is_a_string_type(strtype) and
|
||||
seqtype.isIterable() and
|
||||
not is_a_string_type(seqtype)
|
||||
|
||||
select loop, "Iteration over $@, of class " + seqtype.getName() + ", may also iterate over $@.", seq_origin, "sequence", str_origin, "string"
|
||||
from
|
||||
For loop, ControlFlowNode iter, Object str, Object seq, ControlFlowNode seq_origin,
|
||||
ClassObject strtype, ClassObject seqtype, ControlFlowNode str_origin
|
||||
where
|
||||
loop.getIter().getAFlowNode() = iter and
|
||||
iter.refersTo(str, strtype, str_origin) and
|
||||
iter.refersTo(seq, seqtype, seq_origin) and
|
||||
is_a_string_type(strtype) and
|
||||
seqtype.isIterable() and
|
||||
not is_a_string_type(seqtype)
|
||||
select loop, "Iteration over $@, of class " + seqtype.getName() + ", may also iterate over $@.",
|
||||
seq_origin, "sequence", str_origin, "string"
|
||||
|
||||
@@ -14,18 +14,19 @@
|
||||
|
||||
import python
|
||||
|
||||
private int len(ExprList el) {
|
||||
result = count(el.getAnItem())
|
||||
}
|
||||
private int len(ExprList el) { result = count(el.getAnItem()) }
|
||||
|
||||
predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequenceType) {
|
||||
exists(ExprList l, ExprList r |
|
||||
(a.getATarget().(Tuple).getElts() = l or
|
||||
a.getATarget().(List).getElts() = l)
|
||||
and
|
||||
((a.getValue().(Tuple).getElts() = r and sequenceType = "tuple") or
|
||||
(a.getValue().(List).getElts() = r and sequenceType = "list"))
|
||||
and
|
||||
(
|
||||
a.getATarget().(Tuple).getElts() = l or
|
||||
a.getATarget().(List).getElts() = l
|
||||
) and
|
||||
(
|
||||
a.getValue().(Tuple).getElts() = r and sequenceType = "tuple"
|
||||
or
|
||||
a.getValue().(List).getElts() = r and sequenceType = "list"
|
||||
) and
|
||||
loc = a.getValue().getLocation() and
|
||||
lcount = len(l) and
|
||||
rcount = len(r) and
|
||||
@@ -36,9 +37,10 @@ predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequ
|
||||
|
||||
predicate mismatched_tuple_rhs(Assign a, int lcount, int rcount, Location loc) {
|
||||
exists(ExprList l, TupleObject r, AstNode origin |
|
||||
(a.getATarget().(Tuple).getElts() = l or
|
||||
a.getATarget().(List).getElts() = l)
|
||||
and
|
||||
(
|
||||
a.getATarget().(Tuple).getElts() = l or
|
||||
a.getATarget().(List).getElts() = l
|
||||
) and
|
||||
a.getValue().refersTo(r, origin) and
|
||||
loc = origin.getLocation() and
|
||||
lcount = len(l) and
|
||||
@@ -48,11 +50,12 @@ predicate mismatched_tuple_rhs(Assign a, int lcount, int rcount, Location loc) {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
from Assign a, int lcount, int rcount, Location loc, string sequenceType
|
||||
where
|
||||
mismatched(a, lcount, rcount, loc, sequenceType)
|
||||
or
|
||||
mismatched_tuple_rhs(a, lcount, rcount, loc) and
|
||||
sequenceType = "tuple"
|
||||
select a, "Left hand side of assignment contains " + lcount + " variables, but right hand side is a $@ of length " + rcount + "." , loc, sequenceType
|
||||
select a,
|
||||
"Left hand side of assignment contains " + lcount +
|
||||
" variables, but right hand side is a $@ of length " + rcount + ".", loc, sequenceType
|
||||
|
||||
@@ -22,14 +22,14 @@ Object aFunctionLocalsObject() {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
predicate modification_of_locals(ControlFlowNode f) {
|
||||
f.(SubscriptNode).getObject().refersTo(aFunctionLocalsObject()) and (f.isStore() or f.isDelete())
|
||||
f.(SubscriptNode).getObject().refersTo(aFunctionLocalsObject()) and
|
||||
(f.isStore() or f.isDelete())
|
||||
or
|
||||
exists(string mname, AttrNode attr |
|
||||
attr = f.(CallNode).getFunction() and
|
||||
attr.getObject(mname).refersTo(aFunctionLocalsObject(), _) |
|
||||
attr.getObject(mname).refersTo(aFunctionLocalsObject(), _)
|
||||
|
|
||||
mname = "pop" or
|
||||
mname = "popitem" or
|
||||
mname = "update" or
|
||||
@@ -39,5 +39,4 @@ predicate modification_of_locals(ControlFlowNode f) {
|
||||
|
||||
from AstNode a, ControlFlowNode f
|
||||
where modification_of_locals(f) and a = f.getNode()
|
||||
|
||||
select a, "Modification of the locals() dictionary will have no effect on the local variables."
|
||||
|
||||
@@ -10,20 +10,21 @@
|
||||
* @precision very-high
|
||||
* @id py/nested-loops-with-same-variable
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
predicate loop_variable(For f, Variable v) {
|
||||
f.getTarget().defines(v)
|
||||
}
|
||||
predicate loop_variable(For f, Variable v) { f.getTarget().defines(v) }
|
||||
|
||||
predicate variableUsedInNestedLoops(For inner, For outer, Variable v) {
|
||||
/* Only treat loops in body as inner loops. Loops in the else clause are ignored. */
|
||||
outer.getBody().contains(inner) and loop_variable(inner, v) and loop_variable(outer, v)
|
||||
outer.getBody().contains(inner) and
|
||||
loop_variable(inner, v) and
|
||||
loop_variable(outer, v) and
|
||||
/* Ignore cases where there is no use of the variable or the only use is in the inner loop */
|
||||
and exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n))
|
||||
exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n))
|
||||
}
|
||||
|
||||
from For inner, For outer, Variable v
|
||||
where variableUsedInNestedLoops(inner, outer, v)
|
||||
select inner, "Nested for statement uses loop variable '" + v.getId() + "' of enclosing $@.",
|
||||
outer, "for statement"
|
||||
select inner, "Nested for statement uses loop variable '" + v.getId() + "' of enclosing $@.", outer,
|
||||
"for statement"
|
||||
|
||||
@@ -19,18 +19,18 @@ predicate loop_variable_ssa(For f, Variable v, SsaVariable s) {
|
||||
|
||||
predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) {
|
||||
/* Ignore cases where there is no use of the variable or the only use is in the inner loop. */
|
||||
outer.contains(n)
|
||||
and not inner.contains(n)
|
||||
outer.contains(n) and
|
||||
not inner.contains(n) and
|
||||
/* Only treat loops in body as inner loops. Loops in the else clause are ignored. */
|
||||
and outer.getBody().contains(inner)
|
||||
and exists(SsaVariable s |
|
||||
loop_variable_ssa(inner, v, s.getAnUltimateDefinition())
|
||||
and loop_variable_ssa(outer, v, _)
|
||||
and s.getAUse().getNode() = n
|
||||
outer.getBody().contains(inner) and
|
||||
exists(SsaVariable s |
|
||||
loop_variable_ssa(inner, v, s.getAnUltimateDefinition()) and
|
||||
loop_variable_ssa(outer, v, _) and
|
||||
s.getAUse().getNode() = n
|
||||
)
|
||||
}
|
||||
|
||||
from For inner, For outer, Variable v, Name n
|
||||
where variableUsedInNestedLoops(inner, outer, v, n)
|
||||
select inner, "Nested for statement $@ loop variable '" + v.getId() + "' of enclosing $@.", n, "uses",
|
||||
outer, "for statement"
|
||||
select inner, "Nested for statement $@ loop variable '" + v.getId() + "' of enclosing $@.", n,
|
||||
"uses", outer, "for statement"
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
import python
|
||||
|
||||
from For loop, ControlFlowNode iter, Value v, ClassValue t, ControlFlowNode origin
|
||||
where loop.getIter().getAFlowNode() = iter and
|
||||
iter.pointsTo(_, v, origin) and v.getClass() = t and
|
||||
not t.isIterable() and not t.failedInference(_) and
|
||||
not v = Value::named("None") and
|
||||
not t.isDescriptorType()
|
||||
|
||||
where
|
||||
loop.getIter().getAFlowNode() = iter and
|
||||
iter.pointsTo(_, v, origin) and
|
||||
v.getClass() = t and
|
||||
not t.isIterable() and
|
||||
not t.failedInference(_) and
|
||||
not v = Value::named("None") and
|
||||
not t.isDescriptorType()
|
||||
select loop, "$@ of class '$@' may be used in for-loop.", origin, "Non-iterator", t, t.getName()
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
predicate assignment(AssignStmt a, Expr left, Expr right)
|
||||
{
|
||||
|
||||
predicate assignment(AssignStmt a, Expr left, Expr right) {
|
||||
a.getATarget() = left and a.getValue() = right
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ predicate corresponding(Expr left, Expr right) {
|
||||
exists(Attribute la, Attribute ra |
|
||||
corresponding(la, ra) and
|
||||
left = la.getObject() and
|
||||
right = ra.getObject())
|
||||
right = ra.getObject()
|
||||
)
|
||||
}
|
||||
|
||||
predicate same_value(Expr left, Expr right) {
|
||||
@@ -33,9 +34,7 @@ predicate same_value(Expr left, Expr right) {
|
||||
}
|
||||
|
||||
predicate maybe_defined_in_outer_scope(Name n) {
|
||||
exists(SsaVariable v | v.getAUse().getNode() = n |
|
||||
v.maybeUndefined()
|
||||
)
|
||||
exists(SsaVariable v | v.getAUse().getNode() = n | v.maybeUndefined())
|
||||
}
|
||||
|
||||
Variable relevant_var(Name n) {
|
||||
@@ -50,17 +49,18 @@ predicate same_name(Name n1, Name n2) {
|
||||
not maybe_defined_in_outer_scope(n2)
|
||||
}
|
||||
|
||||
ClassObject value_type(Attribute a) {
|
||||
a.getObject().refersTo(_, result, _)
|
||||
}
|
||||
ClassObject value_type(Attribute a) { a.getObject().refersTo(_, result, _) }
|
||||
|
||||
predicate is_property_access(Attribute a) {
|
||||
value_type(a).lookupAttribute(a.getName()) instanceof PropertyObject
|
||||
}
|
||||
|
||||
predicate same_attribute(Attribute a1, Attribute a2) {
|
||||
corresponding(a1, a2) and a1.getName() = a2.getName() and same_value(a1.getObject(), a2.getObject()) and
|
||||
exists(value_type(a1)) and not is_property_access(a1)
|
||||
corresponding(a1, a2) and
|
||||
a1.getName() = a2.getName() and
|
||||
same_value(a1.getObject(), a2.getObject()) and
|
||||
exists(value_type(a1)) and
|
||||
not is_property_access(a1)
|
||||
}
|
||||
|
||||
int pyflakes_commented_line(File file) {
|
||||
@@ -72,21 +72,24 @@ int pyflakes_commented_line(File file) {
|
||||
predicate pyflakes_commented(AssignStmt assignment) {
|
||||
exists(Location loc |
|
||||
assignment.getLocation() = loc and
|
||||
loc.getStartLine() = pyflakes_commented_line(loc.getFile()))
|
||||
loc.getStartLine() = pyflakes_commented_line(loc.getFile())
|
||||
)
|
||||
}
|
||||
|
||||
predicate side_effecting_lhs(Attribute lhs) {
|
||||
exists(ClassObject cls, ClassObject decl |
|
||||
lhs.getObject().refersTo(_, cls, _) and
|
||||
decl = cls.getAnImproperSuperType() and
|
||||
not decl.isBuiltin() |
|
||||
not decl.isBuiltin()
|
||||
|
|
||||
decl.declaresAttribute("__setattr__")
|
||||
)
|
||||
}
|
||||
|
||||
from AssignStmt a, Expr left, Expr right
|
||||
where assignment(a, left, right)
|
||||
and same_value(left, right)
|
||||
and not pyflakes_commented(a) and
|
||||
not side_effecting_lhs(left)
|
||||
where
|
||||
assignment(a, left, right) and
|
||||
same_value(left, right) and
|
||||
not pyflakes_commented(a) and
|
||||
not side_effecting_lhs(left)
|
||||
select a, "This assignment assigns a variable to itself."
|
||||
|
||||
@@ -14,12 +14,12 @@ import python
|
||||
|
||||
from AstNode node, string kind
|
||||
where
|
||||
not node.getScope() instanceof Function and
|
||||
(
|
||||
node instanceof Return and kind = "return"
|
||||
or
|
||||
node instanceof Yield and kind = "yield"
|
||||
or
|
||||
node instanceof YieldFrom and kind = "yield from"
|
||||
)
|
||||
not node.getScope() instanceof Function and
|
||||
(
|
||||
node instanceof Return and kind = "return"
|
||||
or
|
||||
node instanceof Yield and kind = "yield"
|
||||
or
|
||||
node instanceof YieldFrom and kind = "yield from"
|
||||
)
|
||||
select node, "'" + kind + "' is used outside a function."
|
||||
|
||||
@@ -14,14 +14,12 @@
|
||||
|
||||
import python
|
||||
|
||||
predicate calls_close(Call c) { exists(Attribute a | c.getFunc() = a and a.getName() = "close") }
|
||||
|
||||
predicate calls_close(Call c) {
|
||||
exists (Attribute a | c.getFunc() = a and a.getName() = "close")
|
||||
}
|
||||
|
||||
predicate
|
||||
only_stmt_in_finally(Try t, Call c) {
|
||||
exists(ExprStmt s | t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1)
|
||||
predicate only_stmt_in_finally(Try t, Call c) {
|
||||
exists(ExprStmt s |
|
||||
t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1
|
||||
)
|
||||
}
|
||||
|
||||
predicate points_to_context_manager(ControlFlowNode f, ClassObject cls) {
|
||||
@@ -30,8 +28,12 @@ predicate points_to_context_manager(ControlFlowNode f, ClassObject cls) {
|
||||
}
|
||||
|
||||
from Call close, Try t, ClassObject cls
|
||||
where only_stmt_in_finally(t, close) and calls_close(close) and
|
||||
exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() |
|
||||
points_to_context_manager(f, cls))
|
||||
select close, "Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.", cls, cls.getName()
|
||||
|
||||
where
|
||||
only_stmt_in_finally(t, close) and
|
||||
calls_close(close) and
|
||||
exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() |
|
||||
points_to_context_manager(f, cls)
|
||||
)
|
||||
select close,
|
||||
"Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.",
|
||||
cls, cls.getName()
|
||||
|
||||
@@ -14,11 +14,16 @@
|
||||
import python
|
||||
|
||||
predicate func_with_side_effects(Expr e) {
|
||||
exists(string name |
|
||||
name = ((Attribute)e).getName() or name = ((Name)e).getId() |
|
||||
name = "print" or name = "write" or name = "append" or
|
||||
name = "pop" or name = "remove" or name = "discard" or
|
||||
name = "delete" or name = "close" or name = "open" or
|
||||
exists(string name | name = e.(Attribute).getName() or name = e.(Name).getId() |
|
||||
name = "print" or
|
||||
name = "write" or
|
||||
name = "append" or
|
||||
name = "pop" or
|
||||
name = "remove" or
|
||||
name = "discard" or
|
||||
name = "delete" or
|
||||
name = "close" or
|
||||
name = "open" or
|
||||
name = "exit"
|
||||
)
|
||||
}
|
||||
@@ -29,7 +34,7 @@ predicate probable_side_effect(Expr e) {
|
||||
or
|
||||
e instanceof YieldFrom
|
||||
or
|
||||
e instanceof Call and func_with_side_effects(((Call)e).getFunc())
|
||||
e instanceof Call and func_with_side_effects(e.(Call).getFunc())
|
||||
}
|
||||
|
||||
from Assert a, Expr e
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
import python
|
||||
|
||||
predicate understood_attribute(Attribute attr, ClassObject cls, ClassObject attr_cls) {
|
||||
exists(string name |
|
||||
attr.getName() = name |
|
||||
exists(string name | attr.getName() = name |
|
||||
attr.getObject().refersTo(_, cls, _) and
|
||||
cls.attributeRefersTo(name, _, attr_cls, _)
|
||||
)
|
||||
@@ -37,15 +36,19 @@ predicate maybe_side_effecting_attribute(Attribute attr) {
|
||||
|
||||
predicate side_effecting_descriptor_type(ClassObject descriptor) {
|
||||
descriptor.isDescriptorType() and
|
||||
/* Technically all descriptor gets have side effects,
|
||||
* but some are indicative of a missing call and
|
||||
* we want to treat them as having no effect. */
|
||||
not descriptor = thePyFunctionType() and
|
||||
not descriptor = theStaticMethodType() and
|
||||
not descriptor = theClassMethodType()
|
||||
/*
|
||||
* Technically all descriptor gets have side effects,
|
||||
* but some are indicative of a missing call and
|
||||
* we want to treat them as having no effect.
|
||||
*/
|
||||
|
||||
not descriptor = thePyFunctionType() and
|
||||
not descriptor = theStaticMethodType() and
|
||||
not descriptor = theClassMethodType()
|
||||
}
|
||||
|
||||
/** Side effecting binary operators are rare, so we assume they are not
|
||||
/**
|
||||
* Side effecting binary operators are rare, so we assume they are not
|
||||
* side-effecting unless we know otherwise.
|
||||
*/
|
||||
predicate side_effecting_binary(Expr b) {
|
||||
@@ -53,20 +56,22 @@ predicate side_effecting_binary(Expr b) {
|
||||
binary_operator_special_method(b, sub, cls, method_name)
|
||||
or
|
||||
comparison_special_method(b, sub, cls, method_name)
|
||||
|
|
||||
|
|
||||
method_name = special_method() and
|
||||
cls.hasAttribute(method_name)
|
||||
and
|
||||
cls.hasAttribute(method_name) and
|
||||
not exists(ClassObject declaring |
|
||||
declaring.declaresAttribute(method_name)
|
||||
and declaring = cls.getAnImproperSuperType() and
|
||||
declaring.isBuiltin() and not declaring = theObjectType()
|
||||
declaring.declaresAttribute(method_name) and
|
||||
declaring = cls.getAnImproperSuperType() and
|
||||
declaring.isBuiltin() and
|
||||
not declaring = theObjectType()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binary_operator_special_method(BinaryExpr b, Expr sub, ClassObject cls, string method_name) {
|
||||
private predicate binary_operator_special_method(
|
||||
BinaryExpr b, Expr sub, ClassObject cls, string method_name
|
||||
) {
|
||||
method_name = special_method() and
|
||||
sub = b.getLeft() and
|
||||
method_name = b.getOp().getSpecialMethodName() and
|
||||
@@ -89,19 +94,17 @@ private string special_method() {
|
||||
}
|
||||
|
||||
predicate is_notebook(File f) {
|
||||
exists(Comment c |
|
||||
c.getLocation().getFile() = f |
|
||||
exists(Comment c | c.getLocation().getFile() = f |
|
||||
c.getText().regexpMatch("#\\s*<nbformat>.+</nbformat>\\s*")
|
||||
)
|
||||
}
|
||||
|
||||
/** Expression (statement) in a jupyter/ipython notebook */
|
||||
predicate in_notebook(Expr e) {
|
||||
is_notebook(e.getScope().(Module).getFile())
|
||||
}
|
||||
predicate in_notebook(Expr e) { is_notebook(e.getScope().(Module).getFile()) }
|
||||
|
||||
FunctionObject assertRaises() {
|
||||
result = ModuleObject::named("unittest").attr("TestCase").(ClassObject).lookupAttribute("assertRaises")
|
||||
result =
|
||||
ModuleObject::named("unittest").attr("TestCase").(ClassObject).lookupAttribute("assertRaises")
|
||||
}
|
||||
|
||||
/** Holds if expression `e` is in a `with` block that tests for exceptions being raised. */
|
||||
@@ -122,13 +125,10 @@ predicate python2_print(Expr e) {
|
||||
|
||||
predicate no_effect(Expr e) {
|
||||
not e instanceof StrConst and
|
||||
not ((StrConst)e).isDocString() and
|
||||
not e.(StrConst).isDocString() and
|
||||
not e.hasSideEffects() and
|
||||
forall(Expr sub |
|
||||
sub = e.getASubExpression*()
|
||||
|
|
||||
not side_effecting_binary(sub)
|
||||
and
|
||||
forall(Expr sub | sub = e.getASubExpression*() |
|
||||
not side_effecting_binary(sub) and
|
||||
not maybe_side_effecting_attribute(sub)
|
||||
) and
|
||||
not in_notebook(e) and
|
||||
@@ -139,4 +139,3 @@ predicate no_effect(Expr e) {
|
||||
from ExprStmt stmt
|
||||
where no_effect(stmt.getValue())
|
||||
select stmt, "This statement has no effect."
|
||||
|
||||
|
||||
@@ -13,17 +13,17 @@
|
||||
import python
|
||||
|
||||
predicate string_concat_in_loop(BinaryExpr b) {
|
||||
b.getOp() instanceof Add
|
||||
and
|
||||
b.getOp() instanceof Add and
|
||||
exists(SsaVariable d, SsaVariable u, BinaryExprNode add, ClassObject str_type |
|
||||
add.getNode() = b and d = u.getAnUltimateDefinition() |
|
||||
d.getDefinition().(DefinitionNode).getValue() = add and u.getAUse() = add.getAnOperand() and
|
||||
add.getAnOperand().refersTo(_, str_type, _) and
|
||||
(str_type = theBytesType() or str_type = theUnicodeType())
|
||||
add.getNode() = b and d = u.getAnUltimateDefinition()
|
||||
|
|
||||
d.getDefinition().(DefinitionNode).getValue() = add and
|
||||
u.getAUse() = add.getAnOperand() and
|
||||
add.getAnOperand().refersTo(_, str_type, _) and
|
||||
(str_type = theBytesType() or str_type = theUnicodeType())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
from BinaryExpr b, Stmt s
|
||||
where string_concat_in_loop(b) and s.getASubExpression() = b
|
||||
select s, "String concatenation in a loop is quadratic in the number of iterations."
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
|
||||
import python
|
||||
|
||||
|
||||
predicate main_eq_name(If i) {
|
||||
exists(Name n, StrConst m, Compare c |
|
||||
i.getTest() = c and c.getLeft() = n and
|
||||
i.getTest() = c and
|
||||
c.getLeft() = n and
|
||||
c.getAComparator() = m and
|
||||
n.getId() = "__name__" and
|
||||
m.getText() = "__main__"
|
||||
@@ -24,12 +24,16 @@ predicate main_eq_name(If i) {
|
||||
}
|
||||
|
||||
predicate is_print_stmt(Stmt s) {
|
||||
s instanceof Print or
|
||||
exists(ExprStmt e, Call c, Name n | e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print")
|
||||
s instanceof Print
|
||||
or
|
||||
exists(ExprStmt e, Call c, Name n |
|
||||
e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print"
|
||||
)
|
||||
}
|
||||
|
||||
from Stmt p
|
||||
where is_print_stmt(p) and
|
||||
exists(ModuleObject m | m.getModule() = p.getScope() and m.getKind() = "module") and
|
||||
not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p)
|
||||
where
|
||||
is_print_stmt(p) and
|
||||
exists(ModuleObject m | m.getModule() = p.getScope() and m.getKind() = "module") and
|
||||
not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p)
|
||||
select p, "Print statement may execute during import."
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
* @id py/unnecessary-delete
|
||||
*/
|
||||
|
||||
|
||||
import python
|
||||
|
||||
from Delete del, Expr e, Function f
|
||||
@@ -23,11 +22,14 @@ where
|
||||
not e instanceof Subscript and
|
||||
not e instanceof Attribute and
|
||||
not exists(Stmt s | s.(While).contains(del) or s.(For).contains(del)) and
|
||||
/* False positive: calling `sys.exc_info` within a function results in a
|
||||
reference cycle,and an explicit call to `del` helps break this cycle. */
|
||||
/*
|
||||
* False positive: calling `sys.exc_info` within a function results in a
|
||||
* reference cycle, and an explicit call to `del` helps break this cycle.
|
||||
*/
|
||||
|
||||
not exists(FunctionObject ex |
|
||||
ex.hasLongName("sys.exc_info") and
|
||||
ex.getACall().getScope() = f
|
||||
)
|
||||
select del, "Unnecessary deletion of local variable $@ in function $@.",
|
||||
e.getLocation(), e.toString(), f.getLocation(), f.getName()
|
||||
select del, "Unnecessary deletion of local variable $@ in function $@.", e.getLocation(),
|
||||
e.toString(), f.getLocation(), f.getName()
|
||||
|
||||
@@ -14,9 +14,11 @@ import python
|
||||
|
||||
from Stmt loop, StmtList body, StmtList clause, string kind
|
||||
where
|
||||
(exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for")
|
||||
or
|
||||
exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while")
|
||||
)
|
||||
and not exists(Break b | body.contains(b))
|
||||
select loop, "This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body."
|
||||
(
|
||||
exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for")
|
||||
or
|
||||
exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while")
|
||||
) and
|
||||
not exists(Break b | body.contains(b))
|
||||
select loop,
|
||||
"This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body."
|
||||
|
||||
@@ -13,21 +13,20 @@
|
||||
import python
|
||||
|
||||
predicate is_doc_string(ExprStmt s) {
|
||||
s.getValue() instanceof Unicode or s.getValue() instanceof Bytes
|
||||
s.getValue() instanceof Unicode or s.getValue() instanceof Bytes
|
||||
}
|
||||
|
||||
predicate has_doc_string(StmtList stmts) {
|
||||
stmts.getParent() instanceof Scope
|
||||
and
|
||||
stmts.getParent() instanceof Scope and
|
||||
is_doc_string(stmts.getItem(0))
|
||||
}
|
||||
|
||||
from Pass p, StmtList list
|
||||
where list.getAnItem() = p and
|
||||
(
|
||||
strictcount(list.getAnItem()) = 2 and not has_doc_string(list)
|
||||
or
|
||||
strictcount(list.getAnItem()) > 2
|
||||
)
|
||||
where
|
||||
list.getAnItem() = p and
|
||||
(
|
||||
strictcount(list.getAnItem()) = 2 and not has_doc_string(list)
|
||||
or
|
||||
strictcount(list.getAnItem()) > 2
|
||||
)
|
||||
select p, "Unnecessary 'pass' statement."
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
import python
|
||||
|
||||
from Call call, ClassObject ex
|
||||
where call.getFunc().refersTo(ex) and ex.getAnImproperSuperType() = theExceptionType()
|
||||
and exists(ExprStmt s | s.getValue() = call)
|
||||
|
||||
where
|
||||
call.getFunc().refersTo(ex) and
|
||||
ex.getAnImproperSuperType() = theExceptionType() and
|
||||
exists(ExprStmt s | s.getValue() = call)
|
||||
select call, "Instantiating an exception, but not raising it, has no effect"
|
||||
|
||||
@@ -13,4 +13,6 @@ import python
|
||||
|
||||
from CallNode call, string name
|
||||
where call.getFunction().refersTo(Object::quitter(name))
|
||||
select call, "The '" + name + "' site.Quitter object may not exist if the 'site' module is not loaded or is modified."
|
||||
select call,
|
||||
"The '" + name +
|
||||
"' site.Quitter object may not exist if the 'site' module is not loaded or is modified."
|
||||
|
||||
Reference in New Issue
Block a user