@@ -16,15 +16,16 @@ import semmle.python.ApiGraphs
1616import semmle.python.dataflow.new.internal.DataFlowDispatch
1717
1818private predicate attributeMethod ( string name ) {
19- name = "__getattribute__" or name = "__getattr__" or name = "__setattr__"
19+ name = [ "__getattribute__" , "__getattr__" ] // __setattr__ excluded as it makes sense to raise different kinds of errors based on the `value` parameter
2020}
2121
2222private predicate indexingMethod ( string name ) {
23- name = "__getitem__" or name = " __setitem__" or name = "__delitem__"
23+ name = [ "__getitem__" , "__delitem__" ] // __setitem__ excluded as it makes sense to raise different kinds of errors based on the `value` parameter
2424}
2525
2626private predicate arithmeticMethod ( string name ) {
27- name in [
27+ name =
28+ [
2829 "__add__" , "__sub__" , "__or__" , "__xor__" , "__rshift__" , "__pow__" , "__mul__" , "__neg__" ,
2930 "__radd__" , "__rsub__" , "__rdiv__" , "__rfloordiv__" , "__div__" , "__rdiv__" , "__rlshift__" ,
3031 "__rand__" , "__ror__" , "__rxor__" , "__rrshift__" , "__rpow__" , "__rmul__" , "__truediv__" ,
@@ -35,32 +36,32 @@ private predicate arithmeticMethod(string name) {
3536}
3637
3738private predicate orderingMethod ( string name ) {
38- name = "__lt__"
39- or
40- name = "__le__"
41- or
42- name = "__gt__"
43- or
44- name = "__ge__"
39+ name =
40+ [
41+ "__lt__" ,
42+ "__le__" ,
43+ "__gt__" ,
44+ "__ge__" ,
45+ ]
4546}
4647
4748private predicate castMethod ( string name ) {
48- name = "__int__"
49- or
50- name = "__float__"
51- or
52- name = "__long__"
53- or
54- name = "__trunc__"
55- or
56- name = "__complex__"
49+ name =
50+ [
51+ "__int__" ,
52+ "__float__" ,
53+ "__long__" ,
54+ "__trunc__" ,
55+ "__complex__"
56+ ]
5757}
5858
5959predicate correctRaise ( string name , Expr exec ) {
6060 execIsOfType ( exec , "TypeError" ) and
6161 (
6262 indexingMethod ( name ) or
63- attributeMethod ( name )
63+ attributeMethod ( name ) or
64+ name = [ "__add__" , "__iadd__" , "__radd__" ]
6465 )
6566 or
6667 exists ( string execName |
@@ -81,11 +82,11 @@ predicate preferredRaise(string name, string execName, string message) {
8182 or
8283 orderingMethod ( name ) and
8384 execName = "TypeError" and
84- message = "should raise a TypeError, or return NotImplemented instead."
85+ message = "should raise a TypeError or return NotImplemented instead."
8586 or
8687 arithmeticMethod ( name ) and
8788 execName = "ArithmeticError" and
88- message = "should raise an ArithmeticError, or return NotImplemented instead."
89+ message = "should raise an ArithmeticError or return NotImplemented instead."
8990 or
9091 name = "__bool__" and
9192 execName = "TypeError" and
@@ -120,6 +121,7 @@ predicate noNeedToAlwaysRaise(Function meth, string message, boolean allowNotImp
120121 castMethod ( meth .getName ( ) ) and
121122 message = "this method does not need to be implemented." and
122123 allowNotImplemented = true and
124+ // Allow an always raising cast method if it's overriding other behavior
123125 not exists ( Function overridden |
124126 overridden .getName ( ) = meth .getName ( ) and
125127 overridden .getScope ( ) = getADirectSuperclass + ( meth .getScope ( ) ) and
@@ -139,7 +141,7 @@ predicate directlyRaises(Function f, Expr exec) {
139141 exists ( Raise r |
140142 r .getScope ( ) = f and
141143 exec = r .getException ( ) and
142- not exec = API :: builtin ( "StopIteration" ) . asSource ( ) . asExpr ( )
144+ exec instanceof Call
143145 )
144146}
145147
@@ -156,15 +158,16 @@ where
156158 exists ( boolean allowNotImplemented , string subMessage |
157159 alwaysRaises ( f , exec ) and
158160 noNeedToAlwaysRaise ( f , subMessage , allowNotImplemented ) and
159- ( allowNotImplemented = false or not isNotImplementedError ( exec ) ) and
161+ ( allowNotImplemented = true implies not isNotImplementedError ( exec ) ) and // don't alert if it's a NotImplementedError and that's ok
160162 message = "This method always raises $@ - " + subMessage
161163 )
162164 or
163- alwaysRaises ( f , exec ) and // for now consider only alwaysRaises cases as original query
164165 not isNotImplementedError ( exec ) and
165166 not correctRaise ( f .getName ( ) , exec ) and
166167 exists ( string subMessage | preferredRaise ( f .getName ( ) , _, subMessage ) |
167- message = "This method always raises $@ - " + subMessage
168+ if alwaysRaises ( f , exec )
169+ then message = "This method always raises $@ - " + subMessage
170+ else message = "This method raises $@ - " + subMessage
168171 )
169172 )
170173select f , message , exec , exec .toString ( ) // TODO: remove tostring
0 commit comments