@@ -20,12 +20,16 @@ private predicate methodOfClass(Function f, Class c) {
2020 exists ( FunctionDef d | d .getDefinedFunction ( ) = f and d .getScope ( ) = c )
2121}
2222
23+ /** Gets the __iter__ method of `c`. */
2324Function iterMethod ( Class c ) { methodOfClass ( result , c ) and result .getName ( ) = "__iter__" }
2425
26+ /** Gets the `__next__` method of `c`. */
2527Function nextMethod ( Class c ) { methodOfClass ( result , c ) and result .getName ( ) = "__next__" }
2628
29+ /** Holds if `var` is a variable referring to the `self` parameter of `f`. */
2730predicate isSelfVar ( Function f , Name var ) { var .getVariable ( ) = f .getArg ( 0 ) .( Name ) .getVariable ( ) }
2831
32+ /** Holds if `e` is an expression that an iter function `f` should return. */
2933predicate isGoodReturn ( Function f , Expr e ) {
3034 isSelfVar ( f , e )
3135 or
@@ -40,6 +44,7 @@ predicate isGoodReturn(Function f, Expr e) {
4044 )
4145}
4246
47+ /** Holds if the iter method `f` does not return `self` or an equivalent. */
4348predicate returnsNonSelf ( Function f ) {
4449 exists ( f .getFallthroughNode ( ) )
4550 or
@@ -48,10 +53,46 @@ predicate returnsNonSelf(Function f) {
4853 exists ( Return r | r .getScope ( ) = f and not exists ( r .getValue ( ) ) )
4954}
5055
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
5292where
53- exists ( nextMethod ( c ) ) and
93+ next = nextMethod ( c ) and
5494 iter = iterMethod ( c ) and
55- returnsNonSelf ( iter )
95+ returnsNonSelf ( iter ) and
96+ not iterWrapperMethods ( iter , next )
5697select iter , "Iter method of iterator $@ does not return `" + iter .getArg ( 0 ) .getName ( ) + "`." , c ,
5798 c .getName ( )
0 commit comments