mirror of
https://github.com/github/codeql.git
synced 2025-12-18 01:33:15 +01:00
Python: Autoformat everything using qlformat.
Will need subsequent PRs fixing up test failures (due to deprecated methods moving around), but other than that everything should be straight-forward.
This commit is contained in:
@@ -13,21 +13,21 @@
|
||||
import python
|
||||
|
||||
predicate explicitly_returns_non_none(Function func) {
|
||||
exists(Return return |
|
||||
return.getScope() = func and
|
||||
exists(Expr val | val = return.getValue() | not val instanceof None)
|
||||
)
|
||||
exists(Return return |
|
||||
return.getScope() = func and
|
||||
exists(Expr val | val = return.getValue() | not val instanceof None)
|
||||
)
|
||||
}
|
||||
|
||||
predicate has_implicit_return(Function func) {
|
||||
exists(ControlFlowNode fallthru |
|
||||
fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable()
|
||||
)
|
||||
or
|
||||
exists(Return return | return.getScope() = func and not exists(return.getValue()))
|
||||
exists(ControlFlowNode fallthru |
|
||||
fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable()
|
||||
)
|
||||
or
|
||||
exists(Return return | return.getScope() = func and not exists(return.getValue()))
|
||||
}
|
||||
|
||||
from Function func
|
||||
where explicitly_returns_non_none(func) and has_implicit_return(func)
|
||||
select func,
|
||||
"Mixing implicit and explicit returns may indicate an error as implicit returns always return None."
|
||||
"Mixing implicit and explicit returns may indicate an error as implicit returns always return None."
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
import python
|
||||
|
||||
predicate slice_method_name(string name) {
|
||||
name = "__getslice__" or name = "__setslice__" or name = "__delslice__"
|
||||
name = "__getslice__" or name = "__setslice__" or name = "__delslice__"
|
||||
}
|
||||
|
||||
from PythonFunctionValue f, string meth
|
||||
where
|
||||
f.getScope().isMethod() and
|
||||
not f.isOverridingMethod() and
|
||||
slice_method_name(meth) and
|
||||
f.getName() = meth
|
||||
f.getScope().isMethod() and
|
||||
not f.isOverridingMethod() and
|
||||
slice_method_name(meth) and
|
||||
f.getName() = meth
|
||||
select f, meth + " method has been deprecated since Python 2.0"
|
||||
|
||||
@@ -14,10 +14,10 @@ import python
|
||||
|
||||
from Return r, Expr rv
|
||||
where
|
||||
exists(Function init | init.isInitMethod() and r.getScope() = init) and
|
||||
r.getValue() = rv and
|
||||
not rv.pointsTo(Value::none_()) and
|
||||
not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and
|
||||
// to avoid double reporting, don't trigger if returning result from other __init__ function
|
||||
not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__")
|
||||
exists(Function init | init.isInitMethod() and r.getScope() = init) and
|
||||
r.getValue() = rv and
|
||||
not rv.pointsTo(Value::none_()) and
|
||||
not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and
|
||||
// to avoid double reporting, don't trigger if returning result from other __init__ function
|
||||
not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__")
|
||||
select r, "Explicit return in __init__ method."
|
||||
|
||||
@@ -14,142 +14,142 @@
|
||||
import python
|
||||
|
||||
private predicate attribute_method(string name) {
|
||||
name = "__getattribute__" or name = "__getattr__" or name = "__setattr__"
|
||||
name = "__getattribute__" or name = "__getattr__" or name = "__setattr__"
|
||||
}
|
||||
|
||||
private predicate indexing_method(string name) {
|
||||
name = "__getitem__" or name = "__setitem__" or name = "__delitem__"
|
||||
name = "__getitem__" or name = "__setitem__" or name = "__delitem__"
|
||||
}
|
||||
|
||||
private predicate arithmetic_method(string name) {
|
||||
name = "__add__" or
|
||||
name = "__sub__" or
|
||||
name = "__div__" or
|
||||
name = "__pos__" or
|
||||
name = "__abs__" or
|
||||
name = "__floordiv__" or
|
||||
name = "__div__" or
|
||||
name = "__divmod__" or
|
||||
name = "__lshift__" or
|
||||
name = "__and__" or
|
||||
name = "__or__" or
|
||||
name = "__xor__" or
|
||||
name = "__rshift__" or
|
||||
name = "__pow__" or
|
||||
name = "__mul__" or
|
||||
name = "__neg__" or
|
||||
name = "__radd__" or
|
||||
name = "__rsub__" or
|
||||
name = "__rdiv__" or
|
||||
name = "__rfloordiv__" or
|
||||
name = "__rdiv__" or
|
||||
name = "__rlshift__" or
|
||||
name = "__rand__" or
|
||||
name = "__ror__" or
|
||||
name = "__rxor__" or
|
||||
name = "__rrshift__" or
|
||||
name = "__rpow__" or
|
||||
name = "__rmul__" or
|
||||
name = "__truediv__" or
|
||||
name = "__rtruediv__" or
|
||||
name = "__iadd__" or
|
||||
name = "__isub__" or
|
||||
name = "__idiv__" or
|
||||
name = "__ifloordiv__" or
|
||||
name = "__idiv__" or
|
||||
name = "__ilshift__" or
|
||||
name = "__iand__" or
|
||||
name = "__ior__" or
|
||||
name = "__ixor__" or
|
||||
name = "__irshift__" or
|
||||
name = "__ipow__" or
|
||||
name = "__imul__" or
|
||||
name = "__itruediv__"
|
||||
name = "__add__" or
|
||||
name = "__sub__" or
|
||||
name = "__div__" or
|
||||
name = "__pos__" or
|
||||
name = "__abs__" or
|
||||
name = "__floordiv__" or
|
||||
name = "__div__" or
|
||||
name = "__divmod__" or
|
||||
name = "__lshift__" or
|
||||
name = "__and__" or
|
||||
name = "__or__" or
|
||||
name = "__xor__" or
|
||||
name = "__rshift__" or
|
||||
name = "__pow__" or
|
||||
name = "__mul__" or
|
||||
name = "__neg__" or
|
||||
name = "__radd__" or
|
||||
name = "__rsub__" or
|
||||
name = "__rdiv__" or
|
||||
name = "__rfloordiv__" or
|
||||
name = "__rdiv__" or
|
||||
name = "__rlshift__" or
|
||||
name = "__rand__" or
|
||||
name = "__ror__" or
|
||||
name = "__rxor__" or
|
||||
name = "__rrshift__" or
|
||||
name = "__rpow__" or
|
||||
name = "__rmul__" or
|
||||
name = "__truediv__" or
|
||||
name = "__rtruediv__" or
|
||||
name = "__iadd__" or
|
||||
name = "__isub__" or
|
||||
name = "__idiv__" or
|
||||
name = "__ifloordiv__" or
|
||||
name = "__idiv__" or
|
||||
name = "__ilshift__" or
|
||||
name = "__iand__" or
|
||||
name = "__ior__" or
|
||||
name = "__ixor__" or
|
||||
name = "__irshift__" or
|
||||
name = "__ipow__" or
|
||||
name = "__imul__" or
|
||||
name = "__itruediv__"
|
||||
}
|
||||
|
||||
private predicate ordering_method(string name) {
|
||||
name = "__lt__"
|
||||
or
|
||||
name = "__le__"
|
||||
or
|
||||
name = "__gt__"
|
||||
or
|
||||
name = "__ge__"
|
||||
or
|
||||
name = "__cmp__" and major_version() = 2
|
||||
name = "__lt__"
|
||||
or
|
||||
name = "__le__"
|
||||
or
|
||||
name = "__gt__"
|
||||
or
|
||||
name = "__ge__"
|
||||
or
|
||||
name = "__cmp__" and major_version() = 2
|
||||
}
|
||||
|
||||
private predicate cast_method(string name) {
|
||||
name = "__nonzero__" and major_version() = 2
|
||||
or
|
||||
name = "__int__"
|
||||
or
|
||||
name = "__float__"
|
||||
or
|
||||
name = "__long__"
|
||||
or
|
||||
name = "__trunc__"
|
||||
or
|
||||
name = "__complex__"
|
||||
name = "__nonzero__" and major_version() = 2
|
||||
or
|
||||
name = "__int__"
|
||||
or
|
||||
name = "__float__"
|
||||
or
|
||||
name = "__long__"
|
||||
or
|
||||
name = "__trunc__"
|
||||
or
|
||||
name = "__complex__"
|
||||
}
|
||||
|
||||
predicate correct_raise(string name, ClassObject ex) {
|
||||
ex.getAnImproperSuperType() = theTypeErrorType() and
|
||||
(
|
||||
name = "__copy__" or
|
||||
name = "__deepcopy__" or
|
||||
name = "__call__" or
|
||||
indexing_method(name) or
|
||||
attribute_method(name)
|
||||
)
|
||||
or
|
||||
preferred_raise(name, ex)
|
||||
or
|
||||
preferred_raise(name, ex.getASuperType())
|
||||
ex.getAnImproperSuperType() = theTypeErrorType() and
|
||||
(
|
||||
name = "__copy__" or
|
||||
name = "__deepcopy__" or
|
||||
name = "__call__" or
|
||||
indexing_method(name) or
|
||||
attribute_method(name)
|
||||
)
|
||||
or
|
||||
preferred_raise(name, ex)
|
||||
or
|
||||
preferred_raise(name, ex.getASuperType())
|
||||
}
|
||||
|
||||
predicate preferred_raise(string name, ClassObject ex) {
|
||||
attribute_method(name) and ex = theAttributeErrorType()
|
||||
or
|
||||
indexing_method(name) and ex = Object::builtin("LookupError")
|
||||
or
|
||||
ordering_method(name) and ex = theTypeErrorType()
|
||||
or
|
||||
arithmetic_method(name) and ex = Object::builtin("ArithmeticError")
|
||||
or
|
||||
name = "__bool__" and ex = theTypeErrorType()
|
||||
attribute_method(name) and ex = theAttributeErrorType()
|
||||
or
|
||||
indexing_method(name) and ex = Object::builtin("LookupError")
|
||||
or
|
||||
ordering_method(name) and ex = theTypeErrorType()
|
||||
or
|
||||
arithmetic_method(name) and ex = Object::builtin("ArithmeticError")
|
||||
or
|
||||
name = "__bool__" and ex = theTypeErrorType()
|
||||
}
|
||||
|
||||
predicate no_need_to_raise(string name, string message) {
|
||||
name = "__hash__" and message = "use __hash__ = None instead"
|
||||
or
|
||||
cast_method(name) and message = "there is no need to implement the method at all."
|
||||
name = "__hash__" and message = "use __hash__ = None instead"
|
||||
or
|
||||
cast_method(name) and message = "there is no need to implement the method at all."
|
||||
}
|
||||
|
||||
predicate is_abstract(FunctionObject func) {
|
||||
func.getFunction().getADecorator().(Name).getId().matches("%abstract%")
|
||||
func.getFunction().getADecorator().(Name).getId().matches("%abstract%")
|
||||
}
|
||||
|
||||
predicate always_raises(FunctionObject f, ClassObject ex) {
|
||||
ex = f.getARaisedType() and
|
||||
strictcount(f.getARaisedType()) = 1 and
|
||||
not exists(f.getFunction().getANormalExit()) and
|
||||
/* raising StopIteration is equivalent to a return in a generator */
|
||||
not ex = theStopIterationType()
|
||||
ex = f.getARaisedType() and
|
||||
strictcount(f.getARaisedType()) = 1 and
|
||||
not exists(f.getFunction().getANormalExit()) and
|
||||
/* raising StopIteration is equivalent to a return in a generator */
|
||||
not ex = theStopIterationType()
|
||||
}
|
||||
|
||||
from FunctionObject f, ClassObject cls, string message
|
||||
where
|
||||
f.getFunction().isSpecialMethod() and
|
||||
not is_abstract(f) and
|
||||
always_raises(f, cls) and
|
||||
(
|
||||
no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError"
|
||||
or
|
||||
not correct_raise(f.getName(), cls) and
|
||||
not cls.getName() = "NotImplementedError" and
|
||||
exists(ClassObject preferred | preferred_raise(f.getName(), preferred) |
|
||||
message = "raise " + preferred.getName() + " instead"
|
||||
)
|
||||
f.getFunction().isSpecialMethod() and
|
||||
not is_abstract(f) and
|
||||
always_raises(f, cls) and
|
||||
(
|
||||
no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError"
|
||||
or
|
||||
not correct_raise(f.getName(), cls) and
|
||||
not cls.getName() = "NotImplementedError" and
|
||||
exists(ClassObject preferred | preferred_raise(f.getName(), preferred) |
|
||||
message = "raise " + preferred.getName() + " instead"
|
||||
)
|
||||
)
|
||||
select f, "Function always raises $@; " + message, cls, cls.toString()
|
||||
|
||||
@@ -14,18 +14,18 @@ import Expressions.CallArgs
|
||||
|
||||
from Call call, FunctionValue func, FunctionValue overridden, string problem
|
||||
where
|
||||
func.overrides(overridden) and
|
||||
(
|
||||
wrong_args(call, func, _, problem) and
|
||||
correct_args_if_called_as_method(call, overridden)
|
||||
or
|
||||
exists(string name |
|
||||
illegally_named_parameter(call, func, name) and
|
||||
problem = "an argument named '" + name + "'" and
|
||||
overridden.getScope().getAnArg().(Name).getId() = name
|
||||
)
|
||||
func.overrides(overridden) and
|
||||
(
|
||||
wrong_args(call, func, _, problem) and
|
||||
correct_args_if_called_as_method(call, overridden)
|
||||
or
|
||||
exists(string name |
|
||||
illegally_named_parameter(call, func, name) and
|
||||
problem = "an argument named '" + name + "'" and
|
||||
overridden.getScope().getAnArg().(Name).getId() = name
|
||||
)
|
||||
)
|
||||
select func,
|
||||
"Overriding method signature does not match $@, where it is passed " + problem +
|
||||
". Overridden method $@ is correctly specified.", call, "here", overridden,
|
||||
overridden.descriptiveString()
|
||||
"Overriding method signature does not match $@, where it is passed " + problem +
|
||||
". Overridden method $@ is correctly specified.", call, "here", overridden,
|
||||
overridden.descriptiveString()
|
||||
|
||||
@@ -15,23 +15,23 @@ import Expressions.CallArgs
|
||||
|
||||
from Call call, FunctionValue func, FunctionValue overriding, string problem
|
||||
where
|
||||
not func.getName() = "__init__" and
|
||||
overriding.overrides(func) and
|
||||
call = overriding.getAMethodCall().getNode() and
|
||||
correct_args_if_called_as_method(call, overriding) and
|
||||
(
|
||||
arg_count(call) + 1 < func.minParameters() and problem = "too few arguments"
|
||||
or
|
||||
arg_count(call) >= func.maxParameters() and problem = "too many arguments"
|
||||
or
|
||||
exists(string name |
|
||||
call.getAKeyword().getArg() = name and
|
||||
overriding.getScope().getAnArg().(Name).getId() = name and
|
||||
not func.getScope().getAnArg().(Name).getId() = name and
|
||||
problem = "an argument named '" + name + "'"
|
||||
)
|
||||
not func.getName() = "__init__" and
|
||||
overriding.overrides(func) and
|
||||
call = overriding.getAMethodCall().getNode() and
|
||||
correct_args_if_called_as_method(call, overriding) and
|
||||
(
|
||||
arg_count(call) + 1 < func.minParameters() and problem = "too few arguments"
|
||||
or
|
||||
arg_count(call) >= func.maxParameters() and problem = "too many arguments"
|
||||
or
|
||||
exists(string name |
|
||||
call.getAKeyword().getArg() = name and
|
||||
overriding.getScope().getAnArg().(Name).getId() = name and
|
||||
not func.getScope().getAnArg().(Name).getId() = name and
|
||||
problem = "an argument named '" + name + "'"
|
||||
)
|
||||
)
|
||||
select func,
|
||||
"Overridden method signature does not match $@, where it is passed " + problem +
|
||||
". Overriding method $@ matches the call.", call, "call", overriding,
|
||||
overriding.descriptiveString()
|
||||
"Overridden method signature does not match $@, where it is passed " + problem +
|
||||
". Overriding method $@ matches the call.", call, "call", overriding,
|
||||
overriding.descriptiveString()
|
||||
|
||||
@@ -14,6 +14,6 @@ import python
|
||||
|
||||
from Function f
|
||||
where
|
||||
f.isInitMethod() and
|
||||
(exists(Yield y | y.getScope() = f) or exists(YieldFrom y | y.getScope() = f))
|
||||
f.isInitMethod() and
|
||||
(exists(Yield y | y.getScope() = f) or exists(YieldFrom y | y.getScope() = f))
|
||||
select f, "__init__ method is a generator."
|
||||
|
||||
@@ -14,10 +14,10 @@ import python
|
||||
|
||||
from ClassValue iterable, FunctionValue iter, ClassValue iterator
|
||||
where
|
||||
iter = iterable.lookup("__iter__") and
|
||||
iterator = iter.getAnInferredReturnType() and
|
||||
not iterator.isIterator()
|
||||
iter = iterable.lookup("__iter__") and
|
||||
iterator = iter.getAnInferredReturnType() and
|
||||
not iterator.isIterator()
|
||||
select iterator,
|
||||
"Class " + iterator.getName() +
|
||||
" is returned as an iterator (by $@) but does not fully implement the iterator interface.",
|
||||
iter, iter.getName()
|
||||
"Class " + iterator.getName() +
|
||||
" is returned as an iterator (by $@) but does not fully implement the iterator interface.",
|
||||
iter, iter.getName()
|
||||
|
||||
@@ -17,14 +17,14 @@ Function iter_method(ClassValue t) { result = t.lookup("__iter__").(FunctionValu
|
||||
predicate is_self(Name value, Function f) { value.getVariable() = f.getArg(0).(Name).getVariable() }
|
||||
|
||||
predicate returns_non_self(Function f) {
|
||||
exists(f.getFallthroughNode())
|
||||
or
|
||||
exists(Return r | r.getScope() = f and not is_self(r.getValue(), f))
|
||||
or
|
||||
exists(Return r | r.getScope() = f and not exists(r.getValue()))
|
||||
exists(f.getFallthroughNode())
|
||||
or
|
||||
exists(Return r | r.getScope() = f and not is_self(r.getValue(), f))
|
||||
or
|
||||
exists(Return r | r.getScope() = f and not exists(r.getValue()))
|
||||
}
|
||||
|
||||
from ClassValue t, Function iter
|
||||
where t.isIterator() and iter = iter_method(t) and returns_non_self(iter)
|
||||
select t, "Class " + t.getName() + " is an iterator but its $@ method does not return 'self'.",
|
||||
iter, iter.getName()
|
||||
iter, iter.getName()
|
||||
|
||||
@@ -15,85 +15,85 @@ import python
|
||||
import semmle.python.security.Paths
|
||||
|
||||
predicate safe_method(string name) {
|
||||
name = "count" or
|
||||
name = "index" or
|
||||
name = "copy" or
|
||||
name = "get" or
|
||||
name = "has_key" or
|
||||
name = "items" or
|
||||
name = "keys" or
|
||||
name = "values" or
|
||||
name = "iteritems" or
|
||||
name = "iterkeys" or
|
||||
name = "itervalues" or
|
||||
name = "__contains__" or
|
||||
name = "__getitem__" or
|
||||
name = "__getattribute__"
|
||||
name = "count" or
|
||||
name = "index" or
|
||||
name = "copy" or
|
||||
name = "get" or
|
||||
name = "has_key" or
|
||||
name = "items" or
|
||||
name = "keys" or
|
||||
name = "values" or
|
||||
name = "iteritems" or
|
||||
name = "iterkeys" or
|
||||
name = "itervalues" or
|
||||
name = "__contains__" or
|
||||
name = "__getitem__" or
|
||||
name = "__getattribute__"
|
||||
}
|
||||
|
||||
/** Gets the truthiness (non emptyness) of the default of `p` if that value is mutable */
|
||||
private boolean mutableDefaultValue(Parameter p) {
|
||||
exists(Dict d | p.getDefault() = d |
|
||||
exists(d.getAKey()) and result = true
|
||||
or
|
||||
not exists(d.getAKey()) and result = false
|
||||
)
|
||||
exists(Dict d | p.getDefault() = d |
|
||||
exists(d.getAKey()) and result = true
|
||||
or
|
||||
exists(List l | p.getDefault() = l |
|
||||
exists(l.getAnElt()) and result = true
|
||||
or
|
||||
not exists(l.getAnElt()) and result = false
|
||||
)
|
||||
not exists(d.getAKey()) and result = false
|
||||
)
|
||||
or
|
||||
exists(List l | p.getDefault() = l |
|
||||
exists(l.getAnElt()) and result = true
|
||||
or
|
||||
not exists(l.getAnElt()) and result = false
|
||||
)
|
||||
}
|
||||
|
||||
class NonEmptyMutableValue extends TaintKind {
|
||||
NonEmptyMutableValue() { this = "non-empty mutable value" }
|
||||
NonEmptyMutableValue() { this = "non-empty mutable value" }
|
||||
}
|
||||
|
||||
class EmptyMutableValue extends TaintKind {
|
||||
EmptyMutableValue() { this = "empty mutable value" }
|
||||
EmptyMutableValue() { this = "empty mutable value" }
|
||||
|
||||
override boolean booleanValue() { result = false }
|
||||
override boolean booleanValue() { result = false }
|
||||
}
|
||||
|
||||
class MutableDefaultValue extends TaintSource {
|
||||
boolean nonEmpty;
|
||||
boolean nonEmpty;
|
||||
|
||||
MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) }
|
||||
MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) }
|
||||
|
||||
override string toString() { result = "mutable default value" }
|
||||
override string toString() { result = "mutable default value" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
nonEmpty = false and kind instanceof EmptyMutableValue
|
||||
or
|
||||
nonEmpty = true and kind instanceof NonEmptyMutableValue
|
||||
}
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
nonEmpty = false and kind instanceof EmptyMutableValue
|
||||
or
|
||||
nonEmpty = true and kind instanceof NonEmptyMutableValue
|
||||
}
|
||||
}
|
||||
|
||||
private ClassValue mutable_class() {
|
||||
result = Value::named("list") or
|
||||
result = Value::named("dict")
|
||||
result = Value::named("list") or
|
||||
result = Value::named("dict")
|
||||
}
|
||||
|
||||
class Mutation extends TaintSink {
|
||||
Mutation() {
|
||||
exists(AugAssign a | a.getTarget().getAFlowNode() = this)
|
||||
or
|
||||
exists(Call c, Attribute a | c.getFunc() = a |
|
||||
a.getObject().getAFlowNode() = this and
|
||||
not safe_method(a.getName()) and
|
||||
this.(ControlFlowNode).pointsTo().getClass() = mutable_class()
|
||||
)
|
||||
}
|
||||
Mutation() {
|
||||
exists(AugAssign a | a.getTarget().getAFlowNode() = this)
|
||||
or
|
||||
exists(Call c, Attribute a | c.getFunc() = a |
|
||||
a.getObject().getAFlowNode() = this and
|
||||
not safe_method(a.getName()) and
|
||||
this.(ControlFlowNode).pointsTo().getClass() = mutable_class()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof EmptyMutableValue
|
||||
or
|
||||
kind instanceof NonEmptyMutableValue
|
||||
}
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof EmptyMutableValue
|
||||
or
|
||||
kind instanceof NonEmptyMutableValue
|
||||
}
|
||||
}
|
||||
|
||||
from TaintedPathSource src, TaintedPathSink sink
|
||||
where src.flowsTo(sink)
|
||||
select sink.getSink(), src, sink, "$@ flows to here and is mutated.", src.getSource(),
|
||||
"Default value"
|
||||
"Default value"
|
||||
|
||||
@@ -15,36 +15,36 @@
|
||||
import python
|
||||
|
||||
predicate first_arg_cls(Function f) {
|
||||
exists(string argname | argname = f.getArgName(0) |
|
||||
argname = "cls"
|
||||
or
|
||||
/* Not PEP8, but relatively common */
|
||||
argname = "mcls"
|
||||
)
|
||||
exists(string argname | argname = f.getArgName(0) |
|
||||
argname = "cls"
|
||||
or
|
||||
/* Not PEP8, but relatively common */
|
||||
argname = "mcls"
|
||||
)
|
||||
}
|
||||
|
||||
predicate is_type_method(Function f) {
|
||||
exists(ClassValue c | c.getScope() = f.getScope() and c.getASuperType() = ClassValue::type())
|
||||
exists(ClassValue c | c.getScope() = f.getScope() and c.getASuperType() = ClassValue::type())
|
||||
}
|
||||
|
||||
predicate classmethod_decorators_only(Function f) {
|
||||
forall(Expr decorator | decorator = f.getADecorator() | decorator.(Name).getId() = "classmethod")
|
||||
forall(Expr decorator | decorator = f.getADecorator() | decorator.(Name).getId() = "classmethod")
|
||||
}
|
||||
|
||||
from Function f, string message
|
||||
where
|
||||
(f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and
|
||||
not first_arg_cls(f) and
|
||||
classmethod_decorators_only(f) and
|
||||
not f.getName() = "__new__" and
|
||||
(
|
||||
if exists(f.getArgName(0))
|
||||
then
|
||||
message =
|
||||
"Class methods or methods of a type deriving from type should have 'cls', rather than '" +
|
||||
f.getArgName(0) + "', as their first parameter."
|
||||
else
|
||||
message =
|
||||
"Class methods or methods of a type deriving from type should have 'cls' as their first parameter."
|
||||
)
|
||||
(f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and
|
||||
not first_arg_cls(f) and
|
||||
classmethod_decorators_only(f) and
|
||||
not f.getName() = "__new__" and
|
||||
(
|
||||
if exists(f.getArgName(0))
|
||||
then
|
||||
message =
|
||||
"Class methods or methods of a type deriving from type should have 'cls', rather than '" +
|
||||
f.getArgName(0) + "', as their first parameter."
|
||||
else
|
||||
message =
|
||||
"Class methods or methods of a type deriving from type should have 'cls' as their first parameter."
|
||||
)
|
||||
select f, message
|
||||
|
||||
@@ -17,42 +17,42 @@ import python
|
||||
import semmle.python.libraries.Zope
|
||||
|
||||
predicate is_type_method(FunctionValue fv) {
|
||||
exists(ClassValue c | c.declaredAttribute(_) = fv and c.getASuperType() = ClassValue::type())
|
||||
exists(ClassValue c | c.declaredAttribute(_) = fv and c.getASuperType() = ClassValue::type())
|
||||
}
|
||||
|
||||
predicate used_in_defining_scope(FunctionValue fv) {
|
||||
exists(Call c | c.getScope() = fv.getScope().getScope() and c.getFunc().pointsTo(fv))
|
||||
exists(Call c | c.getScope() = fv.getScope().getScope() and c.getFunc().pointsTo(fv))
|
||||
}
|
||||
|
||||
from Function f, FunctionValue fv, string message
|
||||
where
|
||||
exists(ClassValue cls, string name |
|
||||
cls.declaredAttribute(name) = fv and
|
||||
cls.isNewStyle() and
|
||||
not name = "__new__" and
|
||||
not name = "__metaclass__" and
|
||||
not name = "__init_subclass__" and
|
||||
not name = "__class_getitem__" and
|
||||
/* declared in scope */
|
||||
f.getScope() = cls.getScope()
|
||||
) and
|
||||
not f.getArgName(0) = "self" and
|
||||
not is_type_method(fv) and
|
||||
fv.getScope() = f and
|
||||
not f.getName() = "lambda" and
|
||||
not used_in_defining_scope(fv) and
|
||||
exists(ClassValue cls, string name |
|
||||
cls.declaredAttribute(name) = fv and
|
||||
cls.isNewStyle() and
|
||||
not name = "__new__" and
|
||||
not name = "__metaclass__" and
|
||||
not name = "__init_subclass__" and
|
||||
not name = "__class_getitem__" and
|
||||
/* declared in scope */
|
||||
f.getScope() = cls.getScope()
|
||||
) and
|
||||
not f.getArgName(0) = "self" and
|
||||
not is_type_method(fv) and
|
||||
fv.getScope() = f and
|
||||
not f.getName() = "lambda" and
|
||||
not used_in_defining_scope(fv) and
|
||||
(
|
||||
(
|
||||
(
|
||||
if exists(f.getArgName(0))
|
||||
then
|
||||
message =
|
||||
"Normal methods should have 'self', rather than '" + f.getArgName(0) +
|
||||
"', as their first parameter."
|
||||
else
|
||||
message =
|
||||
"Normal methods should have at least one parameter (the first of which should be 'self')."
|
||||
) and
|
||||
not f.hasVarArg()
|
||||
if exists(f.getArgName(0))
|
||||
then
|
||||
message =
|
||||
"Normal methods should have 'self', rather than '" + f.getArgName(0) +
|
||||
"', as their first parameter."
|
||||
else
|
||||
message =
|
||||
"Normal methods should have at least one parameter (the first of which should be 'self')."
|
||||
) and
|
||||
not fv instanceof ZopeInterfaceMethodValue
|
||||
not f.hasVarArg()
|
||||
) and
|
||||
not fv instanceof ZopeInterfaceMethodValue
|
||||
select f, message
|
||||
|
||||
@@ -17,8 +17,8 @@ import python
|
||||
|
||||
from FunctionValue method
|
||||
where
|
||||
exists(ClassValue c |
|
||||
c.declaredAttribute("__del__") = method and
|
||||
method.getScope().getMetrics().getCyclomaticComplexity() > 3
|
||||
)
|
||||
exists(ClassValue c |
|
||||
c.declaredAttribute("__del__") = method and
|
||||
method.getScope().getMetrics().getCyclomaticComplexity() > 3
|
||||
)
|
||||
select method, "Overly complex '__del__' method."
|
||||
|
||||
@@ -13,18 +13,18 @@
|
||||
import python
|
||||
|
||||
predicate returns_tuple_of_size(Function func, int size, AstNode origin) {
|
||||
exists(Return return, TupleValue val |
|
||||
return.getScope() = func and
|
||||
return.getValue().pointsTo(val, origin)
|
||||
|
|
||||
size = val.length()
|
||||
)
|
||||
exists(Return return, TupleValue val |
|
||||
return.getScope() = func and
|
||||
return.getValue().pointsTo(val, origin)
|
||||
|
|
||||
size = val.length()
|
||||
)
|
||||
}
|
||||
|
||||
from Function func, int s1, int s2, AstNode t1, AstNode t2
|
||||
where
|
||||
returns_tuple_of_size(func, s1, t1) and
|
||||
returns_tuple_of_size(func, s2, t2) and
|
||||
s1 < s2
|
||||
returns_tuple_of_size(func, s1, t1) and
|
||||
returns_tuple_of_size(func, s2, t2) and
|
||||
s1 < s2
|
||||
select func, func.getQualifiedName() + " returns $@ and $@.", t1, "tuple of size " + s1, t2,
|
||||
"tuple of size " + s2
|
||||
"tuple of size " + s2
|
||||
|
||||
@@ -18,66 +18,66 @@ import python
|
||||
import semmle.python.objects.Callables
|
||||
|
||||
predicate meaningful_return_value(Expr val) {
|
||||
val instanceof Name
|
||||
or
|
||||
val instanceof BooleanLiteral
|
||||
or
|
||||
exists(FunctionValue callee |
|
||||
val = callee.getACall().getNode() and returns_meaningful_value(callee)
|
||||
)
|
||||
or
|
||||
not exists(FunctionValue callee | val = callee.getACall().getNode()) and not val instanceof Name
|
||||
val instanceof Name
|
||||
or
|
||||
val instanceof BooleanLiteral
|
||||
or
|
||||
exists(FunctionValue callee |
|
||||
val = callee.getACall().getNode() and returns_meaningful_value(callee)
|
||||
)
|
||||
or
|
||||
not exists(FunctionValue callee | val = callee.getACall().getNode()) and not val instanceof Name
|
||||
}
|
||||
|
||||
/* Value is used before returning, and thus its value is not lost if ignored */
|
||||
predicate used_value(Expr val) {
|
||||
exists(LocalVariable var, Expr other |
|
||||
var.getAnAccess() = val and other = var.getAnAccess() and not other = val
|
||||
)
|
||||
exists(LocalVariable var, Expr other |
|
||||
var.getAnAccess() = val and other = var.getAnAccess() and not other = val
|
||||
)
|
||||
}
|
||||
|
||||
predicate returns_meaningful_value(FunctionValue f) {
|
||||
not exists(f.getScope().getFallthroughNode()) and
|
||||
(
|
||||
exists(Return ret, Expr val | ret.getScope() = f.getScope() and val = ret.getValue() |
|
||||
meaningful_return_value(val) and
|
||||
not used_value(val)
|
||||
)
|
||||
or
|
||||
/*
|
||||
* Is f a builtin function that returns something other than None?
|
||||
* Ignore __import__ as it is often called purely for side effects
|
||||
*/
|
||||
|
||||
f.isBuiltin() and
|
||||
f.getAnInferredReturnType() != ClassValue::nonetype() and
|
||||
not f.getName() = "__import__"
|
||||
not exists(f.getScope().getFallthroughNode()) and
|
||||
(
|
||||
exists(Return ret, Expr val | ret.getScope() = f.getScope() and val = ret.getValue() |
|
||||
meaningful_return_value(val) and
|
||||
not used_value(val)
|
||||
)
|
||||
or
|
||||
/*
|
||||
* Is f a builtin function that returns something other than None?
|
||||
* Ignore __import__ as it is often called purely for side effects
|
||||
*/
|
||||
|
||||
f.isBuiltin() and
|
||||
f.getAnInferredReturnType() != ClassValue::nonetype() and
|
||||
not f.getName() = "__import__"
|
||||
)
|
||||
}
|
||||
|
||||
/* If a call is wrapped tightly in a try-except then we assume it is being executed for the exception. */
|
||||
predicate wrapped_in_try_except(ExprStmt call) {
|
||||
exists(Try t |
|
||||
exists(t.getAHandler()) and
|
||||
strictcount(Call c | t.getBody().contains(c)) = 1 and
|
||||
call = t.getAStmt()
|
||||
)
|
||||
exists(Try t |
|
||||
exists(t.getAHandler()) and
|
||||
strictcount(Call c | t.getBody().contains(c)) = 1 and
|
||||
call = t.getAStmt()
|
||||
)
|
||||
}
|
||||
|
||||
from ExprStmt call, FunctionValue callee, float percentage_used, int total
|
||||
where
|
||||
call.getValue() = callee.getACall().getNode() and
|
||||
returns_meaningful_value(callee) and
|
||||
not wrapped_in_try_except(call) and
|
||||
exists(int unused |
|
||||
unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and
|
||||
total = count(callee.getACall())
|
||||
|
|
||||
percentage_used = (100.0 * (total - unused) / total).floor()
|
||||
) and
|
||||
/* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */
|
||||
percentage_used >= 75 and
|
||||
total >= 5
|
||||
call.getValue() = callee.getACall().getNode() and
|
||||
returns_meaningful_value(callee) and
|
||||
not wrapped_in_try_except(call) and
|
||||
exists(int unused |
|
||||
unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and
|
||||
total = count(callee.getACall())
|
||||
|
|
||||
percentage_used = (100.0 * (total - unused) / total).floor()
|
||||
) and
|
||||
/* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */
|
||||
percentage_used >= 75 and
|
||||
total >= 5
|
||||
select call,
|
||||
"Call discards return value of function $@. The result is used in " + percentage_used.toString() +
|
||||
"% of calls.", callee, callee.getName()
|
||||
"Call discards return value of function $@. The result is used in " + percentage_used.toString() +
|
||||
"% of calls.", callee, callee.getName()
|
||||
|
||||
@@ -16,20 +16,20 @@ import Expressions.CallArgs
|
||||
|
||||
from FunctionValue base, PythonFunctionValue derived
|
||||
where
|
||||
not exists(base.getACall()) and
|
||||
not exists(FunctionValue a_derived |
|
||||
a_derived.overrides(base) and
|
||||
exists(a_derived.getACall())
|
||||
) and
|
||||
not derived.getScope().isSpecialMethod() and
|
||||
derived.getName() != "__init__" and
|
||||
derived.isNormalMethod() and
|
||||
not derived.getScope().isSpecialMethod() and
|
||||
// call to overrides distributed for efficiency
|
||||
(
|
||||
derived.overrides(base) and derived.minParameters() > base.maxParameters()
|
||||
or
|
||||
derived.overrides(base) and derived.maxParameters() < base.minParameters()
|
||||
)
|
||||
not exists(base.getACall()) and
|
||||
not exists(FunctionValue a_derived |
|
||||
a_derived.overrides(base) and
|
||||
exists(a_derived.getACall())
|
||||
) and
|
||||
not derived.getScope().isSpecialMethod() and
|
||||
derived.getName() != "__init__" and
|
||||
derived.isNormalMethod() and
|
||||
not derived.getScope().isSpecialMethod() and
|
||||
// call to overrides distributed for efficiency
|
||||
(
|
||||
derived.overrides(base) and derived.minParameters() > base.maxParameters()
|
||||
or
|
||||
derived.overrides(base) and derived.maxParameters() < base.minParameters()
|
||||
)
|
||||
select derived, "Overriding method '" + derived.getName() + "' has signature mismatch with $@.",
|
||||
base, "overridden method"
|
||||
base, "overridden method"
|
||||
|
||||
@@ -13,200 +13,200 @@
|
||||
import python
|
||||
|
||||
predicate is_unary_op(string name) {
|
||||
name = "__del__" or
|
||||
name = "__repr__" or
|
||||
name = "__str__" or
|
||||
name = "__hash__" or
|
||||
name = "__bool__" or
|
||||
name = "__nonzero__" or
|
||||
name = "__unicode__" or
|
||||
name = "__len__" or
|
||||
name = "__iter__" or
|
||||
name = "__reversed__" or
|
||||
name = "__neg__" or
|
||||
name = "__pos__" or
|
||||
name = "__abs__" or
|
||||
name = "__invert__" or
|
||||
name = "__complex__" or
|
||||
name = "__int__" or
|
||||
name = "__float__" or
|
||||
name = "__long__" or
|
||||
name = "__oct__" or
|
||||
name = "__hex__" or
|
||||
name = "__index__" or
|
||||
name = "__enter__"
|
||||
name = "__del__" or
|
||||
name = "__repr__" or
|
||||
name = "__str__" or
|
||||
name = "__hash__" or
|
||||
name = "__bool__" or
|
||||
name = "__nonzero__" or
|
||||
name = "__unicode__" or
|
||||
name = "__len__" or
|
||||
name = "__iter__" or
|
||||
name = "__reversed__" or
|
||||
name = "__neg__" or
|
||||
name = "__pos__" or
|
||||
name = "__abs__" or
|
||||
name = "__invert__" or
|
||||
name = "__complex__" or
|
||||
name = "__int__" or
|
||||
name = "__float__" or
|
||||
name = "__long__" or
|
||||
name = "__oct__" or
|
||||
name = "__hex__" or
|
||||
name = "__index__" or
|
||||
name = "__enter__"
|
||||
}
|
||||
|
||||
predicate is_binary_op(string name) {
|
||||
name = "__lt__" or
|
||||
name = "__le__" or
|
||||
name = "__eq__" or
|
||||
name = "__ne__" or
|
||||
name = "__gt__" or
|
||||
name = "__ge__" or
|
||||
name = "__cmp__" or
|
||||
name = "__rcmp__" or
|
||||
name = "__getattr___" or
|
||||
name = "__getattribute___" or
|
||||
name = "__delattr__" or
|
||||
name = "__delete__" or
|
||||
name = "__instancecheck__" or
|
||||
name = "__subclasscheck__" or
|
||||
name = "__getitem__" or
|
||||
name = "__delitem__" or
|
||||
name = "__contains__" or
|
||||
name = "__add__" or
|
||||
name = "__sub__" or
|
||||
name = "__mul__" or
|
||||
name = "__floordiv__" or
|
||||
name = "__div__" or
|
||||
name = "__truediv__" or
|
||||
name = "__mod__" or
|
||||
name = "__divmod__" or
|
||||
name = "__lshift__" or
|
||||
name = "__rshift__" or
|
||||
name = "__and__" or
|
||||
name = "__xor__" or
|
||||
name = "__or__" or
|
||||
name = "__radd__" or
|
||||
name = "__rsub__" or
|
||||
name = "__rmul__" or
|
||||
name = "__rfloordiv__" or
|
||||
name = "__rdiv__" or
|
||||
name = "__rtruediv__" or
|
||||
name = "__rmod__" or
|
||||
name = "__rdivmod__" or
|
||||
name = "__rpow__" or
|
||||
name = "__rlshift__" or
|
||||
name = "__rrshift__" or
|
||||
name = "__rand__" or
|
||||
name = "__rxor__" or
|
||||
name = "__ror__" or
|
||||
name = "__iadd__" or
|
||||
name = "__isub__" or
|
||||
name = "__imul__" or
|
||||
name = "__ifloordiv__" or
|
||||
name = "__idiv__" or
|
||||
name = "__itruediv__" or
|
||||
name = "__imod__" or
|
||||
name = "__idivmod__" or
|
||||
name = "__ipow__" or
|
||||
name = "__ilshift__" or
|
||||
name = "__irshift__" or
|
||||
name = "__iand__" or
|
||||
name = "__ixor__" or
|
||||
name = "__ior__" or
|
||||
name = "__coerce__"
|
||||
name = "__lt__" or
|
||||
name = "__le__" or
|
||||
name = "__eq__" or
|
||||
name = "__ne__" or
|
||||
name = "__gt__" or
|
||||
name = "__ge__" or
|
||||
name = "__cmp__" or
|
||||
name = "__rcmp__" or
|
||||
name = "__getattr___" or
|
||||
name = "__getattribute___" or
|
||||
name = "__delattr__" or
|
||||
name = "__delete__" or
|
||||
name = "__instancecheck__" or
|
||||
name = "__subclasscheck__" or
|
||||
name = "__getitem__" or
|
||||
name = "__delitem__" or
|
||||
name = "__contains__" or
|
||||
name = "__add__" or
|
||||
name = "__sub__" or
|
||||
name = "__mul__" or
|
||||
name = "__floordiv__" or
|
||||
name = "__div__" or
|
||||
name = "__truediv__" or
|
||||
name = "__mod__" or
|
||||
name = "__divmod__" or
|
||||
name = "__lshift__" or
|
||||
name = "__rshift__" or
|
||||
name = "__and__" or
|
||||
name = "__xor__" or
|
||||
name = "__or__" or
|
||||
name = "__radd__" or
|
||||
name = "__rsub__" or
|
||||
name = "__rmul__" or
|
||||
name = "__rfloordiv__" or
|
||||
name = "__rdiv__" or
|
||||
name = "__rtruediv__" or
|
||||
name = "__rmod__" or
|
||||
name = "__rdivmod__" or
|
||||
name = "__rpow__" or
|
||||
name = "__rlshift__" or
|
||||
name = "__rrshift__" or
|
||||
name = "__rand__" or
|
||||
name = "__rxor__" or
|
||||
name = "__ror__" or
|
||||
name = "__iadd__" or
|
||||
name = "__isub__" or
|
||||
name = "__imul__" or
|
||||
name = "__ifloordiv__" or
|
||||
name = "__idiv__" or
|
||||
name = "__itruediv__" or
|
||||
name = "__imod__" or
|
||||
name = "__idivmod__" or
|
||||
name = "__ipow__" or
|
||||
name = "__ilshift__" or
|
||||
name = "__irshift__" or
|
||||
name = "__iand__" or
|
||||
name = "__ixor__" or
|
||||
name = "__ior__" or
|
||||
name = "__coerce__"
|
||||
}
|
||||
|
||||
predicate is_ternary_op(string name) {
|
||||
name = "__setattr__" or
|
||||
name = "__set__" or
|
||||
name = "__setitem__" or
|
||||
name = "__getslice__" or
|
||||
name = "__delslice__"
|
||||
name = "__setattr__" or
|
||||
name = "__set__" or
|
||||
name = "__setitem__" or
|
||||
name = "__getslice__" or
|
||||
name = "__delslice__"
|
||||
}
|
||||
|
||||
predicate is_quad_op(string name) { name = "__setslice__" or name = "__exit__" }
|
||||
|
||||
int argument_count(PythonFunctionValue f, string name, ClassValue cls) {
|
||||
cls.declaredAttribute(name) = f and
|
||||
(
|
||||
is_unary_op(name) and result = 1
|
||||
or
|
||||
is_binary_op(name) and result = 2
|
||||
or
|
||||
is_ternary_op(name) and result = 3
|
||||
or
|
||||
is_quad_op(name) and result = 4
|
||||
)
|
||||
cls.declaredAttribute(name) = f and
|
||||
(
|
||||
is_unary_op(name) and result = 1
|
||||
or
|
||||
is_binary_op(name) and result = 2
|
||||
or
|
||||
is_ternary_op(name) and result = 3
|
||||
or
|
||||
is_quad_op(name) and result = 4
|
||||
)
|
||||
}
|
||||
|
||||
predicate incorrect_special_method_defn(
|
||||
PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner
|
||||
PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner
|
||||
) {
|
||||
exists(int required | required = argument_count(func, name, owner) |
|
||||
/* actual_non_default <= actual */
|
||||
if required > func.maxParameters()
|
||||
then message = "Too few parameters" and show_counts = true
|
||||
else
|
||||
if required < func.minParameters()
|
||||
then message = "Too many parameters" and show_counts = true
|
||||
else
|
||||
if func.minParameters() < required and not func.getScope().hasVarArg()
|
||||
then
|
||||
message = (required - func.minParameters()) + " default values(s) will never be used" and
|
||||
show_counts = false
|
||||
else none()
|
||||
)
|
||||
exists(int required | required = argument_count(func, name, owner) |
|
||||
/* actual_non_default <= actual */
|
||||
if required > func.maxParameters()
|
||||
then message = "Too few parameters" and show_counts = true
|
||||
else
|
||||
if required < func.minParameters()
|
||||
then message = "Too many parameters" and show_counts = true
|
||||
else
|
||||
if func.minParameters() < required and not func.getScope().hasVarArg()
|
||||
then
|
||||
message = (required - func.minParameters()) + " default values(s) will never be used" and
|
||||
show_counts = false
|
||||
else none()
|
||||
)
|
||||
}
|
||||
|
||||
predicate incorrect_pow(FunctionValue func, string message, boolean show_counts, ClassValue owner) {
|
||||
owner.declaredAttribute("__pow__") = func and
|
||||
(
|
||||
func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true
|
||||
or
|
||||
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
|
||||
or
|
||||
func.minParameters() < 2 and
|
||||
message = (2 - func.minParameters()) + " default value(s) will never be used" and
|
||||
show_counts = false
|
||||
or
|
||||
func.minParameters() = 3 and
|
||||
message = "Third parameter to __pow__ should have a default value" and
|
||||
show_counts = false
|
||||
)
|
||||
owner.declaredAttribute("__pow__") = func and
|
||||
(
|
||||
func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true
|
||||
or
|
||||
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
|
||||
or
|
||||
func.minParameters() < 2 and
|
||||
message = (2 - func.minParameters()) + " default value(s) will never be used" and
|
||||
show_counts = false
|
||||
or
|
||||
func.minParameters() = 3 and
|
||||
message = "Third parameter to __pow__ should have a default value" and
|
||||
show_counts = false
|
||||
)
|
||||
}
|
||||
|
||||
predicate incorrect_get(FunctionValue func, string message, boolean show_counts, ClassValue owner) {
|
||||
owner.declaredAttribute("__get__") = func and
|
||||
(
|
||||
func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true
|
||||
or
|
||||
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
|
||||
or
|
||||
func.minParameters() < 2 and
|
||||
not func.getScope().hasVarArg() and
|
||||
message = (2 - func.minParameters()) + " default value(s) will never be used" and
|
||||
show_counts = false
|
||||
)
|
||||
owner.declaredAttribute("__get__") = func and
|
||||
(
|
||||
func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true
|
||||
or
|
||||
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
|
||||
or
|
||||
func.minParameters() < 2 and
|
||||
not func.getScope().hasVarArg() and
|
||||
message = (2 - func.minParameters()) + " default value(s) will never be used" and
|
||||
show_counts = false
|
||||
)
|
||||
}
|
||||
|
||||
string should_have_parameters(PythonFunctionValue f, string name, ClassValue owner) {
|
||||
exists(int i | i = argument_count(f, name, owner) | result = i.toString())
|
||||
or
|
||||
owner.declaredAttribute(name) = f and
|
||||
(name = "__get__" or name = "__pow__") and
|
||||
result = "2 or 3"
|
||||
exists(int i | i = argument_count(f, name, owner) | result = i.toString())
|
||||
or
|
||||
owner.declaredAttribute(name) = f and
|
||||
(name = "__get__" or name = "__pow__") and
|
||||
result = "2 or 3"
|
||||
}
|
||||
|
||||
string has_parameters(PythonFunctionValue f) {
|
||||
exists(int i | i = f.minParameters() |
|
||||
i = 0 and result = "no parameters"
|
||||
or
|
||||
i = 1 and result = "1 parameter"
|
||||
or
|
||||
i > 1 and result = i.toString() + " parameters"
|
||||
)
|
||||
exists(int i | i = f.minParameters() |
|
||||
i = 0 and result = "no parameters"
|
||||
or
|
||||
i = 1 and result = "1 parameter"
|
||||
or
|
||||
i > 1 and result = i.toString() + " parameters"
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
PythonFunctionValue f, string message, string sizes, boolean show_counts, string name,
|
||||
ClassValue owner
|
||||
PythonFunctionValue f, string message, string sizes, boolean show_counts, string name,
|
||||
ClassValue owner
|
||||
where
|
||||
(
|
||||
incorrect_special_method_defn(f, message, show_counts, name, owner)
|
||||
or
|
||||
incorrect_pow(f, message, show_counts, owner) and name = "__pow__"
|
||||
or
|
||||
incorrect_get(f, message, show_counts, owner) and name = "__get__"
|
||||
) and
|
||||
(
|
||||
show_counts = false and sizes = ""
|
||||
or
|
||||
show_counts = true and
|
||||
sizes =
|
||||
", which has " + has_parameters(f) + ", but should have " +
|
||||
should_have_parameters(f, name, owner)
|
||||
)
|
||||
(
|
||||
incorrect_special_method_defn(f, message, show_counts, name, owner)
|
||||
or
|
||||
incorrect_pow(f, message, show_counts, owner) and name = "__pow__"
|
||||
or
|
||||
incorrect_get(f, message, show_counts, owner) and name = "__get__"
|
||||
) and
|
||||
(
|
||||
show_counts = false and sizes = ""
|
||||
or
|
||||
show_counts = true and
|
||||
sizes =
|
||||
", which has " + has_parameters(f) + ", but should have " +
|
||||
should_have_parameters(f, name, owner)
|
||||
)
|
||||
select f, message + " for special method " + name + sizes + ", in class $@.", owner, owner.getName()
|
||||
|
||||
@@ -13,26 +13,26 @@ import python
|
||||
import Testing.Mox
|
||||
|
||||
predicate is_used(Call c) {
|
||||
exists(Expr outer | outer != c and outer.containsInScope(c) |
|
||||
outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript
|
||||
)
|
||||
or
|
||||
exists(Stmt s |
|
||||
c = s.getASubExpression() and
|
||||
not s instanceof ExprStmt and
|
||||
/* Ignore if a single return, as def f(): return g() is quite common. Covers implicit return in a lambda. */
|
||||
not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1)
|
||||
)
|
||||
exists(Expr outer | outer != c and outer.containsInScope(c) |
|
||||
outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript
|
||||
)
|
||||
or
|
||||
exists(Stmt s |
|
||||
c = s.getASubExpression() and
|
||||
not s instanceof ExprStmt and
|
||||
/* Ignore if a single return, as def f(): return g() is quite common. Covers implicit return in a lambda. */
|
||||
not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1)
|
||||
)
|
||||
}
|
||||
|
||||
from Call c, FunctionValue func
|
||||
where
|
||||
/* Call result is used, but callee is a procedure */
|
||||
is_used(c) and
|
||||
c.getFunc().pointsTo(func) and
|
||||
func.getScope().isProcedure() and
|
||||
/* All callees are procedures */
|
||||
forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and
|
||||
/* Mox return objects have an `AndReturn` method */
|
||||
not useOfMoxInModule(c.getEnclosingModule())
|
||||
/* Call result is used, but callee is a procedure */
|
||||
is_used(c) and
|
||||
c.getFunc().pointsTo(func) and
|
||||
func.getScope().isProcedure() and
|
||||
/* All callees are procedures */
|
||||
forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and
|
||||
/* Mox return objects have an `AndReturn` method */
|
||||
not useOfMoxInModule(c.getEnclosingModule())
|
||||
select c, "The result of '$@' is used even though it is always None.", func, func.getQualifiedName()
|
||||
|
||||
Reference in New Issue
Block a user