Example #1
0
    def instantiate_return(self, node, subst, sources):
        return_type = self.pytd_sig.return_type
        # Type parameter values, which are instantiated by the matcher, will end up
        # in the return value. Since the matcher does not call __init__, we need to
        # do that now. The one exception is that Type[X] does not instantiate X, so
        # we do not call X.__init__.
        if (not isinstance(return_type, pytd.GenericType)
                or return_type.base_type.name != "__builtin__.type"):
            for param in pytd_utils.GetTypeParameters(return_type):
                if param.full_name in subst:
                    node = self.vm.call_init(node, subst[param.full_name])
        try:
            ret = self.vm.convert.constant_to_var(
                abstract_utils.AsReturnValue(return_type),
                subst,
                node,
                source_sets=[sources])
        except self.vm.convert.TypeParameterError:
            # The return type contains a type parameter without a substitution.
            subst = subst.copy()
            visitor = visitors.CollectTypeParameters()
            return_type.Visit(visitor)

            for t in visitor.params:
                if t.full_name not in subst:
                    subst[t.full_name] = self.vm.convert.empty.to_variable(
                        node)
            return node, self.vm.convert.constant_to_var(
                abstract_utils.AsReturnValue(return_type),
                subst,
                node,
                source_sets=[sources])
        if not ret.bindings and isinstance(return_type, pytd.TypeParameter):
            ret.AddBinding(self.vm.convert.empty, [], node)
        return node, ret
Example #2
0
    def instantiate_return(self, node, subst, sources):
        return_type = self.pytd_sig.return_type
        for param in pytd_utils.GetTypeParameters(return_type):
            if param.full_name in subst:
                # This value, which was instantiated by the matcher, will end up in the
                # return value. Since the matcher does not call __init__, we need to do
                # that now.
                node = self.vm.call_init(node, subst[param.full_name])
        try:
            ret = self.vm.convert.constant_to_var(
                abstract_utils.AsReturnValue(return_type),
                subst,
                node,
                source_sets=[sources])
        except self.vm.convert.TypeParameterError:
            # The return type contains a type parameter without a substitution.
            subst = subst.copy()
            visitor = visitors.CollectTypeParameters()
            return_type.Visit(visitor)

            for t in visitor.params:
                if t.full_name not in subst:
                    subst[t.full_name] = self.vm.convert.empty.to_variable(
                        node)
            return node, self.vm.convert.constant_to_var(
                abstract_utils.AsReturnValue(return_type),
                subst,
                node,
                source_sets=[sources])
        if not ret.bindings and isinstance(return_type, pytd.TypeParameter):
            ret.AddBinding(self.vm.convert.empty, [], node)
        return node, ret
Example #3
0
 def _function_to_return_types(self, node, fvar):
     """Convert a function variable to a list of PyTD return types."""
     options = fvar.FilteredData(self.vm.exitpoint, strict=False)
     if not all(isinstance(o, abstract.Function) for o in options):
         return [pytd.AnythingType()]
     types = []
     for val in options:
         if isinstance(val, abstract.InterpreterFunction):
             combinations = val.get_call_combinations(node)
             for node_after, _, return_value in combinations:
                 types.append(
                     self._function_call_to_return_type(
                         node_after, val, return_value, len(combinations)))
         elif isinstance(val, abstract.PyTDFunction):
             types.extend(sig.pytd_sig.return_type
                          for sig in val.signatures)
         else:
             types.append(pytd.AnythingType())
     safe_types = []  # types without type parameters
     for t in types:
         collector = visitors.CollectTypeParameters()
         t.Visit(collector)
         t = t.Visit(
             visitors.ReplaceTypeParameters(
                 {p: p.upper_value
                  for p in collector.params}))
         safe_types.append(t)
     return safe_types
Example #4
0
  def _get_mutation(self, node, arg_dict, subst):
    """Mutation for changing the type parameters of mutable arguments.

    This will adjust the type parameters as needed for pytd functions like:
      def append_float(x: list[int]):
        x = list[int or float]
    This is called after all the signature matching has succeeded, and we
    know we're actually calling this function.

    Args:
      node: The current CFG node.
      arg_dict: A map of strings to pytd.Bindings instances.
      subst: Current type parameters.
    Returns:
      A list of Mutation instances.
    Raises:
      ValueError: If the pytd contains invalid information for mutated params.
    """
    # Handle mutable parameters using the information type parameters
    mutations = []
    # It's possible that the signature contains type parameters that are used
    # in mutations but are not filled in by the arguments, e.g. when starargs
    # and starstarargs have type parameters but are not in the args. Check that
    # subst has an entry for every type parameter, adding any that are missing.
    if any(f.mutated_type for f in self.pytd_sig.params):
      subst = subst.copy()
      visitor = visitors.CollectTypeParameters()
      self.pytd_sig.Visit(visitor)
      for t in visitor.params:
        if t.full_name not in subst:
          subst[t.full_name] = self.vm.convert.empty.to_variable(node)
    for formal in self.pytd_sig.params:
      actual = arg_dict[formal.name]
      arg = actual.data
      if (formal.mutated_type is not None and arg.isinstance_SimpleValue()):
        if (isinstance(formal.type, pytd.GenericType) and
            isinstance(formal.mutated_type, pytd.GenericType) and
            formal.type.base_type == formal.mutated_type.base_type and
            isinstance(formal.type.base_type, pytd.ClassType) and
            formal.type.base_type.cls):
          names_actuals = zip(formal.mutated_type.base_type.cls.template,
                              formal.mutated_type.parameters)
          for tparam, type_actual in names_actuals:
            log.info("Mutating %s to %s",
                     tparam.name,
                     pytd_utils.Print(type_actual))
            type_actual_val = self.vm.convert.constant_to_var(
                abstract_utils.AsInstance(type_actual), subst, node,
                discard_concrete_values=True)
            mutations.append(Mutation(arg, tparam.full_name, type_actual_val))
        else:
          log.error("Old: %s", pytd_utils.Print(formal.type))
          log.error("New: %s", pytd_utils.Print(formal.mutated_type))
          log.error("Actual: %r", actual)
          raise ValueError("Mutable parameters setting a type to a "
                           "different base type is not allowed.")
    return mutations
Example #5
0
    def call_with_args(self,
                       node,
                       func,
                       arg_dict,
                       subst,
                       ret_map,
                       alias_map=None):
        """Call this signature. Used by PyTDFunction."""
        return_type = self.pytd_sig.return_type
        t = (return_type, subst)
        sources = [func] + list(arg_dict.values())
        if t not in ret_map:
            for param in pytd_utils.GetTypeParameters(return_type):
                if param.full_name in subst:
                    # This value, which was instantiated by the matcher, will end up in
                    # the return value. Since the matcher does not call __init__, we need
                    # to do that now.
                    node = self.vm.call_init(node, subst[param.full_name])
            try:
                ret_map[t] = self.vm.convert.constant_to_var(
                    abstract_utils.AsReturnValue(return_type),
                    subst,
                    node,
                    source_sets=[sources])
            except self.vm.convert.TypeParameterError:
                # The return type contains a type parameter without a substitution.
                subst = subst.copy()
                visitor = visitors.CollectTypeParameters()
                return_type.Visit(visitor)

                for t in visitor.params:
                    if t.full_name not in subst:
                        subst[t.full_name] = self.vm.convert.empty.to_variable(
                            node)
                ret_map[t] = self.vm.convert.constant_to_var(
                    abstract_utils.AsReturnValue(return_type),
                    subst,
                    node,
                    source_sets=[sources])
            else:
                if (not ret_map[t].bindings
                        and isinstance(return_type, pytd.TypeParameter)):
                    ret_map[t].AddBinding(self.vm.convert.empty, [], node)
        else:
            # add the new sources
            for data in ret_map[t].data:
                ret_map[t].AddBinding(data, sources, node)
        mutations = self._get_mutation(node, arg_dict, subst)
        self.vm.trace_call(
            node, func, (self, ),
            tuple(arg_dict[p.name] for p in self.pytd_sig.params), {},
            ret_map[t])
        return node, ret_map[t], mutations
Example #6
0
 def add_attributes_from(instance):
     for name, member in instance.members.items():
         if name in CLASS_LEVEL_IGNORE or name in ignore:
             continue
         for value in member.FilteredData(self.vm.exitpoint,
                                          strict=False):
             typ = value.to_type(node)
             collector = visitors.CollectTypeParameters()
             typ.Visit(collector)
             if collector.params:
                 # This attribute's type comes from an annotation that contains a
                 # type parameter; we do not want to merge in substituted values of
                 # the type parameter.
                 canonical_attributes.add(name)
             constants[name].add_type(typ)
Example #7
0
def GetTypeParameters(node):
    collector = visitors.CollectTypeParameters()
    node.Visit(collector)
    return collector.params
Example #8
0
 def _GetTypeParameters(self, node):
     collector = visitors.CollectTypeParameters()
     node.Visit(collector)
     return {x.name for x in collector.params}