@@ -20,12 +20,16 @@ private predicate methodOfClass(Function f, Class c) {
20
20
exists ( FunctionDef d | d .getDefinedFunction ( ) = f and d .getScope ( ) = c )
21
21
}
22
22
23
+ /** Gets the __iter__ method of `c`. */
23
24
Function iterMethod ( Class c ) { methodOfClass ( result , c ) and result .getName ( ) = "__iter__" }
24
25
26
+ /** Gets the `__next__` method of `c`. */
25
27
Function nextMethod ( Class c ) { methodOfClass ( result , c ) and result .getName ( ) = "__next__" }
26
28
29
+ /** Holds if `var` is a variable referring to the `self` parameter of `f`. */
27
30
predicate isSelfVar ( Function f , Name var ) { var .getVariable ( ) = f .getArg ( 0 ) .( Name ) .getVariable ( ) }
28
31
32
+ /** Holds if `e` is an expression that an iter function `f` should return. */
29
33
predicate isGoodReturn ( Function f , Expr e ) {
30
34
isSelfVar ( f , e )
31
35
or
@@ -40,6 +44,7 @@ predicate isGoodReturn(Function f, Expr e) {
40
44
)
41
45
}
42
46
47
+ /** Holds if the iter method `f` does not return `self` or an equivalent. */
43
48
predicate returnsNonSelf ( Function f ) {
44
49
exists ( f .getFallthroughNode ( ) )
45
50
or
@@ -48,10 +53,46 @@ predicate returnsNonSelf(Function f) {
48
53
exists ( Return r | r .getScope ( ) = f and not exists ( r .getValue ( ) ) )
49
54
}
50
55
51
- from Class c , Function iter
56
+ /** Holds if `iter` and `next` methods are wrappers around some field. */
57
+ predicate iterWrapperMethods ( Function iter , Function next ) {
58
+ exists ( string field |
59
+ exists ( Return r , DataFlow:: Node self , DataFlow:: AttrRead read |
60
+ r .getScope ( ) = iter and
61
+ r .getValue ( ) = iterCall ( read ) .asExpr ( ) and
62
+ read .accesses ( self , field ) and
63
+ isSelfVar ( iter , self .asExpr ( ) )
64
+ ) and
65
+ exists ( Return r , DataFlow:: Node self , DataFlow:: AttrRead read |
66
+ r .getScope ( ) = next and
67
+ r .getValue ( ) = nextCall ( read ) .asExpr ( ) and
68
+ read .accesses ( self , field ) and
69
+ isSelfVar ( next , self .asExpr ( ) )
70
+ )
71
+ )
72
+ }
73
+
74
+ DataFlow:: CallCfgNode iterCall ( DataFlow:: Node arg ) {
75
+ result .( DataFlow:: MethodCallNode ) .calls ( arg , "__iter__" )
76
+ or
77
+ result = API:: builtin ( "iter" ) .getACall ( ) and
78
+ arg = result .getArg ( 0 ) and
79
+ not exists ( result .getArg ( 1 ) )
80
+ or
81
+ result = arg // assume the wrapping field is already an iterator
82
+ }
83
+
84
+ DataFlow:: CallCfgNode nextCall ( DataFlow:: Node arg ) {
85
+ result .( DataFlow:: MethodCallNode ) .calls ( arg , "__next__" )
86
+ or
87
+ result = API:: builtin ( "next" ) .getACall ( ) and
88
+ arg = result .getArg ( 0 )
89
+ }
90
+
91
+ from Class c , Function iter , Function next
52
92
where
53
- exists ( nextMethod ( c ) ) and
93
+ next = nextMethod ( c ) and
54
94
iter = iterMethod ( c ) and
55
- returnsNonSelf ( iter )
95
+ returnsNonSelf ( iter ) and
96
+ not iterWrapperMethods ( iter , next )
56
97
select iter , "Iter method of iterator $@ does not return `" + iter .getArg ( 0 ) .getName ( ) + "`." , c ,
57
98
c .getName ( )
0 commit comments