22
33import python
44import semmle.python.ApiGraphs
5+ import semmle.python.dataflow.new.internal.DataFlowDispatch
56import DataFlow
67
78/** Holds if `f` is a method of the class `c`. */
8- private predicate methodOfClass ( Function f , Class c ) { f .getScope ( ) = c }
9+ private predicate methodOfClass ( Function f , Class c ) {
10+ exists ( FunctionDef d | d .getDefinedFunction ( ) = f and d .getScope ( ) = c )
11+ }
912
1013/** Holds if `c` is a metaclass. */
1114private predicate isMetaclass ( Class c ) {
1215 c = API:: builtin ( "type" ) .getASubclass * ( ) .asSource ( ) .asExpr ( ) .( ClassExpr ) .getInnerScope ( )
1316}
1417
15- /** Holds if `f` is a class method. */
16- private predicate isClassMethod ( Function f ) {
17- f .getADecorator ( ) = API:: builtin ( "classmethod" ) .asSource ( ) .asExpr ( )
18- or
19- f .getName ( ) in [ "__new__" , "__init_subclass__" , "__metaclass__" , "__class_getitem__" ]
20- }
21-
22- /** Holds if `f` is a static method. */
23- private predicate isStaticMethod ( Function f ) {
24- f .getADecorator ( ) = API:: builtin ( "staticmethod" ) .asSource ( ) .asExpr ( )
25- }
26-
2718/** Holds if `c` is a Zope interface. */
2819private predicate isZopeInterface ( Class c ) {
2920 c =
@@ -55,8 +46,8 @@ private predicate usedInInit(Function f, Class c) {
5546/** Holds if the first parameter of `f` should be named `self`. */
5647predicate shouldBeSelf ( Function f , Class c ) {
5748 methodOfClass ( f , c ) and
58- not isStaticMethod ( f ) and
59- not isClassMethod ( f ) and
49+ not isStaticmethod ( f ) and
50+ not isClassmethod ( f ) and
6051 not isMetaclass ( c ) and
6152 not isZopeInterface ( c ) and
6253 not usedInInit ( f , c )
@@ -65,24 +56,29 @@ predicate shouldBeSelf(Function f, Class c) {
6556/** Holds if the first parameter of `f` should be named `cls`. */
6657predicate shouldBeCls ( Function f , Class c ) {
6758 methodOfClass ( f , c ) and
68- not isStaticMethod ( f ) and
59+ not isStaticmethod ( f ) and
6960 (
70- isClassMethod ( f ) and not isMetaclass ( c )
61+ isClassmethod ( f ) and not isMetaclass ( c )
7162 or
72- isMetaclass ( c ) and not isClassMethod ( f )
63+ isMetaclass ( c ) and not isClassmethod ( f )
7364 )
7465}
7566
7667/** Holds if the first parameter of `f` is named `self`. */
7768predicate firstArgNamedSelf ( Function f ) { f .getArgName ( 0 ) = "self" }
7869
79- /** Holds if the first parameter of `f` is named `cls`. */
80- predicate firstArgNamedCls ( Function f ) {
70+ /** Holds if the first parameter of `f` refers to the class - it is either named `cls`, or it is named `self` and is a method of a metaclass. */
71+ predicate firstArgRefersToCls ( Function f , Class c ) {
72+ methodOfClass ( f , c ) and
8173 exists ( string argname | argname = f .getArgName ( 0 ) |
8274 argname = "cls"
8375 or
8476 /* Not PEP8, but relatively common */
8577 argname = "mcls"
78+ or
79+ /* If c is a metaclass, allow arguments named `self`. */
80+ argname = "self" and
81+ isMetaclass ( c )
8682 )
8783}
8884
@@ -92,17 +88,11 @@ predicate firstArgShouldBeNamedSelfAndIsnt(Function f) {
9288 not firstArgNamedSelf ( f )
9389}
9490
95- /** Holds if `f` is a regular method of a metaclass, and its first argument is named `self`. */
96- private predicate metaclassNamedSelf ( Function f , Class c ) {
97- methodOfClass ( f , c ) and
98- firstArgNamedSelf ( f ) and
99- isMetaclass ( c ) and
100- not isClassMethod ( f )
101- }
102-
10391/** Holds if the first parameter of `f` should be named `cls`, but isn't. */
104- predicate firstArgShouldBeNamedClsAndIsnt ( Function f ) {
105- shouldBeCls ( f , _) and
106- not firstArgNamedCls ( f ) and
107- not metaclassNamedSelf ( f , _)
92+ predicate firstArgShouldReferToClsAndDoesnt ( Function f ) {
93+ exists ( Class c |
94+ methodOfClass ( f , c ) and
95+ shouldBeCls ( f , c ) and
96+ not firstArgRefersToCls ( f , c )
97+ )
10898}
0 commit comments