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
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
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
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
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
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)
def GetTypeParameters(node): collector = visitors.CollectTypeParameters() node.Visit(collector) return collector.params
def _GetTypeParameters(self, node): collector = visitors.CollectTypeParameters() node.Visit(collector) return {x.name for x in collector.params}