Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 29 additions & 26 deletions python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql
Original file line number Diff line number Diff line change
Expand Up @@ -16,51 +16,52 @@
import semmle.python.dataflow.new.internal.DataFlowDispatch

private predicate attributeMethod(string name) {
name = "__getattribute__" or name = "__getattr__" or name = "__setattr__"
name = ["__getattribute__", "__getattr__"] // __setattr__ excluded as it makes sense to raise different kinds of errors based on the `value` parameter
}

private predicate indexingMethod(string name) {
name = "__getitem__" or name = "__setitem__" or name = "__delitem__"
name = ["__getitem__", "__delitem__"] // __setitem__ excluded as it makes sense to raise different kinds of errors based on the `value` parameter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding these comments! ❤️

}

private predicate arithmeticMethod(string name) {
name in [
name =
[
"__add__", "__sub__", "__or__", "__xor__", "__rshift__", "__pow__", "__mul__", "__neg__",
"__radd__", "__rsub__", "__rdiv__", "__rfloordiv__", "__div__", "__rdiv__", "__rlshift__",
"__rand__", "__ror__", "__rxor__", "__rrshift__", "__rpow__", "__rmul__", "__truediv__",
"__rtruediv__", "__pos__", "__iadd__", "__isub__", "__idiv__", "__ifloordiv__", "__idiv__",

Check warning

Code scanning / CodeQL

Redundant assignment. Warning

The variable name
has previously been assigned
the same value
.
"__ilshift__", "__iand__", "__ior__", "__ixor__", "__irshift__", "__abs__", "__ipow__",
"__imul__", "__itruediv__", "__floordiv__", "__div__", "__divmod__", "__lshift__", "__and__"

Check warning

Code scanning / CodeQL

Redundant assignment. Warning

The variable name
has previously been assigned
the same value
.
]
}

private predicate orderingMethod(string name) {
name = "__lt__"
or
name = "__le__"
or
name = "__gt__"
or
name = "__ge__"
name =
[
"__lt__",
"__le__",
"__gt__",
"__ge__",
]
}

private predicate castMethod(string name) {
name = "__int__"
or
name = "__float__"
or
name = "__long__"
or
name = "__trunc__"
or
name = "__complex__"
name =
[
"__int__",
"__float__",
"__long__",
"__trunc__",
"__complex__"
]
}

predicate correctRaise(string name, Expr exec) {
execIsOfType(exec, "TypeError") and
(
indexingMethod(name) or
attributeMethod(name)
attributeMethod(name) or
name = ["__add__", "__iadd__", "__radd__"]
)
or
exists(string execName |
Expand All @@ -81,11 +82,11 @@
or
orderingMethod(name) and
execName = "TypeError" and
message = "should raise a TypeError, or return NotImplemented instead."
message = "should raise a TypeError or return NotImplemented instead."
or
arithmeticMethod(name) and
execName = "ArithmeticError" and
message = "should raise an ArithmeticError, or return NotImplemented instead."
message = "should raise an ArithmeticError or return NotImplemented instead."
or
name = "__bool__" and
execName = "TypeError" and
Expand Down Expand Up @@ -120,6 +121,7 @@
castMethod(meth.getName()) and
message = "this method does not need to be implemented." and
allowNotImplemented = true and
// Allow an always raising cast method if it's overriding other behavior
not exists(Function overridden |
overridden.getName() = meth.getName() and
overridden.getScope() = getADirectSuperclass+(meth.getScope()) and
Expand All @@ -139,7 +141,7 @@
exists(Raise r |
r.getScope() = f and
exec = r.getException() and
not exec = API::builtin("StopIteration").asSource().asExpr()
exec instanceof Call
)
}

Expand All @@ -156,15 +158,16 @@
exists(boolean allowNotImplemented, string subMessage |
alwaysRaises(f, exec) and
noNeedToAlwaysRaise(f, subMessage, allowNotImplemented) and
(allowNotImplemented = false or not isNotImplementedError(exec)) and
(allowNotImplemented = true implies not isNotImplementedError(exec)) and // don't alert if it's a NotImplementedError and that's ok
message = "This method always raises $@ - " + subMessage
)
or
alwaysRaises(f, exec) and // for now consider only alwaysRaises cases as original query
not isNotImplementedError(exec) and
not correctRaise(f.getName(), exec) and
exists(string subMessage | preferredRaise(f.getName(), _, subMessage) |
message = "This method always raises $@ - " + subMessage
if alwaysRaises(f, exec)
then message = "This method always raises $@ - " + subMessage
else message = "This method raises $@ - " + subMessage
)
)
select f, message, exec, exec.toString() // TODO: remove tostring