blob: 97913334dea61d66220bd960f8845f554da30143 [file] [log] [blame]
Martin v. Löwisef04c442008-03-19 05:04:44 +00001"""Fixer for it.next() -> next(it), per PEP 3114."""
2# Author: Collin Winter
3
4# Things that currently aren't covered:
5# - listcomp "next" names aren't warned
6# - "with" statement targets aren't checked
7
8# Local imports
9from ..pgen2 import token
10from ..pygram import python_symbols as syms
Benjamin Petersondf6dc8f2008-06-15 02:57:40 +000011from .. import fixer_base
12from ..fixer_util import Name, Call, find_binding, any
Martin v. Löwisef04c442008-03-19 05:04:44 +000013
14bind_warning = "Calls to builtin next() possibly shadowed by global binding"
15
16
Benjamin Petersondf6dc8f2008-06-15 02:57:40 +000017class FixNext(fixer_base.BaseFix):
Martin v. Löwisef04c442008-03-19 05:04:44 +000018 PATTERN = """
19 power< base=any+ trailer< '.' attr='next' > trailer< '(' ')' > >
20 |
21 power< head=any+ trailer< '.' attr='next' > not trailer< '(' ')' > >
22 |
23 classdef< 'class' any+ ':'
24 suite< any*
25 funcdef< 'def'
26 name='next'
27 parameters< '(' NAME ')' > any+ >
28 any* > >
29 |
30 global=global_stmt< 'global' any* 'next' any* >
31 |
32 mod=file_input< any+ >
33 """
34
35 order = "pre" # Pre-order tree traversal
36
37 def start_tree(self, tree, filename):
38 super(FixNext, self).start_tree(tree, filename)
39 self.shadowed_next = False
40
41 def transform(self, node, results):
42 assert results
43
44 base = results.get("base")
45 attr = results.get("attr")
46 name = results.get("name")
47 mod = results.get("mod")
48
49 if base:
Martin v. Löwisf733c602008-03-19 05:26:18 +000050 if self.shadowed_next:
51 attr.replace(Name("__next__", prefix=attr.get_prefix()))
52 else:
53 base = [n.clone() for n in base]
54 base[0].set_prefix("")
55 node.replace(Call(Name("next", prefix=node.get_prefix()), base))
Martin v. Löwisef04c442008-03-19 05:04:44 +000056 elif name:
57 n = Name("__next__", prefix=name.get_prefix())
58 name.replace(n)
59 elif attr:
60 # We don't do this transformation if we're assigning to "x.next".
61 # Unfortunately, it doesn't seem possible to do this in PATTERN,
62 # so it's being done here.
63 if is_assign_target(node):
64 head = results["head"]
65 if "".join([str(n) for n in head]).strip() == '__builtin__':
66 self.warning(node, bind_warning)
67 return
68 attr.replace(Name("__next__"))
69 elif "global" in results:
70 self.warning(node, bind_warning)
71 self.shadowed_next = True
72 elif mod:
73 n = find_binding('next', mod)
74 if n:
75 self.warning(n, bind_warning)
76 self.shadowed_next = True
77
78
79### The following functions help test if node is part of an assignment
80### target.
81
82def is_assign_target(node):
83 assign = find_assign(node)
84 if assign is None:
85 return False
86
87 for child in assign.children:
88 if child.type == token.EQUAL:
89 return False
90 elif is_subtree(child, node):
91 return True
92 return False
93
94def find_assign(node):
95 if node.type == syms.expr_stmt:
96 return node
97 if node.type == syms.simple_stmt or node.parent is None:
98 return None
99 return find_assign(node.parent)
100
101def is_subtree(root, node):
102 if root == node:
103 return True
104 return any([is_subtree(c, node) for c in root.children])