@@ -734,9 +734,53 @@ def on_conditional_branch():
734
734
self .report (messages .UndefinedName , node , name )
735
735
736
736
def handleChildren (self , tree , omit = None ):
737
+ """Handle all children recursively, but may be flattened."""
737
738
for node in iter_child_nodes (tree , omit = omit ):
738
739
self .handleNode (node , tree )
739
740
741
+ def handleChildrenNested (self , node ):
742
+ """Handle all children recursively."""
743
+ self .handleChildren (node )
744
+
745
+ def _iter_flattened (self , node , omit , _fields_order = _FieldsOrder ()):
746
+ """
747
+ Yield child nodes of *node* and their children, with a recurse flag.
748
+
749
+ The value yielded is a tuple of the node, its parent and a boolean flag
750
+ to indicate whether the node needs to be recursed by the caller because
751
+ all child nodes can not be processed as a flat list.
752
+
753
+ e.g. flag is True for lambda and comprehension nodes,
754
+ as their children must be handled by the appropriate node handler.
755
+ """
756
+ nodes = [(node , None )]
757
+ while nodes :
758
+ new_nodes = []
759
+ for node , parent in nodes :
760
+ yield_children = True
761
+ if parent :
762
+ handler = self .getNodeHandler (node .__class__ )
763
+ if handler in (self .handleChildren ,
764
+ self .handleChildrenFlattened ,
765
+ self .handleNode ):
766
+ handler = False
767
+ else :
768
+ yield_children = False
769
+ yield node , parent , handler
770
+
771
+ if yield_children :
772
+ new_nodes += list (
773
+ (child , node )
774
+ for child in iter_child_nodes (node ,
775
+ omit ,
776
+ _fields_order ))
777
+
778
+ nodes [:] = new_nodes
779
+
780
+ def handleChildrenFlattened (self , tree , omit = None ):
781
+ for node , parent , handler in self ._iter_flattened (tree , omit = omit ):
782
+ self .handleNode (node , parent , handler )
783
+
740
784
def isLiteralTupleUnpacking (self , node ):
741
785
if isinstance (node , ast .Assign ):
742
786
for child in node .targets + [node .value ]:
@@ -766,7 +810,7 @@ def getDocstring(self, node):
766
810
767
811
return (node .s , doctest_lineno )
768
812
769
- def handleNode (self , node , parent ):
813
+ def handleNode (self , node , parent , handler = None ):
770
814
if node is None :
771
815
return
772
816
if self .offset and getattr (node , 'lineno' , None ) is not None :
@@ -777,11 +821,18 @@ def handleNode(self, node, parent):
777
821
if self .futuresAllowed and not (isinstance (node , ast .ImportFrom ) or
778
822
self .isDocstring (node )):
779
823
self .futuresAllowed = False
780
- self . nodeDepth += 1
781
- node .depth = self .nodeDepth
824
+
825
+ node .depth = self .nodeDepth + 1
782
826
node .parent = parent
783
- try :
827
+
828
+ if handler is False :
829
+ return
830
+
831
+ if not handler :
784
832
handler = self .getNodeHandler (node .__class__ )
833
+
834
+ self .nodeDepth += 1
835
+ try :
785
836
handler (node )
786
837
finally :
787
838
self .nodeDepth -= 1
@@ -833,21 +884,30 @@ def ignore(self, node):
833
884
pass
834
885
835
886
# "stmt" type nodes
836
- DELETE = PRINT = FOR = ASYNCFOR = WHILE = IF = WITH = WITHITEM = \
837
- ASYNCWITH = ASYNCWITHITEM = RAISE = TRYFINALLY = EXEC = \
838
- EXPR = ASSIGN = handleChildren
887
+
888
+ # many of these handleChildrenFlattened are wrong, and need tests
889
+ # Anywhere a function/class can be defined probably caueses a failure,
890
+ # due to the delayed processing.
891
+ DELETE = PRINT = handleChildrenFlattened
892
+ FOR = ASYNCFOR = handleChildren
893
+ WHILE = IF = WITH = ASYNCWITH = handleChildren
894
+ WITHITEM = ASYNCWITHITEM = handleChildrenFlattened
895
+ RAISE = handleChildrenFlattened
896
+ TRYFINALLY = handleChildren
897
+ EXEC = EXPR = handleChildrenFlattened
898
+ ASSIGN = handleChildren
839
899
840
900
PASS = ignore
841
901
842
902
# "expr" type nodes
843
903
BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = \
844
904
COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = \
845
- STARRED = NAMECONSTANT = handleChildren
905
+ STARRED = NAMECONSTANT = handleChildrenFlattened
846
906
847
907
NUM = STR = BYTES = ELLIPSIS = ignore
848
908
849
909
# "slice" type nodes
850
- SLICE = EXTSLICE = INDEX = handleChildren
910
+ SLICE = EXTSLICE = INDEX = handleChildrenFlattened
851
911
852
912
# expression contexts are node instances too, though being constants
853
913
LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
@@ -859,7 +919,8 @@ def ignore(self, node):
859
919
MATMULT = ignore
860
920
861
921
# additional node types
862
- COMPREHENSION = KEYWORD = FORMATTEDVALUE = handleChildren
922
+ COMPREHENSION = handleChildren
923
+ KEYWORD = FORMATTEDVALUE = handleChildrenFlattened
863
924
864
925
def ASSERT (self , node ):
865
926
if isinstance (node .test , ast .Tuple ) and node .test .elts != []:
@@ -903,7 +964,7 @@ def GENERATOREXP(self, node):
903
964
self .handleChildren (node )
904
965
self .popScope ()
905
966
906
- LISTCOMP = handleChildren if PY2 else GENERATOREXP
967
+ LISTCOMP = handleChildrenNested if PY2 else GENERATOREXP
907
968
908
969
DICTCOMP = SETCOMP = GENERATOREXP
909
970
0 commit comments