17db96d56Sopenharmony_ci"""Fixer for it.next() -> next(it), per PEP 3114."""
27db96d56Sopenharmony_ci# Author: Collin Winter
37db96d56Sopenharmony_ci
47db96d56Sopenharmony_ci# Things that currently aren't covered:
57db96d56Sopenharmony_ci#   - listcomp "next" names aren't warned
67db96d56Sopenharmony_ci#   - "with" statement targets aren't checked
77db96d56Sopenharmony_ci
87db96d56Sopenharmony_ci# Local imports
97db96d56Sopenharmony_cifrom ..pgen2 import token
107db96d56Sopenharmony_cifrom ..pygram import python_symbols as syms
117db96d56Sopenharmony_cifrom .. import fixer_base
127db96d56Sopenharmony_cifrom ..fixer_util import Name, Call, find_binding
137db96d56Sopenharmony_ci
147db96d56Sopenharmony_cibind_warning = "Calls to builtin next() possibly shadowed by global binding"
157db96d56Sopenharmony_ci
167db96d56Sopenharmony_ci
177db96d56Sopenharmony_ciclass FixNext(fixer_base.BaseFix):
187db96d56Sopenharmony_ci    BM_compatible = True
197db96d56Sopenharmony_ci    PATTERN = """
207db96d56Sopenharmony_ci    power< base=any+ trailer< '.' attr='next' > trailer< '(' ')' > >
217db96d56Sopenharmony_ci    |
227db96d56Sopenharmony_ci    power< head=any+ trailer< '.' attr='next' > not trailer< '(' ')' > >
237db96d56Sopenharmony_ci    |
247db96d56Sopenharmony_ci    classdef< 'class' any+ ':'
257db96d56Sopenharmony_ci              suite< any*
267db96d56Sopenharmony_ci                     funcdef< 'def'
277db96d56Sopenharmony_ci                              name='next'
287db96d56Sopenharmony_ci                              parameters< '(' NAME ')' > any+ >
297db96d56Sopenharmony_ci                     any* > >
307db96d56Sopenharmony_ci    |
317db96d56Sopenharmony_ci    global=global_stmt< 'global' any* 'next' any* >
327db96d56Sopenharmony_ci    """
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_ci    order = "pre" # Pre-order tree traversal
357db96d56Sopenharmony_ci
367db96d56Sopenharmony_ci    def start_tree(self, tree, filename):
377db96d56Sopenharmony_ci        super(FixNext, self).start_tree(tree, filename)
387db96d56Sopenharmony_ci
397db96d56Sopenharmony_ci        n = find_binding('next', tree)
407db96d56Sopenharmony_ci        if n:
417db96d56Sopenharmony_ci            self.warning(n, bind_warning)
427db96d56Sopenharmony_ci            self.shadowed_next = True
437db96d56Sopenharmony_ci        else:
447db96d56Sopenharmony_ci            self.shadowed_next = False
457db96d56Sopenharmony_ci
467db96d56Sopenharmony_ci    def transform(self, node, results):
477db96d56Sopenharmony_ci        assert results
487db96d56Sopenharmony_ci
497db96d56Sopenharmony_ci        base = results.get("base")
507db96d56Sopenharmony_ci        attr = results.get("attr")
517db96d56Sopenharmony_ci        name = results.get("name")
527db96d56Sopenharmony_ci
537db96d56Sopenharmony_ci        if base:
547db96d56Sopenharmony_ci            if self.shadowed_next:
557db96d56Sopenharmony_ci                attr.replace(Name("__next__", prefix=attr.prefix))
567db96d56Sopenharmony_ci            else:
577db96d56Sopenharmony_ci                base = [n.clone() for n in base]
587db96d56Sopenharmony_ci                base[0].prefix = ""
597db96d56Sopenharmony_ci                node.replace(Call(Name("next", prefix=node.prefix), base))
607db96d56Sopenharmony_ci        elif name:
617db96d56Sopenharmony_ci            n = Name("__next__", prefix=name.prefix)
627db96d56Sopenharmony_ci            name.replace(n)
637db96d56Sopenharmony_ci        elif attr:
647db96d56Sopenharmony_ci            # We don't do this transformation if we're assigning to "x.next".
657db96d56Sopenharmony_ci            # Unfortunately, it doesn't seem possible to do this in PATTERN,
667db96d56Sopenharmony_ci            #  so it's being done here.
677db96d56Sopenharmony_ci            if is_assign_target(node):
687db96d56Sopenharmony_ci                head = results["head"]
697db96d56Sopenharmony_ci                if "".join([str(n) for n in head]).strip() == '__builtin__':
707db96d56Sopenharmony_ci                    self.warning(node, bind_warning)
717db96d56Sopenharmony_ci                return
727db96d56Sopenharmony_ci            attr.replace(Name("__next__"))
737db96d56Sopenharmony_ci        elif "global" in results:
747db96d56Sopenharmony_ci            self.warning(node, bind_warning)
757db96d56Sopenharmony_ci            self.shadowed_next = True
767db96d56Sopenharmony_ci
777db96d56Sopenharmony_ci
787db96d56Sopenharmony_ci### The following functions help test if node is part of an assignment
797db96d56Sopenharmony_ci###  target.
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_cidef is_assign_target(node):
827db96d56Sopenharmony_ci    assign = find_assign(node)
837db96d56Sopenharmony_ci    if assign is None:
847db96d56Sopenharmony_ci        return False
857db96d56Sopenharmony_ci
867db96d56Sopenharmony_ci    for child in assign.children:
877db96d56Sopenharmony_ci        if child.type == token.EQUAL:
887db96d56Sopenharmony_ci            return False
897db96d56Sopenharmony_ci        elif is_subtree(child, node):
907db96d56Sopenharmony_ci            return True
917db96d56Sopenharmony_ci    return False
927db96d56Sopenharmony_ci
937db96d56Sopenharmony_cidef find_assign(node):
947db96d56Sopenharmony_ci    if node.type == syms.expr_stmt:
957db96d56Sopenharmony_ci        return node
967db96d56Sopenharmony_ci    if node.type == syms.simple_stmt or node.parent is None:
977db96d56Sopenharmony_ci        return None
987db96d56Sopenharmony_ci    return find_assign(node.parent)
997db96d56Sopenharmony_ci
1007db96d56Sopenharmony_cidef is_subtree(root, node):
1017db96d56Sopenharmony_ci    if root == node:
1027db96d56Sopenharmony_ci        return True
1037db96d56Sopenharmony_ci    return any(is_subtree(c, node) for c in root.children)
104