Files
codeql/python/ql/src/Functions/SignatureSpecialMethods.ql
2021-10-26 12:55:25 +02:00

144 lines
5.0 KiB
Plaintext

/**
* @name Special method has incorrect signature
* @description Special method has incorrect signature
* @kind problem
* @tags reliability
* correctness
* @problem.severity error
* @sub-severity low
* @precision high
* @id py/special-method-wrong-signature
*/
import python
predicate is_unary_op(string name) {
name in [
"__del__", "__repr__", "__neg__", "__pos__", "__abs__", "__invert__", "__complex__",
"__int__", "__float__", "__long__", "__oct__", "__hex__", "__str__", "__index__", "__enter__",
"__hash__", "__bool__", "__nonzero__", "__unicode__", "__len__", "__iter__", "__reversed__"
]
}
predicate is_binary_op(string name) {
name in [
"__lt__", "__le__", "__delattr__", "__delete__", "__instancecheck__", "__subclasscheck__",
"__getitem__", "__delitem__", "__contains__", "__add__", "__sub__", "__mul__", "__eq__",
"__floordiv__", "__div__", "__truediv__", "__mod__", "__divmod__", "__lshift__", "__rshift__",
"__and__", "__xor__", "__or__", "__ne__", "__radd__", "__rsub__", "__rmul__", "__rfloordiv__",
"__rdiv__", "__rtruediv__", "__rmod__", "__rdivmod__", "__rpow__", "__rlshift__", "__gt__",
"__rrshift__", "__rand__", "__rxor__", "__ror__", "__iadd__", "__isub__", "__imul__",
"__ifloordiv__", "__idiv__", "__itruediv__", "__ge__", "__imod__", "__idivmod__", "__ipow__",
"__ilshift__", "__irshift__", "__iand__", "__ixor__", "__ior__", "__coerce__", "__cmp__",
"__rcmp__", "__getattr___", "__getattribute___"
]
}
predicate is_ternary_op(string name) {
name in ["__setattr__", "__set__", "__setitem__", "__getslice__", "__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
)
}
predicate incorrect_special_method_defn(
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 (
func.minParameters() < required and
not func.getScope().hasVarArg() and
message = (required - func.minParameters()) + " default values(s) will never be used" and
show_counts = false
)
)
}
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
)
}
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
)
}
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"
}
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"
)
}
from
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)
)
select f, message + " for special method " + name + sizes + ", in class $@.", owner, owner.getName()