Example #1
0
 def simplify(self, node, vm, match_signature=None):
     """Try to insert part of *args, **kwargs into posargs / namedargs."""
     # TODO(rechen): When we have type information about *args/**kwargs,
     # we need to check it before doing this simplification.
     posargs = self.posargs
     namedargs = self.namedargs
     starargs = self.starargs
     starstarargs = self.starstarargs
     # Unpack starstarargs into namedargs. We need to do this first so we can see
     # what posargs are still required.
     starstarargs_as_dict = self.starstarargs_as_dict()
     if starstarargs_as_dict is not None:
         # Unlike varargs below, we do not adjust starstarargs into namedargs when
         # the function signature has matching param_names because we have not
         # found a benefit in doing so.
         if self.namedargs is None:
             namedargs = starstarargs_as_dict
         else:
             namedargs.update(node, starstarargs_as_dict)
         starstarargs = None
     starargs_as_tuple = self.starargs_as_tuple(node, vm)
     if starargs_as_tuple is not None:
         if match_signature:
             posargs, starargs = self._unpack_and_match_args(
                 node, vm, match_signature, starargs_as_tuple)
         elif (starargs_as_tuple
               and abstract_utils.is_var_splat(starargs_as_tuple[-1])):
             # If the last arg is an indefinite iterable keep it in starargs. Convert
             # any other splats to Any.
             # TODO(mdemello): If there are multiple splats should we just fall
             # through to the next case (setting them all to Any), and only hit this
             # case for a *single* splat in terminal position?
             posargs = self.posargs + _splats_to_any(
                 starargs_as_tuple[:-1], vm)
             starargs = abstract_utils.unwrap_splat(starargs_as_tuple[-1])
         else:
             # Don't try to unpack iterables in any other position since we don't
             # have a signature to match. Just set all splats to Any.
             posargs = self.posargs + _splats_to_any(starargs_as_tuple, vm)
             starargs = None
     return Args(posargs, namedargs, starargs, starstarargs)
Example #2
0
def _splats_to_any(seq, vm):
  return tuple(
      vm.new_unsolvable(vm.root_node) if abstract_utils.is_var_splat(v) else v
      for v in seq)
Example #3
0
  def _unpack_and_match_args(self, node, vm, match_signature, starargs_tuple):
    """Match args against a signature with unpacking."""
    posargs = self.posargs
    namedargs = self.namedargs
    # As we have the function signature we will attempt to adjust the
    # starargs into the missing posargs.
    pre = []
    post = []
    stars = collections.deque(starargs_tuple)
    while stars and not abstract_utils.is_var_splat(stars[0]):
      pre.append(stars.popleft())
    while stars and not abstract_utils.is_var_splat(stars[-1]):
      post.append(stars.pop())
    post.reverse()
    n_matched = len(posargs) + len(pre) + len(post)
    required_posargs = 0
    for p in match_signature.param_names:
      if p in namedargs or p in match_signature.defaults:
        break
      required_posargs += 1
    posarg_delta = required_posargs - n_matched

    if stars and not post:
      star = stars[-1]
      if match_signature.varargs_name:
        # If the invocation ends with `*args`, return it to match against *args
        # in the function signature. For f(<k args>, *xs, ..., *ys), transform
        # to f(<k args>, *ys) since ys is an indefinite tuple anyway and will
        # match against all remaining posargs.
        return posargs + tuple(pre), abstract_utils.unwrap_splat(star)
      else:
        # If we do not have a `*args` in match_signature, just expand the
        # terminal splat to as many args as needed and then drop it.
        mid = self._expand_typed_star(vm, node, star, posarg_delta)
        return posargs + tuple(pre + mid), None
    elif posarg_delta <= len(stars):
      # We have too many args; don't do *xs expansion. Go back to matching from
      # the start and treat every entry in starargs_tuple as length 1.
      n_params = len(match_signature.param_names)
      all_args = posargs + starargs_tuple
      if not match_signature.varargs_name:
        # If the function sig has no *args, return everything in posargs
        pos = _splats_to_any(all_args, vm)
        return pos, None
      # Don't unwrap splats here because f(*xs, y) is not the same as f(xs, y).
      # TODO(mdemello): Ideally, since we are matching call f(*xs, y) against
      # sig f(x, y) we should raise an error here.
      pos = _splats_to_any(all_args[:n_params], vm)
      star = []
      for var in all_args[n_params:]:
        if abstract_utils.is_var_splat(var):
          star.append(
              abstract_utils.merged_type_parameter(node, var, abstract_utils.T))
        else:
          star.append(var)
      if star:
        return pos, vm.convert.tuple_to_value(star).to_variable(node)
      else:
        return pos, None
    elif stars:
      if len(stars) == 1:
        # Special case (<pre>, *xs) and (*xs, <post>) to fill in the type of xs
        # in every remaining arg.
        mid = self._expand_typed_star(vm, node, stars[0], posarg_delta)
      else:
        # If we have (*xs, <k args>, *ys) remaining, and more than k+2 params to
        # match, don't try to match the intermediate params to any range, just
        # match all k+2 to Any
        mid = [vm.new_unsolvable(node) for _ in range(posarg_delta)]
      return posargs + tuple(pre + mid + post), None
    else:
      # We have **kwargs but no *args in the invocation
      return posargs + tuple(pre), None
Example #4
0
  def _unpack_and_match_args(self, node, vm, match_signature, starargs_tuple):
    """Match args against a signature with unpacking."""

    posargs = self.posargs
    # As we have the function signature we will attempt to adjust the
    # starargs into the missing posargs.
    pre = []
    post = []
    stars = collections.deque(starargs_tuple)
    while stars and not abstract_utils.is_var_splat(stars[0]):
      pre.append(stars.popleft())
    while stars and not abstract_utils.is_var_splat(stars[-1]):
      post.append(stars.pop())
    post.reverse()
    n_matched = len(posargs) + len(pre) + len(post)
    posarg_delta = len(match_signature.param_names) - n_matched
    if stars and not post:
      # If the invocation ends with `*args`, return it to match against *args in
      # the function signature. For f(<k args>, *xs, ..., *ys), transform to
      # f(<k args>, *ys) since ys is an indefinite tuple anyway and will match
      # against all remaining posargs.
      return posargs + tuple(pre), abstract_utils.unwrap_splat(stars[-1])
    elif posarg_delta <= len(stars):
      # We have too many args; don't do *xs expansion. Go back to matching from
      # the start and treat every entry in starargs_tuple as length 1.
      n_params = len(match_signature.param_names)
      all_args = posargs + starargs_tuple
      # Don't unwrap splats here because f(*xs, y) is not the same as f(xs, y).
      # TODO(mdemello): Ideally, since we are matching call f(*xs, y) against
      # sig f(x, y) we should raise an error here.
      pos = _splats_to_any(all_args[:n_params], vm)
      star = []
      for var in all_args[n_params:]:
        if abstract_utils.is_var_splat(var):
          star.append(
              abstract_utils.merged_type_parameter(node, var, abstract_utils.T))
        else:
          star.append(var)
      if star:
        return pos, vm.convert.tuple_to_value(star).to_variable(node)
      else:
        return pos, None
    elif stars:
      if len(stars) == 1:
        # Special case (*xs, <post>) to fill in the type of xs in every arg
        p = abstract_utils.merged_type_parameter(
            node, stars[0], abstract_utils.T)
        if not p.bindings:
          # TODO(b/159052609): This shouldn't happen. For some reason,
          # namedtuple instances don't have any bindings in T; see
          # tests/py3/test_unpack:TestUnpack.test_unpack_namedtuple.
          p.AddBinding(vm.convert.unsolvable)
        mid = [p.AssignToNewVariable(node) for _ in range(posarg_delta)]
      else:
        # If we have (*xs, <k args>, *ys) remaining, and more than k+2 params to
        # match, don't try to match the intermediate params to any range, just
        # match all k+2 to Any
        mid = [vm.new_unsolvable(node) for _ in range(posarg_delta)]
      return posargs + tuple(pre + mid + post), None
    else:
      # We have **kwargs but no *args in the invocation
      return posargs + tuple(pre), None