mirror of
https://github.com/github/codeql.git
synced 2025-12-24 20:56:33 +01:00
201 lines
5.9 KiB
Plaintext
201 lines
5.9 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 = "__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__"
|
|
}
|
|
|
|
predicate is_ternary_op(string name) {
|
|
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(PyFunctionObject f, string name, ClassObject 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(PyFunctionObject func, string message, boolean show_counts, string name, ClassObject 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.getFunction().hasVarArg()) then
|
|
(message = (required -func.minParameters()) + " default values(s) will never be used" and show_counts = false)
|
|
else
|
|
none()
|
|
)
|
|
}
|
|
|
|
predicate incorrect_pow(FunctionObject func, string message, boolean show_counts, ClassObject 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(FunctionObject func, string message, boolean show_counts, ClassObject 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.getFunction().hasVarArg() and
|
|
message = (2 - func.minParameters()) + " default value(s) will never be used" and show_counts = false
|
|
)
|
|
}
|
|
|
|
string should_have_parameters(PyFunctionObject f, string name, ClassObject 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(PyFunctionObject 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 PyFunctionObject f, string message, string sizes, boolean show_counts, string name, ClassObject 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()
|