@@ -33,12 +33,27 @@ predicate mutates_globals(ModuleValue m) {
33
33
exists ( SubscriptNode sub | sub .getObject ( ) = globals and sub .isStore ( ) )
34
34
)
35
35
or
36
- exists ( Value enum_convert , ClassValue enum_class |
36
+ // Enum (added in 3.4) has method `_convert_` that alters globals
37
+ // This was called `_convert` until 3.8, but that name will be removed in 3.9
38
+ exists ( ClassValue enum_class |
37
39
enum_class .getASuperType ( ) = Value:: named ( "enum.Enum" ) and
38
- enum_convert = enum_class .attr ( "_convert" ) and
39
- exists ( CallNode call | call .getScope ( ) = m .getScope ( ) |
40
- enum_convert .getACall ( ) = call or
41
- call .getFunction ( ) .pointsTo ( enum_convert )
40
+ (
41
+ // In Python < 3.8, Enum._convert can be found with points-to
42
+ exists ( Value enum_convert |
43
+ enum_convert = enum_class .attr ( [ "_convert" ] ) and
44
+ exists ( CallNode call | call .getScope ( ) = m .getScope ( ) |
45
+ enum_convert .getACall ( ) = call or
46
+ call .getFunction ( ) .pointsTo ( enum_convert )
47
+ )
48
+ )
49
+ or
50
+ // In Python 3.8, Enum._convert_ is implemented using a metaclass, and our points-to
51
+ // analysis doesn't handle that good enough. So we need special case for this
52
+ not exists ( Value enum_convert | enum_convert = enum_class .attr ( "_convert" ) ) and
53
+ exists ( CallNode call | call .getScope ( ) = m .getScope ( ) |
54
+ call .getFunction ( ) .( AttrNode ) .getObject ( [ "_convert" , "_convert_" ] ) .pointsTo ( ) =
55
+ enum_class
56
+ )
42
57
)
43
58
)
44
59
}
0 commit comments