@@ -15,14 +15,17 @@ import python
1515import semmle.python.ApiGraphs
1616import semmle.python.dataflow.new.internal.DataFlowDispatch
1717
18+ /** Holds if `name` is the name of a special method for attribute access such as `a.b`, that should raise an `AttributeError`. */
1819private predicate attributeMethod ( string name ) {
1920 name = [ "__getattribute__" , "__getattr__" , "__delattr__" ] // __setattr__ excluded as it makes sense to raise different kinds of errors based on the `value` parameter
2021}
2122
23+ /** Holds if `name` is the name of a special method for indexing operations such as `a[b]`, that should raise a `LookupError`. */
2224private predicate indexingMethod ( string name ) {
2325 name = [ "__getitem__" , "__delitem__" ] // __setitem__ excluded as it makes sense to raise different kinds of errors based on the `value` parameter
2426}
2527
28+ /** Holds if `name` is the name of a special method for arithmetic operations. */
2629private predicate arithmeticMethod ( string name ) {
2730 name =
2831 [
@@ -35,6 +38,7 @@ private predicate arithmeticMethod(string name) {
3538 ]
3639}
3740
41+ /** Holds if `name is the name of a special method for ordering operations such as `a < b`. */
3842private predicate orderingMethod ( string name ) {
3943 name =
4044 [
@@ -45,6 +49,7 @@ private predicate orderingMethod(string name) {
4549 ]
4650}
4751
52+ /** Holds if `name` is the name of a special method for casting an object to a numeric type, such as `int(x)` */
4853private predicate castMethod ( string name ) {
4954 name =
5055 [
@@ -53,9 +58,10 @@ private predicate castMethod(string name) {
5358 "__index__" ,
5459 "__trunc__" ,
5560 "__complex__"
56- ]
61+ ] // __bool__ excluded as it makes sense to allow it to always raise
5762}
5863
64+ /** Holds if we allow a special method named `name` to raise `exec` as an exception. */
5965predicate correctRaise ( string name , Expr exec ) {
6066 execIsOfType ( exec , "TypeError" ) and
6167 (
@@ -71,6 +77,7 @@ predicate correctRaise(string name, Expr exec) {
7177 )
7278}
7379
80+ /** Holds if it is preferred for `name` to raise exceptions of type `execName`. `message` is the alert message. */
7481predicate preferredRaise ( string name , string execName , string message ) {
7582 attributeMethod ( name ) and
7683 execName = "AttributeError" and
@@ -93,6 +100,7 @@ predicate preferredRaise(string name, string execName, string message) {
93100 message = "should raise a TypeError instead."
94101}
95102
103+ /** Holds if `exec` is an exception object of the type named `execName`. */
96104predicate execIsOfType ( Expr exec , string execName ) {
97105 // Might make sense to have execName be an IPA type here. Or part of a more general API modeling builtin/stdlib subclass relations.
98106 exists ( string subclass |
@@ -114,6 +122,10 @@ predicate execIsOfType(Expr exec, string execName) {
114122 )
115123}
116124
125+ /**
126+ * Holds if `meth` need not be implemented if it always raises. `message` is the alert message, and `allowNotImplemented` is true
127+ * if we still allow the method to always raise `NotImplementedError`.
128+ */
117129predicate noNeedToAlwaysRaise ( Function meth , string message , boolean allowNotImplemented ) {
118130 meth .getName ( ) = "__hash__" and
119131 message = "use __hash__ = None instead." and
@@ -130,14 +142,17 @@ predicate noNeedToAlwaysRaise(Function meth, string message, boolean allowNotImp
130142 )
131143}
132144
145+ /** Holds if `func` has a decorator likely marking it as an abstract method. */
133146predicate isAbstract ( Function func ) { func .getADecorator ( ) .( Name ) .getId ( ) .matches ( "%abstract%" ) }
134147
148+ /** Holds if `f` always raises the exception `exec`. */
135149predicate alwaysRaises ( Function f , Expr exec ) {
136150 directlyRaises ( f , exec ) and
137151 strictcount ( Expr e | directlyRaises ( f , e ) ) = 1 and
138152 not exists ( f .getANormalExit ( ) )
139153}
140154
155+ /** Holds if `f` directly raises `expr` using a `raise` statement. */
141156predicate directlyRaises ( Function f , Expr exec ) {
142157 exists ( Raise r |
143158 r .getScope ( ) = f and
@@ -146,10 +161,12 @@ predicate directlyRaises(Function f, Expr exec) {
146161 )
147162}
148163
164+ /** Holds if `exec` is a `NotImplementedError`. */
149165predicate isNotImplementedError ( Expr exec ) {
150166 exec = API:: builtin ( "NotImplementedError" ) .getACall ( ) .asExpr ( )
151167}
152168
169+ /** Gets the name of the builtin exception type `exec` constructs, if it can be determined. */
153170string getExecName ( Expr exec ) { result = exec .( Call ) .getFunc ( ) .( Name ) .getId ( ) }
154171
155172from Function f , Expr exec , string message
0 commit comments