@@ -33,12 +33,27 @@ predicate mutates_globals(ModuleValue m) {
3333 exists ( SubscriptNode sub | sub .getObject ( ) = globals and sub .isStore ( ) )
3434 )
3535 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 |
3739 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+ )
4257 )
4358 )
4459}
0 commit comments