17db96d56Sopenharmony_ci# Copyright 2006 Google, Inc. All Rights Reserved.
27db96d56Sopenharmony_ci# Licensed to PSF under a Contributor Agreement.
37db96d56Sopenharmony_ci
47db96d56Sopenharmony_ci"""Fixer for has_key().
57db96d56Sopenharmony_ci
67db96d56Sopenharmony_ciCalls to .has_key() methods are expressed in terms of the 'in'
77db96d56Sopenharmony_cioperator:
87db96d56Sopenharmony_ci
97db96d56Sopenharmony_ci    d.has_key(k) -> k in d
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_ciCAVEATS:
127db96d56Sopenharmony_ci1) While the primary target of this fixer is dict.has_key(), the
137db96d56Sopenharmony_ci   fixer will change any has_key() method call, regardless of its
147db96d56Sopenharmony_ci   class.
157db96d56Sopenharmony_ci
167db96d56Sopenharmony_ci2) Cases like this will not be converted:
177db96d56Sopenharmony_ci
187db96d56Sopenharmony_ci    m = d.has_key
197db96d56Sopenharmony_ci    if m(k):
207db96d56Sopenharmony_ci        ...
217db96d56Sopenharmony_ci
227db96d56Sopenharmony_ci   Only *calls* to has_key() are converted. While it is possible to
237db96d56Sopenharmony_ci   convert the above to something like
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_ci    m = d.__contains__
267db96d56Sopenharmony_ci    if m(k):
277db96d56Sopenharmony_ci        ...
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ci   this is currently not done.
307db96d56Sopenharmony_ci"""
317db96d56Sopenharmony_ci
327db96d56Sopenharmony_ci# Local imports
337db96d56Sopenharmony_cifrom .. import pytree
347db96d56Sopenharmony_cifrom .. import fixer_base
357db96d56Sopenharmony_cifrom ..fixer_util import Name, parenthesize
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ci
387db96d56Sopenharmony_ciclass FixHasKey(fixer_base.BaseFix):
397db96d56Sopenharmony_ci    BM_compatible = True
407db96d56Sopenharmony_ci
417db96d56Sopenharmony_ci    PATTERN = """
427db96d56Sopenharmony_ci    anchor=power<
437db96d56Sopenharmony_ci        before=any+
447db96d56Sopenharmony_ci        trailer< '.' 'has_key' >
457db96d56Sopenharmony_ci        trailer<
467db96d56Sopenharmony_ci            '('
477db96d56Sopenharmony_ci            ( not(arglist | argument<any '=' any>) arg=any
487db96d56Sopenharmony_ci            | arglist<(not argument<any '=' any>) arg=any ','>
497db96d56Sopenharmony_ci            )
507db96d56Sopenharmony_ci            ')'
517db96d56Sopenharmony_ci        >
527db96d56Sopenharmony_ci        after=any*
537db96d56Sopenharmony_ci    >
547db96d56Sopenharmony_ci    |
557db96d56Sopenharmony_ci    negation=not_test<
567db96d56Sopenharmony_ci        'not'
577db96d56Sopenharmony_ci        anchor=power<
587db96d56Sopenharmony_ci            before=any+
597db96d56Sopenharmony_ci            trailer< '.' 'has_key' >
607db96d56Sopenharmony_ci            trailer<
617db96d56Sopenharmony_ci                '('
627db96d56Sopenharmony_ci                ( not(arglist | argument<any '=' any>) arg=any
637db96d56Sopenharmony_ci                | arglist<(not argument<any '=' any>) arg=any ','>
647db96d56Sopenharmony_ci                )
657db96d56Sopenharmony_ci                ')'
667db96d56Sopenharmony_ci            >
677db96d56Sopenharmony_ci        >
687db96d56Sopenharmony_ci    >
697db96d56Sopenharmony_ci    """
707db96d56Sopenharmony_ci
717db96d56Sopenharmony_ci    def transform(self, node, results):
727db96d56Sopenharmony_ci        assert results
737db96d56Sopenharmony_ci        syms = self.syms
747db96d56Sopenharmony_ci        if (node.parent.type == syms.not_test and
757db96d56Sopenharmony_ci            self.pattern.match(node.parent)):
767db96d56Sopenharmony_ci            # Don't transform a node matching the first alternative of the
777db96d56Sopenharmony_ci            # pattern when its parent matches the second alternative
787db96d56Sopenharmony_ci            return None
797db96d56Sopenharmony_ci        negation = results.get("negation")
807db96d56Sopenharmony_ci        anchor = results["anchor"]
817db96d56Sopenharmony_ci        prefix = node.prefix
827db96d56Sopenharmony_ci        before = [n.clone() for n in results["before"]]
837db96d56Sopenharmony_ci        arg = results["arg"].clone()
847db96d56Sopenharmony_ci        after = results.get("after")
857db96d56Sopenharmony_ci        if after:
867db96d56Sopenharmony_ci            after = [n.clone() for n in after]
877db96d56Sopenharmony_ci        if arg.type in (syms.comparison, syms.not_test, syms.and_test,
887db96d56Sopenharmony_ci                        syms.or_test, syms.test, syms.lambdef, syms.argument):
897db96d56Sopenharmony_ci            arg = parenthesize(arg)
907db96d56Sopenharmony_ci        if len(before) == 1:
917db96d56Sopenharmony_ci            before = before[0]
927db96d56Sopenharmony_ci        else:
937db96d56Sopenharmony_ci            before = pytree.Node(syms.power, before)
947db96d56Sopenharmony_ci        before.prefix = " "
957db96d56Sopenharmony_ci        n_op = Name("in", prefix=" ")
967db96d56Sopenharmony_ci        if negation:
977db96d56Sopenharmony_ci            n_not = Name("not", prefix=" ")
987db96d56Sopenharmony_ci            n_op = pytree.Node(syms.comp_op, (n_not, n_op))
997db96d56Sopenharmony_ci        new = pytree.Node(syms.comparison, (arg, n_op, before))
1007db96d56Sopenharmony_ci        if after:
1017db96d56Sopenharmony_ci            new = parenthesize(new)
1027db96d56Sopenharmony_ci            new = pytree.Node(syms.power, (new,) + tuple(after))
1037db96d56Sopenharmony_ci        if node.parent.type in (syms.comparison, syms.expr, syms.xor_expr,
1047db96d56Sopenharmony_ci                                syms.and_expr, syms.shift_expr,
1057db96d56Sopenharmony_ci                                syms.arith_expr, syms.term,
1067db96d56Sopenharmony_ci                                syms.factor, syms.power):
1077db96d56Sopenharmony_ci            new = parenthesize(new)
1087db96d56Sopenharmony_ci        new.prefix = prefix
1097db96d56Sopenharmony_ci        return new
110