@@ -374,7 +374,7 @@ class CallableValue extends Value {
374374
375375/** Class representing classes in the Python program, both Python and built-in.
376376 */
377- class ClassValue extends Value {
377+ class ClassValue extends Value {
378378
379379 ClassValue ( ) {
380380 this .( ObjectInternal ) .isClass ( ) = true
@@ -413,6 +413,26 @@ class ClassValue extends Value {
413413 this .hasAttribute ( "__getitem__" )
414414 }
415415
416+ /** Holds if this class is an iterator. */
417+ predicate isIterator ( ) {
418+ this .hasAttribute ( "__iter__" ) and
419+ ( major_version ( ) = 3 and this .hasAttribute ( "__next__" )
420+ or
421+ /* Because 'next' is a common method name we need to check that an __iter__
422+ * method actually returns this class. This is not needed for Py3 as the
423+ * '__next__' method exists to define a class as an iterator.
424+ */
425+ major_version ( ) = 2 and this .hasAttribute ( "next" ) and
426+ exists ( ClassObject other , FunctionObject iter |
427+ other .declaredAttribute ( "__iter__" ) = iter |
428+ iter .getAnInferredReturnType ( ) = this
429+ )
430+ )
431+ or
432+ /* This will be redundant when we have C class information */
433+ this = ClassValue:: generator ( )
434+ }
435+
416436 /** Holds if this class is a descriptor. */
417437 predicate isDescriptorType ( ) {
418438 this .hasAttribute ( "__get__" )
0 commit comments