17db96d56Sopenharmony_ci"""Utility functions, node construction macros, etc."""
27db96d56Sopenharmony_ci# Author: Collin Winter
37db96d56Sopenharmony_ci
47db96d56Sopenharmony_ci# Local imports
57db96d56Sopenharmony_cifrom .pgen2 import token
67db96d56Sopenharmony_cifrom .pytree import Leaf, Node
77db96d56Sopenharmony_cifrom .pygram import python_symbols as syms
87db96d56Sopenharmony_cifrom . import patcomp
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_ci###########################################################
127db96d56Sopenharmony_ci### Common node-construction "macros"
137db96d56Sopenharmony_ci###########################################################
147db96d56Sopenharmony_ci
157db96d56Sopenharmony_cidef KeywordArg(keyword, value):
167db96d56Sopenharmony_ci    return Node(syms.argument,
177db96d56Sopenharmony_ci                [keyword, Leaf(token.EQUAL, "="), value])
187db96d56Sopenharmony_ci
197db96d56Sopenharmony_cidef LParen():
207db96d56Sopenharmony_ci    return Leaf(token.LPAR, "(")
217db96d56Sopenharmony_ci
227db96d56Sopenharmony_cidef RParen():
237db96d56Sopenharmony_ci    return Leaf(token.RPAR, ")")
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_cidef Assign(target, source):
267db96d56Sopenharmony_ci    """Build an assignment statement"""
277db96d56Sopenharmony_ci    if not isinstance(target, list):
287db96d56Sopenharmony_ci        target = [target]
297db96d56Sopenharmony_ci    if not isinstance(source, list):
307db96d56Sopenharmony_ci        source.prefix = " "
317db96d56Sopenharmony_ci        source = [source]
327db96d56Sopenharmony_ci
337db96d56Sopenharmony_ci    return Node(syms.atom,
347db96d56Sopenharmony_ci                target + [Leaf(token.EQUAL, "=", prefix=" ")] + source)
357db96d56Sopenharmony_ci
367db96d56Sopenharmony_cidef Name(name, prefix=None):
377db96d56Sopenharmony_ci    """Return a NAME leaf"""
387db96d56Sopenharmony_ci    return Leaf(token.NAME, name, prefix=prefix)
397db96d56Sopenharmony_ci
407db96d56Sopenharmony_cidef Attr(obj, attr):
417db96d56Sopenharmony_ci    """A node tuple for obj.attr"""
427db96d56Sopenharmony_ci    return [obj, Node(syms.trailer, [Dot(), attr])]
437db96d56Sopenharmony_ci
447db96d56Sopenharmony_cidef Comma():
457db96d56Sopenharmony_ci    """A comma leaf"""
467db96d56Sopenharmony_ci    return Leaf(token.COMMA, ",")
477db96d56Sopenharmony_ci
487db96d56Sopenharmony_cidef Dot():
497db96d56Sopenharmony_ci    """A period (.) leaf"""
507db96d56Sopenharmony_ci    return Leaf(token.DOT, ".")
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_cidef ArgList(args, lparen=LParen(), rparen=RParen()):
537db96d56Sopenharmony_ci    """A parenthesised argument list, used by Call()"""
547db96d56Sopenharmony_ci    node = Node(syms.trailer, [lparen.clone(), rparen.clone()])
557db96d56Sopenharmony_ci    if args:
567db96d56Sopenharmony_ci        node.insert_child(1, Node(syms.arglist, args))
577db96d56Sopenharmony_ci    return node
587db96d56Sopenharmony_ci
597db96d56Sopenharmony_cidef Call(func_name, args=None, prefix=None):
607db96d56Sopenharmony_ci    """A function call"""
617db96d56Sopenharmony_ci    node = Node(syms.power, [func_name, ArgList(args)])
627db96d56Sopenharmony_ci    if prefix is not None:
637db96d56Sopenharmony_ci        node.prefix = prefix
647db96d56Sopenharmony_ci    return node
657db96d56Sopenharmony_ci
667db96d56Sopenharmony_cidef Newline():
677db96d56Sopenharmony_ci    """A newline literal"""
687db96d56Sopenharmony_ci    return Leaf(token.NEWLINE, "\n")
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_cidef BlankLine():
717db96d56Sopenharmony_ci    """A blank line"""
727db96d56Sopenharmony_ci    return Leaf(token.NEWLINE, "")
737db96d56Sopenharmony_ci
747db96d56Sopenharmony_cidef Number(n, prefix=None):
757db96d56Sopenharmony_ci    return Leaf(token.NUMBER, n, prefix=prefix)
767db96d56Sopenharmony_ci
777db96d56Sopenharmony_cidef Subscript(index_node):
787db96d56Sopenharmony_ci    """A numeric or string subscript"""
797db96d56Sopenharmony_ci    return Node(syms.trailer, [Leaf(token.LBRACE, "["),
807db96d56Sopenharmony_ci                               index_node,
817db96d56Sopenharmony_ci                               Leaf(token.RBRACE, "]")])
827db96d56Sopenharmony_ci
837db96d56Sopenharmony_cidef String(string, prefix=None):
847db96d56Sopenharmony_ci    """A string leaf"""
857db96d56Sopenharmony_ci    return Leaf(token.STRING, string, prefix=prefix)
867db96d56Sopenharmony_ci
877db96d56Sopenharmony_cidef ListComp(xp, fp, it, test=None):
887db96d56Sopenharmony_ci    """A list comprehension of the form [xp for fp in it if test].
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ci    If test is None, the "if test" part is omitted.
917db96d56Sopenharmony_ci    """
927db96d56Sopenharmony_ci    xp.prefix = ""
937db96d56Sopenharmony_ci    fp.prefix = " "
947db96d56Sopenharmony_ci    it.prefix = " "
957db96d56Sopenharmony_ci    for_leaf = Leaf(token.NAME, "for")
967db96d56Sopenharmony_ci    for_leaf.prefix = " "
977db96d56Sopenharmony_ci    in_leaf = Leaf(token.NAME, "in")
987db96d56Sopenharmony_ci    in_leaf.prefix = " "
997db96d56Sopenharmony_ci    inner_args = [for_leaf, fp, in_leaf, it]
1007db96d56Sopenharmony_ci    if test:
1017db96d56Sopenharmony_ci        test.prefix = " "
1027db96d56Sopenharmony_ci        if_leaf = Leaf(token.NAME, "if")
1037db96d56Sopenharmony_ci        if_leaf.prefix = " "
1047db96d56Sopenharmony_ci        inner_args.append(Node(syms.comp_if, [if_leaf, test]))
1057db96d56Sopenharmony_ci    inner = Node(syms.listmaker, [xp, Node(syms.comp_for, inner_args)])
1067db96d56Sopenharmony_ci    return Node(syms.atom,
1077db96d56Sopenharmony_ci                       [Leaf(token.LBRACE, "["),
1087db96d56Sopenharmony_ci                        inner,
1097db96d56Sopenharmony_ci                        Leaf(token.RBRACE, "]")])
1107db96d56Sopenharmony_ci
1117db96d56Sopenharmony_cidef FromImport(package_name, name_leafs):
1127db96d56Sopenharmony_ci    """ Return an import statement in the form:
1137db96d56Sopenharmony_ci        from package import name_leafs"""
1147db96d56Sopenharmony_ci    # XXX: May not handle dotted imports properly (eg, package_name='foo.bar')
1157db96d56Sopenharmony_ci    #assert package_name == '.' or '.' not in package_name, "FromImport has "\
1167db96d56Sopenharmony_ci    #       "not been tested with dotted package names -- use at your own "\
1177db96d56Sopenharmony_ci    #       "peril!"
1187db96d56Sopenharmony_ci
1197db96d56Sopenharmony_ci    for leaf in name_leafs:
1207db96d56Sopenharmony_ci        # Pull the leaves out of their old tree
1217db96d56Sopenharmony_ci        leaf.remove()
1227db96d56Sopenharmony_ci
1237db96d56Sopenharmony_ci    children = [Leaf(token.NAME, "from"),
1247db96d56Sopenharmony_ci                Leaf(token.NAME, package_name, prefix=" "),
1257db96d56Sopenharmony_ci                Leaf(token.NAME, "import", prefix=" "),
1267db96d56Sopenharmony_ci                Node(syms.import_as_names, name_leafs)]
1277db96d56Sopenharmony_ci    imp = Node(syms.import_from, children)
1287db96d56Sopenharmony_ci    return imp
1297db96d56Sopenharmony_ci
1307db96d56Sopenharmony_cidef ImportAndCall(node, results, names):
1317db96d56Sopenharmony_ci    """Returns an import statement and calls a method
1327db96d56Sopenharmony_ci    of the module:
1337db96d56Sopenharmony_ci
1347db96d56Sopenharmony_ci    import module
1357db96d56Sopenharmony_ci    module.name()"""
1367db96d56Sopenharmony_ci    obj = results["obj"].clone()
1377db96d56Sopenharmony_ci    if obj.type == syms.arglist:
1387db96d56Sopenharmony_ci        newarglist = obj.clone()
1397db96d56Sopenharmony_ci    else:
1407db96d56Sopenharmony_ci        newarglist = Node(syms.arglist, [obj.clone()])
1417db96d56Sopenharmony_ci    after = results["after"]
1427db96d56Sopenharmony_ci    if after:
1437db96d56Sopenharmony_ci        after = [n.clone() for n in after]
1447db96d56Sopenharmony_ci    new = Node(syms.power,
1457db96d56Sopenharmony_ci               Attr(Name(names[0]), Name(names[1])) +
1467db96d56Sopenharmony_ci               [Node(syms.trailer,
1477db96d56Sopenharmony_ci                     [results["lpar"].clone(),
1487db96d56Sopenharmony_ci                      newarglist,
1497db96d56Sopenharmony_ci                      results["rpar"].clone()])] + after)
1507db96d56Sopenharmony_ci    new.prefix = node.prefix
1517db96d56Sopenharmony_ci    return new
1527db96d56Sopenharmony_ci
1537db96d56Sopenharmony_ci
1547db96d56Sopenharmony_ci###########################################################
1557db96d56Sopenharmony_ci### Determine whether a node represents a given literal
1567db96d56Sopenharmony_ci###########################################################
1577db96d56Sopenharmony_ci
1587db96d56Sopenharmony_cidef is_tuple(node):
1597db96d56Sopenharmony_ci    """Does the node represent a tuple literal?"""
1607db96d56Sopenharmony_ci    if isinstance(node, Node) and node.children == [LParen(), RParen()]:
1617db96d56Sopenharmony_ci        return True
1627db96d56Sopenharmony_ci    return (isinstance(node, Node)
1637db96d56Sopenharmony_ci            and len(node.children) == 3
1647db96d56Sopenharmony_ci            and isinstance(node.children[0], Leaf)
1657db96d56Sopenharmony_ci            and isinstance(node.children[1], Node)
1667db96d56Sopenharmony_ci            and isinstance(node.children[2], Leaf)
1677db96d56Sopenharmony_ci            and node.children[0].value == "("
1687db96d56Sopenharmony_ci            and node.children[2].value == ")")
1697db96d56Sopenharmony_ci
1707db96d56Sopenharmony_cidef is_list(node):
1717db96d56Sopenharmony_ci    """Does the node represent a list literal?"""
1727db96d56Sopenharmony_ci    return (isinstance(node, Node)
1737db96d56Sopenharmony_ci            and len(node.children) > 1
1747db96d56Sopenharmony_ci            and isinstance(node.children[0], Leaf)
1757db96d56Sopenharmony_ci            and isinstance(node.children[-1], Leaf)
1767db96d56Sopenharmony_ci            and node.children[0].value == "["
1777db96d56Sopenharmony_ci            and node.children[-1].value == "]")
1787db96d56Sopenharmony_ci
1797db96d56Sopenharmony_ci
1807db96d56Sopenharmony_ci###########################################################
1817db96d56Sopenharmony_ci### Misc
1827db96d56Sopenharmony_ci###########################################################
1837db96d56Sopenharmony_ci
1847db96d56Sopenharmony_cidef parenthesize(node):
1857db96d56Sopenharmony_ci    return Node(syms.atom, [LParen(), node, RParen()])
1867db96d56Sopenharmony_ci
1877db96d56Sopenharmony_ci
1887db96d56Sopenharmony_ciconsuming_calls = {"sorted", "list", "set", "any", "all", "tuple", "sum",
1897db96d56Sopenharmony_ci                   "min", "max", "enumerate"}
1907db96d56Sopenharmony_ci
1917db96d56Sopenharmony_cidef attr_chain(obj, attr):
1927db96d56Sopenharmony_ci    """Follow an attribute chain.
1937db96d56Sopenharmony_ci
1947db96d56Sopenharmony_ci    If you have a chain of objects where a.foo -> b, b.foo-> c, etc,
1957db96d56Sopenharmony_ci    use this to iterate over all objects in the chain. Iteration is
1967db96d56Sopenharmony_ci    terminated by getattr(x, attr) is None.
1977db96d56Sopenharmony_ci
1987db96d56Sopenharmony_ci    Args:
1997db96d56Sopenharmony_ci        obj: the starting object
2007db96d56Sopenharmony_ci        attr: the name of the chaining attribute
2017db96d56Sopenharmony_ci
2027db96d56Sopenharmony_ci    Yields:
2037db96d56Sopenharmony_ci        Each successive object in the chain.
2047db96d56Sopenharmony_ci    """
2057db96d56Sopenharmony_ci    next = getattr(obj, attr)
2067db96d56Sopenharmony_ci    while next:
2077db96d56Sopenharmony_ci        yield next
2087db96d56Sopenharmony_ci        next = getattr(next, attr)
2097db96d56Sopenharmony_ci
2107db96d56Sopenharmony_cip0 = """for_stmt< 'for' any 'in' node=any ':' any* >
2117db96d56Sopenharmony_ci        | comp_for< 'for' any 'in' node=any any* >
2127db96d56Sopenharmony_ci     """
2137db96d56Sopenharmony_cip1 = """
2147db96d56Sopenharmony_cipower<
2157db96d56Sopenharmony_ci    ( 'iter' | 'list' | 'tuple' | 'sorted' | 'set' | 'sum' |
2167db96d56Sopenharmony_ci      'any' | 'all' | 'enumerate' | (any* trailer< '.' 'join' >) )
2177db96d56Sopenharmony_ci    trailer< '(' node=any ')' >
2187db96d56Sopenharmony_ci    any*
2197db96d56Sopenharmony_ci>
2207db96d56Sopenharmony_ci"""
2217db96d56Sopenharmony_cip2 = """
2227db96d56Sopenharmony_cipower<
2237db96d56Sopenharmony_ci    ( 'sorted' | 'enumerate' )
2247db96d56Sopenharmony_ci    trailer< '(' arglist<node=any any*> ')' >
2257db96d56Sopenharmony_ci    any*
2267db96d56Sopenharmony_ci>
2277db96d56Sopenharmony_ci"""
2287db96d56Sopenharmony_cipats_built = False
2297db96d56Sopenharmony_cidef in_special_context(node):
2307db96d56Sopenharmony_ci    """ Returns true if node is in an environment where all that is required
2317db96d56Sopenharmony_ci        of it is being iterable (ie, it doesn't matter if it returns a list
2327db96d56Sopenharmony_ci        or an iterator).
2337db96d56Sopenharmony_ci        See test_map_nochange in test_fixers.py for some examples and tests.
2347db96d56Sopenharmony_ci        """
2357db96d56Sopenharmony_ci    global p0, p1, p2, pats_built
2367db96d56Sopenharmony_ci    if not pats_built:
2377db96d56Sopenharmony_ci        p0 = patcomp.compile_pattern(p0)
2387db96d56Sopenharmony_ci        p1 = patcomp.compile_pattern(p1)
2397db96d56Sopenharmony_ci        p2 = patcomp.compile_pattern(p2)
2407db96d56Sopenharmony_ci        pats_built = True
2417db96d56Sopenharmony_ci    patterns = [p0, p1, p2]
2427db96d56Sopenharmony_ci    for pattern, parent in zip(patterns, attr_chain(node, "parent")):
2437db96d56Sopenharmony_ci        results = {}
2447db96d56Sopenharmony_ci        if pattern.match(parent, results) and results["node"] is node:
2457db96d56Sopenharmony_ci            return True
2467db96d56Sopenharmony_ci    return False
2477db96d56Sopenharmony_ci
2487db96d56Sopenharmony_cidef is_probably_builtin(node):
2497db96d56Sopenharmony_ci    """
2507db96d56Sopenharmony_ci    Check that something isn't an attribute or function name etc.
2517db96d56Sopenharmony_ci    """
2527db96d56Sopenharmony_ci    prev = node.prev_sibling
2537db96d56Sopenharmony_ci    if prev is not None and prev.type == token.DOT:
2547db96d56Sopenharmony_ci        # Attribute lookup.
2557db96d56Sopenharmony_ci        return False
2567db96d56Sopenharmony_ci    parent = node.parent
2577db96d56Sopenharmony_ci    if parent.type in (syms.funcdef, syms.classdef):
2587db96d56Sopenharmony_ci        return False
2597db96d56Sopenharmony_ci    if parent.type == syms.expr_stmt and parent.children[0] is node:
2607db96d56Sopenharmony_ci        # Assignment.
2617db96d56Sopenharmony_ci        return False
2627db96d56Sopenharmony_ci    if parent.type == syms.parameters or \
2637db96d56Sopenharmony_ci            (parent.type == syms.typedargslist and (
2647db96d56Sopenharmony_ci            (prev is not None and prev.type == token.COMMA) or
2657db96d56Sopenharmony_ci            parent.children[0] is node
2667db96d56Sopenharmony_ci            )):
2677db96d56Sopenharmony_ci        # The name of an argument.
2687db96d56Sopenharmony_ci        return False
2697db96d56Sopenharmony_ci    return True
2707db96d56Sopenharmony_ci
2717db96d56Sopenharmony_cidef find_indentation(node):
2727db96d56Sopenharmony_ci    """Find the indentation of *node*."""
2737db96d56Sopenharmony_ci    while node is not None:
2747db96d56Sopenharmony_ci        if node.type == syms.suite and len(node.children) > 2:
2757db96d56Sopenharmony_ci            indent = node.children[1]
2767db96d56Sopenharmony_ci            if indent.type == token.INDENT:
2777db96d56Sopenharmony_ci                return indent.value
2787db96d56Sopenharmony_ci        node = node.parent
2797db96d56Sopenharmony_ci    return ""
2807db96d56Sopenharmony_ci
2817db96d56Sopenharmony_ci###########################################################
2827db96d56Sopenharmony_ci### The following functions are to find bindings in a suite
2837db96d56Sopenharmony_ci###########################################################
2847db96d56Sopenharmony_ci
2857db96d56Sopenharmony_cidef make_suite(node):
2867db96d56Sopenharmony_ci    if node.type == syms.suite:
2877db96d56Sopenharmony_ci        return node
2887db96d56Sopenharmony_ci    node = node.clone()
2897db96d56Sopenharmony_ci    parent, node.parent = node.parent, None
2907db96d56Sopenharmony_ci    suite = Node(syms.suite, [node])
2917db96d56Sopenharmony_ci    suite.parent = parent
2927db96d56Sopenharmony_ci    return suite
2937db96d56Sopenharmony_ci
2947db96d56Sopenharmony_cidef find_root(node):
2957db96d56Sopenharmony_ci    """Find the top level namespace."""
2967db96d56Sopenharmony_ci    # Scamper up to the top level namespace
2977db96d56Sopenharmony_ci    while node.type != syms.file_input:
2987db96d56Sopenharmony_ci        node = node.parent
2997db96d56Sopenharmony_ci        if not node:
3007db96d56Sopenharmony_ci            raise ValueError("root found before file_input node was found.")
3017db96d56Sopenharmony_ci    return node
3027db96d56Sopenharmony_ci
3037db96d56Sopenharmony_cidef does_tree_import(package, name, node):
3047db96d56Sopenharmony_ci    """ Returns true if name is imported from package at the
3057db96d56Sopenharmony_ci        top level of the tree which node belongs to.
3067db96d56Sopenharmony_ci        To cover the case of an import like 'import foo', use
3077db96d56Sopenharmony_ci        None for the package and 'foo' for the name. """
3087db96d56Sopenharmony_ci    binding = find_binding(name, find_root(node), package)
3097db96d56Sopenharmony_ci    return bool(binding)
3107db96d56Sopenharmony_ci
3117db96d56Sopenharmony_cidef is_import(node):
3127db96d56Sopenharmony_ci    """Returns true if the node is an import statement."""
3137db96d56Sopenharmony_ci    return node.type in (syms.import_name, syms.import_from)
3147db96d56Sopenharmony_ci
3157db96d56Sopenharmony_cidef touch_import(package, name, node):
3167db96d56Sopenharmony_ci    """ Works like `does_tree_import` but adds an import statement
3177db96d56Sopenharmony_ci        if it was not imported. """
3187db96d56Sopenharmony_ci    def is_import_stmt(node):
3197db96d56Sopenharmony_ci        return (node.type == syms.simple_stmt and node.children and
3207db96d56Sopenharmony_ci                is_import(node.children[0]))
3217db96d56Sopenharmony_ci
3227db96d56Sopenharmony_ci    root = find_root(node)
3237db96d56Sopenharmony_ci
3247db96d56Sopenharmony_ci    if does_tree_import(package, name, root):
3257db96d56Sopenharmony_ci        return
3267db96d56Sopenharmony_ci
3277db96d56Sopenharmony_ci    # figure out where to insert the new import.  First try to find
3287db96d56Sopenharmony_ci    # the first import and then skip to the last one.
3297db96d56Sopenharmony_ci    insert_pos = offset = 0
3307db96d56Sopenharmony_ci    for idx, node in enumerate(root.children):
3317db96d56Sopenharmony_ci        if not is_import_stmt(node):
3327db96d56Sopenharmony_ci            continue
3337db96d56Sopenharmony_ci        for offset, node2 in enumerate(root.children[idx:]):
3347db96d56Sopenharmony_ci            if not is_import_stmt(node2):
3357db96d56Sopenharmony_ci                break
3367db96d56Sopenharmony_ci        insert_pos = idx + offset
3377db96d56Sopenharmony_ci        break
3387db96d56Sopenharmony_ci
3397db96d56Sopenharmony_ci    # if there are no imports where we can insert, find the docstring.
3407db96d56Sopenharmony_ci    # if that also fails, we stick to the beginning of the file
3417db96d56Sopenharmony_ci    if insert_pos == 0:
3427db96d56Sopenharmony_ci        for idx, node in enumerate(root.children):
3437db96d56Sopenharmony_ci            if (node.type == syms.simple_stmt and node.children and
3447db96d56Sopenharmony_ci               node.children[0].type == token.STRING):
3457db96d56Sopenharmony_ci                insert_pos = idx + 1
3467db96d56Sopenharmony_ci                break
3477db96d56Sopenharmony_ci
3487db96d56Sopenharmony_ci    if package is None:
3497db96d56Sopenharmony_ci        import_ = Node(syms.import_name, [
3507db96d56Sopenharmony_ci            Leaf(token.NAME, "import"),
3517db96d56Sopenharmony_ci            Leaf(token.NAME, name, prefix=" ")
3527db96d56Sopenharmony_ci        ])
3537db96d56Sopenharmony_ci    else:
3547db96d56Sopenharmony_ci        import_ = FromImport(package, [Leaf(token.NAME, name, prefix=" ")])
3557db96d56Sopenharmony_ci
3567db96d56Sopenharmony_ci    children = [import_, Newline()]
3577db96d56Sopenharmony_ci    root.insert_child(insert_pos, Node(syms.simple_stmt, children))
3587db96d56Sopenharmony_ci
3597db96d56Sopenharmony_ci
3607db96d56Sopenharmony_ci_def_syms = {syms.classdef, syms.funcdef}
3617db96d56Sopenharmony_cidef find_binding(name, node, package=None):
3627db96d56Sopenharmony_ci    """ Returns the node which binds variable name, otherwise None.
3637db96d56Sopenharmony_ci        If optional argument package is supplied, only imports will
3647db96d56Sopenharmony_ci        be returned.
3657db96d56Sopenharmony_ci        See test cases for examples."""
3667db96d56Sopenharmony_ci    for child in node.children:
3677db96d56Sopenharmony_ci        ret = None
3687db96d56Sopenharmony_ci        if child.type == syms.for_stmt:
3697db96d56Sopenharmony_ci            if _find(name, child.children[1]):
3707db96d56Sopenharmony_ci                return child
3717db96d56Sopenharmony_ci            n = find_binding(name, make_suite(child.children[-1]), package)
3727db96d56Sopenharmony_ci            if n: ret = n
3737db96d56Sopenharmony_ci        elif child.type in (syms.if_stmt, syms.while_stmt):
3747db96d56Sopenharmony_ci            n = find_binding(name, make_suite(child.children[-1]), package)
3757db96d56Sopenharmony_ci            if n: ret = n
3767db96d56Sopenharmony_ci        elif child.type == syms.try_stmt:
3777db96d56Sopenharmony_ci            n = find_binding(name, make_suite(child.children[2]), package)
3787db96d56Sopenharmony_ci            if n:
3797db96d56Sopenharmony_ci                ret = n
3807db96d56Sopenharmony_ci            else:
3817db96d56Sopenharmony_ci                for i, kid in enumerate(child.children[3:]):
3827db96d56Sopenharmony_ci                    if kid.type == token.COLON and kid.value == ":":
3837db96d56Sopenharmony_ci                        # i+3 is the colon, i+4 is the suite
3847db96d56Sopenharmony_ci                        n = find_binding(name, make_suite(child.children[i+4]), package)
3857db96d56Sopenharmony_ci                        if n: ret = n
3867db96d56Sopenharmony_ci        elif child.type in _def_syms and child.children[1].value == name:
3877db96d56Sopenharmony_ci            ret = child
3887db96d56Sopenharmony_ci        elif _is_import_binding(child, name, package):
3897db96d56Sopenharmony_ci            ret = child
3907db96d56Sopenharmony_ci        elif child.type == syms.simple_stmt:
3917db96d56Sopenharmony_ci            ret = find_binding(name, child, package)
3927db96d56Sopenharmony_ci        elif child.type == syms.expr_stmt:
3937db96d56Sopenharmony_ci            if _find(name, child.children[0]):
3947db96d56Sopenharmony_ci                ret = child
3957db96d56Sopenharmony_ci
3967db96d56Sopenharmony_ci        if ret:
3977db96d56Sopenharmony_ci            if not package:
3987db96d56Sopenharmony_ci                return ret
3997db96d56Sopenharmony_ci            if is_import(ret):
4007db96d56Sopenharmony_ci                return ret
4017db96d56Sopenharmony_ci    return None
4027db96d56Sopenharmony_ci
4037db96d56Sopenharmony_ci_block_syms = {syms.funcdef, syms.classdef, syms.trailer}
4047db96d56Sopenharmony_cidef _find(name, node):
4057db96d56Sopenharmony_ci    nodes = [node]
4067db96d56Sopenharmony_ci    while nodes:
4077db96d56Sopenharmony_ci        node = nodes.pop()
4087db96d56Sopenharmony_ci        if node.type > 256 and node.type not in _block_syms:
4097db96d56Sopenharmony_ci            nodes.extend(node.children)
4107db96d56Sopenharmony_ci        elif node.type == token.NAME and node.value == name:
4117db96d56Sopenharmony_ci            return node
4127db96d56Sopenharmony_ci    return None
4137db96d56Sopenharmony_ci
4147db96d56Sopenharmony_cidef _is_import_binding(node, name, package=None):
4157db96d56Sopenharmony_ci    """ Will return node if node will import name, or node
4167db96d56Sopenharmony_ci        will import * from package.  None is returned otherwise.
4177db96d56Sopenharmony_ci        See test cases for examples. """
4187db96d56Sopenharmony_ci
4197db96d56Sopenharmony_ci    if node.type == syms.import_name and not package:
4207db96d56Sopenharmony_ci        imp = node.children[1]
4217db96d56Sopenharmony_ci        if imp.type == syms.dotted_as_names:
4227db96d56Sopenharmony_ci            for child in imp.children:
4237db96d56Sopenharmony_ci                if child.type == syms.dotted_as_name:
4247db96d56Sopenharmony_ci                    if child.children[2].value == name:
4257db96d56Sopenharmony_ci                        return node
4267db96d56Sopenharmony_ci                elif child.type == token.NAME and child.value == name:
4277db96d56Sopenharmony_ci                    return node
4287db96d56Sopenharmony_ci        elif imp.type == syms.dotted_as_name:
4297db96d56Sopenharmony_ci            last = imp.children[-1]
4307db96d56Sopenharmony_ci            if last.type == token.NAME and last.value == name:
4317db96d56Sopenharmony_ci                return node
4327db96d56Sopenharmony_ci        elif imp.type == token.NAME and imp.value == name:
4337db96d56Sopenharmony_ci            return node
4347db96d56Sopenharmony_ci    elif node.type == syms.import_from:
4357db96d56Sopenharmony_ci        # str(...) is used to make life easier here, because
4367db96d56Sopenharmony_ci        # from a.b import parses to ['import', ['a', '.', 'b'], ...]
4377db96d56Sopenharmony_ci        if package and str(node.children[1]).strip() != package:
4387db96d56Sopenharmony_ci            return None
4397db96d56Sopenharmony_ci        n = node.children[3]
4407db96d56Sopenharmony_ci        if package and _find("as", n):
4417db96d56Sopenharmony_ci            # See test_from_import_as for explanation
4427db96d56Sopenharmony_ci            return None
4437db96d56Sopenharmony_ci        elif n.type == syms.import_as_names and _find(name, n):
4447db96d56Sopenharmony_ci            return node
4457db96d56Sopenharmony_ci        elif n.type == syms.import_as_name:
4467db96d56Sopenharmony_ci            child = n.children[2]
4477db96d56Sopenharmony_ci            if child.type == token.NAME and child.value == name:
4487db96d56Sopenharmony_ci                return node
4497db96d56Sopenharmony_ci        elif n.type == token.NAME and n.value == name:
4507db96d56Sopenharmony_ci            return node
4517db96d56Sopenharmony_ci        elif package and n.type == token.STAR:
4527db96d56Sopenharmony_ci            return node
4537db96d56Sopenharmony_ci    return None
454