def add_method(funcname: str, ret: Type, args: List[Argument], name: Optional[str] = None, is_classmethod: bool = False, is_new: bool = False, ) -> None: if is_classmethod or is_new: first = [Argument(Var('cls'), TypeType.make_normalized(selftype), None, ARG_POS)] else: first = [Argument(Var('self'), selftype, None, ARG_POS)] args = first + args types = [arg.type_annotation for arg in args] items = [arg.variable.name() for arg in args] arg_kinds = [arg.kind for arg in args] assert None not in types signature = CallableType(cast(List[Type], types), arg_kinds, items, ret, function_type) signature.variables = [tvd] func = FuncDef(funcname, args, Block([])) func.info = info func.is_class = is_classmethod func.type = set_callable_name(signature, func) func._fullname = info.fullname() + '.' + funcname if is_classmethod: v = Var(funcname, func.type) v.is_classmethod = True v.info = info v._fullname = func._fullname dec = Decorator(func, [NameExpr('classmethod')], v) info.names[funcname] = SymbolTableNode(MDEF, dec) else: info.names[funcname] = SymbolTableNode(MDEF, func)
def add_method( ctx: ClassDefContext, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarDef] = None, ) -> None: """Adds a new method to a class. """ info = ctx.cls.info self_type = self_type or fill_typevars(info) function_type = ctx.api.named_type('__builtins__.function') args = [Argument(Var('self'), self_type, None, ARG_POS)] + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, 'All arguments must be fully typed.' arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name()) arg_kinds.append(arg.kind) signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func._fullname = info.fullname() + '.' + name func.line = info.line info.names[name] = SymbolTableNode(MDEF, func, plugin_generated=True) info.defn.defs.body.append(func)
def add_method(self, method_name: str, args: List[Argument], ret_type: Type, self_type: Optional[Type] = None, tvd: Optional[TypeVarDef] = None) -> None: """Add a method: def <method_name>(self, <args>) -> <ret_type>): ... to info. self_type: The type to use for the self argument or None to use the inferred self type. tvd: If the method is generic these should be the type variables. """ from mypy.semanal import set_callable_name self_type = self_type if self_type is not None else self.self_type args = [Argument(Var('self'), self_type, None, ARG_POS)] + args arg_types = [arg.type_annotation for arg in args] arg_names = [arg.variable.name() for arg in args] arg_kinds = [arg.kind for arg in args] assert None not in arg_types signature = CallableType(cast(List[Type], arg_types), arg_kinds, arg_names, ret_type, self.function_type) if tvd: signature.variables = [tvd] func = FuncDef(method_name, args, Block([PassStmt()])) func.info = self.info func.type = set_callable_name(signature, func) func._fullname = self.info.fullname() + '.' + method_name func.line = self.info.line self.info.names[method_name] = SymbolTableNode(MDEF, func) # Add the created methods to the body so that they can get further semantic analysis. # e.g. Forward Reference Resolution. self.info.defn.defs.body.append(func)
def make_setter_wrapper(self, name, typ): """Create a setter wrapper for a data attribute. The setter will be of this form: . void set$name(C self, typ name): . self.name! = name """ scope = self.make_scope() selft = self.self_type() selfv = scope.add('self', selft) namev = scope.add(name, typ) lvalue = MemberExpr(scope.name_expr('self'), name, direct=True) rvalue = scope.name_expr(name) ret = AssignmentStmt([lvalue], rvalue) wrapper_name = 'set$' + name sig = Callable([selft, typ], [nodes.ARG_POS, nodes.ARG_POS], [None, None], Void(), False) fdef = FuncDef(wrapper_name, [selfv, namev], [nodes.ARG_POS, nodes.ARG_POS], [None, None], Block([ret]), sig) fdef.info = self.tf.type_context() return fdef
def visit_func_def(self, node: FuncDef) -> None: if not self.recurse_into_functions: return node.expanded = [] node.type = node.unanalyzed_type with self.enter_method(node.info) if node.info else nothing(): super().visit_func_def(node)
def visit_func_def(self, o: FuncDef) -> None: if self.is_private_name(o.name()): return if self.is_not_in_all(o.name()): return if self.is_recorded_name(o.name()): return if not self._indent and self._state not in (EMPTY, FUNC): self.add('\n') if not self.is_top_level(): self_inits = find_self_initializers(o) for init in self_inits: init_code = self.get_init(init) if init_code: self.add(init_code) self.add("%sdef %s(" % (self._indent, o.name())) self.record_name(o.name()) args = [] # type: List[str] for i, arg_ in enumerate(o.arguments): var = arg_.variable kind = arg_.kind name = var.name() init_stmt = arg_.initialization_statement if init_stmt: if kind == ARG_NAMED and '*' not in args: args.append('*') arg = '%s=' % name rvalue = init_stmt.rvalue if isinstance(rvalue, IntExpr): arg += str(rvalue.value) elif isinstance(rvalue, StrExpr): arg += "''" elif isinstance(rvalue, BytesExpr): arg += "b''" elif isinstance(rvalue, FloatExpr): arg += "0.0" elif isinstance(rvalue, UnaryExpr) and isinstance(rvalue.expr, IntExpr): arg += '-%s' % rvalue.expr.value elif isinstance(rvalue, NameExpr) and rvalue.name in ('None', 'True', 'False'): arg += rvalue.name else: arg += '...' elif kind == ARG_STAR: arg = '*%s' % name elif kind == ARG_STAR2: arg = '**%s' % name else: arg = name args.append(arg) self.add(', '.join(args)) self.add("): ...\n") self._state = FUNC
def visit_func_def(self, node: FuncDef) -> None: if not self.recurse_into_functions: return node.expanded = [] node.type = node.unanalyzed_type # Type variable binder binds tvars before the type is analyzed. # It should be refactored, before that we just undo this change here. # TODO: this will be not necessary when #4814 is fixed. if node.type: assert isinstance(node.type, CallableType) node.type.variables = [] with self.enter_method(node.info) if node.info else nothing(): super().visit_func_def(node)
def visit_func_def(self, func: FuncDef) -> None: sem = self.sem if sem.type is not None: # Don't process methods during pass 1. return func.is_conditional = sem.block_depth[-1] > 0 func._fullname = sem.qualified_name(func.name()) at_module = sem.is_module_scope() if at_module and func.name() in sem.globals: # Already defined in this module. original_sym = sem.globals[func.name()] if original_sym.kind == UNBOUND_IMPORTED: # Ah this is an imported name. We can't resolve them now, so we'll postpone # this until the main phase of semantic analysis. return if not sem.set_original_def(original_sym.node, func): # Report error. sem.check_no_global(func.name(), func) else: if at_module: sem.globals[func.name()] = SymbolTableNode(GDEF, func) # Also analyze the function body (needed in case there are unreachable # conditional imports). sem.function_stack.append(func) sem.errors.push_function(func.name()) sem.enter() func.body.accept(self) sem.leave() sem.errors.pop_function() sem.function_stack.pop()
def visit_func_def(self, node: FuncDef) -> None: if not self.recurse_into_functions: return node.expanded = [] node.type = node.unanalyzed_type if node.type: # Type variable binder binds type variables before the type is analyzed, # this causes unanalyzed_type to be modified in place. We needed to revert this # in order to get the state exactly as it was before semantic analysis. # See also #4814. assert isinstance(node.type, CallableType) node.type.variables = [] with self.enter_method(node.info) if node.info else nothing(): super().visit_func_def(node)
def set_callable_name(sig: Type, fdef: FuncDef) -> Type: if isinstance(sig, FunctionLike): if fdef.info: if fdef.info.fullname() in TPDICT_FB_NAMES: # Avoid exposing the internal _TypedDict name. class_name = 'TypedDict' else: class_name = fdef.info.name() return sig.with_name( '{} of {}'.format(fdef.name(), class_name)) else: return sig.with_name(fdef.name()) else: return sig
def make_generic_wrapper_init(self, info: TypeInfo) -> FuncDef: """Build constructor of a generic wrapper class.""" nslots = num_slots(info) cdefs = [] # type: List[Node] # Build superclass constructor call. base = info.mro[1] if base.fullname() != 'builtins.object' and self.tf.is_java: s = SuperExpr('__init__') cargs = [NameExpr('__o')] # type: List[Node] for n in range(num_slots(base)): cargs.append(NameExpr(tvar_arg_name(n + 1))) for n in range(num_slots(base)): cargs.append(NameExpr(tvar_arg_name(n + 1, BOUND_VAR))) c = CallExpr(s, cargs, [nodes.ARG_POS] * len(cargs)) cdefs.append(ExpressionStmt(c)) # Create initialization of the wrapped object. cdefs.append(AssignmentStmt([MemberExpr( self_expr(), self.object_member_name(info), direct=True)], NameExpr('__o'))) # Build constructor arguments. args = [Var('self'), Var('__o')] init = [None, None] # type: List[Node] for alt in [False, BOUND_VAR]: for n in range(nslots): args.append(Var(tvar_arg_name(n + 1, alt))) init.append(None) nargs = nslots * 2 + 2 fdef = FuncDef('__init__', args, [nodes.ARG_POS] * nargs, init, Block(cdefs), Callable( [AnyType()] * nargs, [nodes.ARG_POS] * nargs, [None] * nargs, Void(), is_type_obj=False)) fdef.info = info self.make_wrapper_slot_initializer(fdef) return fdef
def visit_func_def(self, defn: FuncDef) -> None: start_line = defn.get_line() - 1 start_indent = self.indentation_level(start_line) cur_line = start_line + 1 end_line = cur_line # After this loop, function body will be lines [start_line, end_line) while cur_line < len(self.source): cur_indent = self.indentation_level(cur_line) if cur_indent is None: # Consume the line, but don't mark it as belonging to the function yet. cur_line += 1 elif cur_indent > start_indent: # A non-blank line that belongs to the function. cur_line += 1 end_line = cur_line else: # We reached a line outside the function definition. break is_typed = defn.type is not None for line in range(start_line, end_line): old_indent, _ = self.lines_covered[line] assert start_indent > old_indent self.lines_covered[line] = (start_indent, is_typed) # Visit the body, in case there are nested functions super().visit_func_def(defn)
def enter_function_scope(self, fdef: FuncDef) -> str: """Enter a function target scope.""" target = '%s.%s' % (self.full_target_stack[-1], fdef.name()) self.target_stack.append(target) self.full_target_stack.append(target) self.scope_stack.append(fdef) return target
def visit_func_def(self, fdef: FuncDef) -> None: if not self.recurse_into_functions: return self.errors.push_function(fdef.name()) self.analyze(fdef.type, fdef) super().visit_func_def(fdef) self.errors.pop_function()
def try_type(self, func: FuncDef, typ: Type) -> List[str]: """Recheck a function while assuming it has type typ. Return all error messages. """ old = func.unanalyzed_type # During reprocessing, unanalyzed_type gets copied to type (by aststrip). # We don't modify type because it isn't necessary and it # would mess up the snapshotting. func.unanalyzed_type = typ try: res = self.fgmanager.trigger(func.fullname()) # if res: # print('\n'.join(res)) return res finally: func.unanalyzed_type = old
def visit_func_def(self, func: FuncDef) -> None: if self.current_info is not None: func.info = self.current_info if func.type is not None: func.type.accept(self.type_fixer) for arg in func.arguments: if arg.type_annotation is not None: arg.type_annotation.accept(self.type_fixer)
def transform_method_implementation(self, fdef, name): """Transform the implementation of a method (i.e. unwrapped).""" args = fdef.args arg_kinds = fdef.arg_kinds typ = function_type(fdef) init = fdef.init_expressions() if fdef.name() == '__init__' and is_generic(fdef): args, arg_kinds, init, typ = self.add_constructor_tvar_args( fdef, typ, args, arg_kinds, init) fdef2 = FuncDef(name, args, arg_kinds, init, fdef.body, typ) fdef2.info = fdef.info self.tf.prepend_generic_function_tvar_args(fdef2) return fdef2
def visit_func_def(self, node: FuncDef) -> FuncDef: # Note that a FuncDef must be transformed to a FuncDef. new = FuncDef(node.name(), [self.copy_argument(arg) for arg in node.arguments], self.block(node.body), cast(FunctionLike, self.optional_type(node.type))) self.copy_function_attributes(new, node) new._fullname = node._fullname new.is_decorated = node.is_decorated new.is_conditional = node.is_conditional new.is_abstract = node.is_abstract new.is_static = node.is_static new.is_class = node.is_class new.is_property = node.is_property new.original_def = node.original_def return new
def transform_method(self, fdef: FuncDef) -> List[FuncDef]: """Transform a method. The result is one or more methods. """ # Transform the body of the method. self.tf.transform_function_body(fdef) res = Undefined # type: List[FuncDef] if fdef.is_constructor(): # The method is a constructor. Constructors are transformed to one # method. res = [self.transform_method_implementation(fdef, fdef.name())] else: # Normal methods are transformed to 1-3 variants. The # first is the main implementation of the method, and the # second is the dynamically-typed wrapper. The third # variant is for method overrides, and represents the # overridden supertype method. res = [self.transform_method_implementation( fdef, fdef.name() + self.tf.type_suffix(fdef))] if fdef.info.bases and fdef.info.mro[1].has_method(fdef.name()): # Override. # TODO do not assume single inheritance # Is is an override with a different signature? For # trivial overrides we can inherit wrappers. if not is_simple_override(fdef, fdef.info): # Create a wrapper for overridden superclass method. res.append(self.override_method_wrapper(fdef)) # Create a dynamically-typed method wrapper. res.append(self.dynamic_method_wrapper(fdef)) else: # Not an override. # Create a dynamically-typed method wrapper. res.append(self.dynamic_method_wrapper(fdef)) return res
def build_newtype_typeinfo(self, name: str, old_type: Type, base_type: Instance) -> TypeInfo: info = self.api.basic_new_typeinfo(name, base_type) info.is_newtype = True # Add __init__ method args = [Argument(Var('self'), NoneTyp(), None, ARG_POS), self.make_argument('item', old_type)] signature = CallableType( arg_types=[Instance(info, []), old_type], arg_kinds=[arg.kind for arg in args], arg_names=['self', 'item'], ret_type=NoneTyp(), fallback=self.api.named_type('__builtins__.function'), name=name) init_func = FuncDef('__init__', args, Block([]), typ=signature) init_func.info = info init_func._fullname = self.api.qualified_name(name) + '.__init__' info.names['__init__'] = SymbolTableNode(MDEF, init_func) return info
def visit_func_def(self, node: FuncDef) -> FuncDef: # Note that a FuncDef must be transformed to a FuncDef. new = FuncDef(node.name(), [self.visit_var(var) for var in node.args], node.arg_kinds[:], [None] * len(node.init), self.block(node.body), self.optional_type(node.type)) self.copy_function_attributes(new, node) new._fullname = node._fullname new.is_decorated = node.is_decorated new.is_conditional = node.is_conditional new.is_abstract = node.is_abstract new.is_static = node.is_static new.is_class = node.is_class new.is_property = node.is_property new.original_def = node.original_def return new
def visit_func_def(self, node: FuncDef) -> FuncDef: # Note that a FuncDef must be transformed to a FuncDef. # These contortions are needed to handle the case of recursive # references inside the function being transformed. # Set up placholder nodes for references within this function # to other functions defined inside it. # Don't create an entry for this function itself though, # since we want self-references to point to the original # function if this is the top-level node we are transforming. init = FuncMapInitializer(self) for stmt in node.body.body: stmt.accept(init) new = FuncDef(node.name(), [self.copy_argument(arg) for arg in node.arguments], self.block(node.body), cast(FunctionLike, self.optional_type(node.type))) self.copy_function_attributes(new, node) new._fullname = node._fullname new.is_decorated = node.is_decorated new.is_conditional = node.is_conditional new.is_abstract = node.is_abstract new.is_static = node.is_static new.is_class = node.is_class new.is_property = node.is_property new.original_def = node.original_def if node in self.func_placeholder_map: # There is a placeholder definition for this function. Replace # the attributes of the placeholder with those form the transformed # function. We know that the classes will be identical (otherwise # this wouldn't work). result = self.func_placeholder_map[node] result.__dict__ = new.__dict__ return result else: return new
def call_wrapper(self, fdef: FuncDef, is_dynamic: bool, is_wrapper_class: bool, target_ann: Callable, cur_ann: Callable, target_suffix: str, bound_sig: Callable) -> Node: """Return the body of wrapper method. The body contains only a call to the wrapped method and a return statement (if the call returns a value). Arguments are coerced to the target signature. """ args = self.call_args(fdef.args, target_ann, cur_ann, is_dynamic, is_wrapper_class, bound_sig, ismethod=fdef.is_method()) selfarg = args[0] args = args[1:] member = fdef.name() + target_suffix if not is_wrapper_class: callee = MemberExpr(selfarg, member) else: callee = MemberExpr( MemberExpr(self_expr(), self.tf.object_member_name()), member) call = CallExpr(callee, args, [nodes.ARG_POS] * len(args), [None] * len(args)) # type: Node if bound_sig: call = self.tf.coerce(call, bound_sig.ret_type, target_ann.ret_type, self.tf.type_context(), is_wrapper_class) call = self.tf.coerce(call, cur_ann.ret_type, bound_sig.ret_type, self.tf.type_context(), is_wrapper_class) else: call = self.tf.coerce(call, cur_ann.ret_type, target_ann.ret_type, self.tf.type_context(), is_wrapper_class) if not isinstance(target_ann.ret_type, Void): return ReturnStmt(call) else: return ExpressionStmt(call)
def get_callsites(self, func: FuncDef) -> Tuple[List[Callsite], List[str]]: """Find all call sites of a function.""" new_type = self.get_trivial_type(func) collector_plugin = SuggestionPlugin(func.fullname()) self.plugin._plugins.insert(0, collector_plugin) try: errors = self.try_type(func, new_type) finally: self.plugin._plugins.pop(0) return collector_plugin.mystery_hits, errors
def visit_func_def(self, fdef: FuncDef) -> int: if fdef.name().endswith("*"): # Wrapper functions are not supported yet. return -1 self.enter() for arg in fdef.args: self.add_local(arg) fdef.body.accept(self) self.add_implicit_return(cast(Callable, fdef.type)) if fdef.info: name = "%s.%s" % (fdef.info.name(), fdef.name()) else: name = fdef.name() self.generated[name] = FuncIcode(len(fdef.args), self.blocks, self.register_types) self.leave() return -1
def visit_FunctionDef(self, n: ast35.FunctionDef) -> Node: args = self.transform_args(n.args, n.lineno) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] arg_types = None # type: List[Type] if n.type_comment is not None: try: func_type_ast = ast35.parse(n.type_comment, '<func_type>', 'func_type') except SyntaxError: raise TypeCommentParseError(TYPE_COMMENT_SYNTAX_ERROR, n.lineno) assert isinstance(func_type_ast, ast35.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance(func_type_ast.argtypes[0], ast35.Ellipsis)): arg_types = [a.type_annotation if a.type_annotation is not None else AnyType() for a in args] else: arg_types = [a if a is not None else AnyType() for a in TypeConverter(line=n.lineno).visit_list(func_type_ast.argtypes)] return_type = TypeConverter(line=n.lineno).visit(func_type_ast.returns) # add implicit self type if self.in_class() and len(arg_types) < len(args): arg_types.insert(0, AnyType()) else: arg_types = [a.type_annotation for a in args] return_type = TypeConverter(line=n.lineno).visit(n.returns) if isinstance(return_type, UnboundType): return_type.is_ret_type = True func_type = None if any(arg_types) or return_type: func_type = CallableType([a if a is not None else AnyType() for a in arg_types], arg_kinds, arg_names, return_type if return_type is not None else AnyType(), None) func_def = FuncDef(n.name, args, self.as_block(n.body, n.lineno), func_type) if func_type is not None: func_type.definition = func_def func_type.line = n.lineno if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(n.lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) return Decorator(func_def, self.visit_list(n.decorator_list), var) else: return func_def
def visit_func_def(self, o: FuncDef) -> None: if self.is_private_name(o.name()): return if self.is_not_in_all(o.name()): return if self.is_recorded_name(o.name()): return if not self._indent and self._state not in (EMPTY, FUNC): self.add('\n') if not self.is_top_level(): self_inits = find_self_initializers(o) for init, value in self_inits: init_code = self.get_init(init, value) if init_code: self.add(init_code) self.add("%sdef %s(" % (self._indent, o.name())) self.record_name(o.name()) args = [] # type: List[str] for i, arg_ in enumerate(o.arguments): var = arg_.variable kind = arg_.kind name = var.name() annotated_type = o.type.arg_types[i] if isinstance(o.type, CallableType) else None if annotated_type and not ( i == 0 and name == 'self' and isinstance(annotated_type, AnyType)): annotation = ": {}".format(self.print_annotation(annotated_type)) else: annotation = "" if arg_.initializer: initializer = '...' if kind in (ARG_NAMED, ARG_NAMED_OPT) and not any(arg.startswith('*') for arg in args): args.append('*') if not annotation: typename = self.get_str_type_of_node(arg_.initializer, True) annotation = ': {} = ...'.format(typename) else: annotation += '={}'.format(initializer) arg = name + annotation elif kind == ARG_STAR: arg = '*%s%s' % (name, annotation) elif kind == ARG_STAR2: arg = '**%s%s' % (name, annotation) else: arg = name + annotation args.append(arg) retname = None if isinstance(o.type, CallableType): retname = self.print_annotation(o.type.ret_type) elif o.name() == '__init__' or not has_return_statement(o): retname = 'None' retfield = '' if retname is not None: retfield = ' -> ' + retname self.add(', '.join(args)) self.add("){}: ...\n".format(retfield)) self._state = FUNC
def visit_func_def(self, func: FuncDef, decorated: bool = False) -> None: """Process a func def. decorated is true if we are processing a func def in a Decorator that needs a _fullname and to have its body analyzed but does not need to be added to the symbol table. """ sem = self.sem if sem.type is not None: # Don't process methods during pass 1. return func.is_conditional = sem.block_depth[-1] > 0 func._fullname = sem.qualified_name(func.name()) at_module = sem.is_module_scope() and not decorated if (at_module and func.name() == '__getattr__' and self.sem.cur_mod_node.is_package_init_file() and self.sem.cur_mod_node.is_stub): if isinstance(func.type, CallableType): ret = func.type.ret_type if isinstance(ret, UnboundType) and not ret.args: sym = self.sem.lookup_qualified(ret.name, func, suppress_errors=True) # We only interpret a package as partial if the __getattr__ return type # is either types.ModuleType of Any. if sym and sym.node and sym.node.fullname() in ('types.ModuleType', 'typing.Any'): self.sem.cur_mod_node.is_partial_stub_package = True if at_module and func.name() in sem.globals: # Already defined in this module. original_sym = sem.globals[func.name()] if (original_sym.kind == UNBOUND_IMPORTED or isinstance(original_sym.node, ImportedName)): # Ah this is an imported name. We can't resolve them now, so we'll postpone # this until the main phase of semantic analysis. return if not sem.set_original_def(original_sym.node, func): # Report error. sem.check_no_global(func.name(), func) else: if at_module: sem.globals[func.name()] = SymbolTableNode(GDEF, func) # Also analyze the function body (needed in case there are unreachable # conditional imports). sem.function_stack.append(func) sem.scope.enter_function(func) sem.enter() func.body.accept(self) sem.leave() sem.scope.leave() sem.function_stack.pop()
def visit_func_def(self, node: FuncDef) -> None: old_global_scope = self.is_global_scope self.is_global_scope = False super().visit_func_def(node) self.is_global_scope = old_global_scope file_node = self.cur_mod_node if (self.is_global_scope and file_node.is_stub and node.name() == '__getattr__' and file_node.is_package_init_file()): # __init__.pyi with __getattr__ means that any submodules are assumed # to exist, even if there is no stub. Note that we can't verify that the # return type is compatible, since we haven't bound types yet. file_node.is_partial_stub_package = True
def visit_func_def(self, o: FuncDef) -> None: self.line = o.line if len(o.expanded) > 1 and o.expanded != [o] * len(o.expanded): if o in o.expanded: print('{}:{}: ERROR: cycle in function expansion; skipping'.format(self.filename, o.get_line())) return for defn in o.expanded: self.visit_func_def(cast(FuncDef, defn)) else: if o.type: sig = cast(CallableType, o.type) arg_types = sig.arg_types if (sig.arg_names and sig.arg_names[0] == 'self' and not self.inferred): arg_types = arg_types[1:] for arg in arg_types: self.type(arg) self.type(sig.ret_type) elif self.all_nodes: self.record_line(self.line, TYPE_ANY) if not o.is_dynamic() or self.visit_untyped_defs: super().visit_func_def(o)
def do_func_def(self, n: Union[ast35.FunctionDef, ast35.AsyncFunctionDef], is_coroutine: bool = False) -> Union[FuncDef, Decorator]: """Helper shared between visit_FunctionDef and visit_AsyncFunctionDef.""" args = self.transform_args(n.args, n.lineno) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] # type: List[Optional[str]] arg_names = [ None if argument_elide_name(name) else name for name in arg_names ] if special_function_elide_names(n.name): arg_names = [None] * len(arg_names) arg_types = None # type: List[Type] if n.type_comment is not None: try: func_type_ast = ast35.parse(n.type_comment, '<func_type>', 'func_type') except SyntaxError: raise TypeCommentParseError(TYPE_COMMENT_SYNTAX_ERROR, n.lineno, n.col_offset) assert isinstance(func_type_ast, ast35.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance(func_type_ast.argtypes[0], ast35.Ellipsis)): arg_types = [ a.type_annotation if a.type_annotation is not None else AnyType() for a in args ] else: translated_args = (TypeConverter( line=n.lineno).translate_expr_list(func_type_ast.argtypes)) arg_types = [ a if a is not None else AnyType() for a in translated_args ] return_type = TypeConverter(line=n.lineno).visit( func_type_ast.returns) # add implicit self type if self.in_class() and len(arg_types) < len(args): arg_types.insert(0, AnyType()) else: arg_types = [a.type_annotation for a in args] return_type = TypeConverter(line=n.lineno).visit(n.returns) for arg, arg_type in zip(args, arg_types): self.set_type_optional(arg_type, arg.initializer) if isinstance(return_type, UnboundType): return_type.is_ret_type = True func_type = None if any(arg_types) or return_type: if len(arg_types) > len(arg_kinds): raise FastParserError('Type signature has too many arguments', n.lineno, offset=0) if len(arg_types) < len(arg_kinds): raise FastParserError('Type signature has too few arguments', n.lineno, offset=0) func_type = CallableType( [ a if a is not None else AnyType(implicit=True) for a in arg_types ], arg_kinds, arg_names, return_type if return_type is not None else AnyType( implicit=True), None) func_def = FuncDef(n.name, args, self.as_block(n.body, n.lineno), func_type) if is_coroutine: # A coroutine is also a generator, mostly for internal reasons. func_def.is_generator = func_def.is_coroutine = True if func_type is not None: func_type.definition = func_def func_type.line = n.lineno if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(n.lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) return Decorator(func_def, self.translate_expr_list(n.decorator_list), var) else: return func_def
def visit_func_def(self, node: FuncDef) -> None: if node not in self.transformer.func_placeholder_map: # Haven't seen this FuncDef before, so create a placeholder node. self.transformer.func_placeholder_map[node] = FuncDef( node.name(), node.arguments, node.body, None) super().visit_func_def(node)
def do_func_def(self, n: Union[ast3.FunctionDef, ast3.AsyncFunctionDef], is_coroutine: bool = False) -> Union[FuncDef, Decorator]: """Helper shared between visit_FunctionDef and visit_AsyncFunctionDef.""" no_type_check = bool( n.decorator_list and any(is_no_type_check_decorator(d) for d in n.decorator_list)) args = self.transform_args(n.args, n.lineno, no_type_check=no_type_check) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] # type: List[Optional[str]] arg_names = [ None if argument_elide_name(name) else name for name in arg_names ] if special_function_elide_names(n.name): arg_names = [None] * len(arg_names) arg_types = [] # type: List[Optional[Type]] if no_type_check: arg_types = [None] * len(args) return_type = None elif n.type_comment is not None: try: func_type_ast = ast3.parse(n.type_comment, '<func_type>', 'func_type') assert isinstance(func_type_ast, ast3.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance( func_type_ast.argtypes[0], ast3.Ellipsis)): if n.returns: # PEP 484 disallows both type annotations and type comments self.fail(errorcode.DUPLICATE_TYPE_SIGNATURES(), n.lineno, n.col_offset) arg_types = [ a.type_annotation if a.type_annotation is not None else AnyType(TypeOfAny.unannotated) for a in args ] else: # PEP 484 disallows both type annotations and type comments if n.returns or any(a.type_annotation is not None for a in args): self.fail(errorcode.DUPLICATE_TYPE_SIGNATURES(), n.lineno, n.col_offset) translated_args = (TypeConverter( self.errors, line=n.lineno).translate_expr_list( func_type_ast.argtypes)) arg_types = [ a if a is not None else AnyType(TypeOfAny.unannotated) for a in translated_args ] return_type = TypeConverter(self.errors, line=n.lineno).visit( func_type_ast.returns) # add implicit self type if self.in_class() and len(arg_types) < len(args): arg_types.insert(0, AnyType(TypeOfAny.special_form)) except SyntaxError: self.fail(errorcode.SYNTAX_ERROR_TYPE_COMMENT(), n.lineno, n.col_offset) if n.type_comment and n.type_comment[0] != "(": self.note( errorcode.MSG_SHOW( 'Suggestion: wrap argument types in parentheses'), n.lineno, n.col_offset) arg_types = [AnyType(TypeOfAny.from_error)] * len(args) return_type = AnyType(TypeOfAny.from_error) else: arg_types = [a.type_annotation for a in args] return_type = TypeConverter( self.errors, line=n.returns.lineno if n.returns else n.lineno).visit( n.returns) for arg, arg_type in zip(args, arg_types): self.set_type_optional(arg_type, arg.initializer) func_type = None if any(arg_types) or return_type: if len(arg_types) != 1 and any( isinstance(t, EllipsisType) for t in arg_types): self.fail( errorcode. ELLIPSES_CANNOT_ACC_OTHER_ARG_TYPES_IN_FUNCTION_TYPE_SIGNATURE( ), n.lineno, 0) elif len(arg_types) > len(arg_kinds): self.fail(errorcode.TYPE_SIGNATURE_HAS_TOO_MANY_ARGUMENTS(), n.lineno, 0) elif len(arg_types) < len(arg_kinds): self.fail(errorcode.TYPE_SIGNATURE_HAS_TOO_FEW_ARGUMENTS(), n.lineno, 0) else: func_type = CallableType([ a if a is not None else AnyType(TypeOfAny.unannotated) for a in arg_types ], arg_kinds, arg_names, return_type if return_type is not None else AnyType(TypeOfAny.unannotated), _dummy_fallback) func_def = FuncDef(n.name, args, self.as_required_block(n.body, n.lineno), func_type) if is_coroutine: # A coroutine is also a generator, mostly for internal reasons. func_def.is_generator = func_def.is_coroutine = True if func_type is not None: func_type.definition = func_def func_type.line = n.lineno if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(n.lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) return Decorator(func_def, self.translate_expr_list(n.decorator_list), var) else: return func_def
def add_method_to_class( api: SemanticAnalyzerPluginInterface, cls: ClassDef, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarDef] = None, is_classmethod: bool = False, ) -> None: """ Adds a new method to a class definition. NOTE: Copied from mypy/plugins/common.py and extended with support for adding classmethods based on https://github.com/python/mypy/pull/7796 """ info = cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in the semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): cls.defs.body.remove(sym.node) self_type = self_type or fill_typevars(info) # Add either self or cls as the first argument if is_classmethod: first = Argument(Var("cls"), TypeType.make_normalized(self_type), None, ARG_POS) else: first = Argument(Var("self"), self_type, None, ARG_POS) args = [first] + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, "All arguments must be fully typed." arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name) arg_kinds.append(arg.kind) function_type = api.named_type("__builtins__.function") signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func._fullname = info.fullname + "." + name # pylint: disable=protected-access func.line = info.line func.is_class = is_classmethod # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] if is_classmethod: func.is_decorated = True v = Var(name, func.type) v.info = info v._fullname = func._fullname # pylint: disable=protected-access v.is_classmethod = True dec = Decorator(func, [NameExpr("classmethod")], v) dec.line = info.line sym = SymbolTableNode(MDEF, dec) else: sym = SymbolTableNode(MDEF, func) sym.plugin_generated = True info.names[name] = sym info.defn.defs.body.append(func)
def add_method( ctx: ClassDefContext, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarDef] = None, is_classmethod: bool = False, is_new: bool = False, # is_staticmethod: bool = False, ) -> None: """ Adds a new method to a class. This can be dropped if/when https://github.com/python/mypy/issues/7301 is merged """ info = ctx.cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in the semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): ctx.cls.defs.body.remove(sym.node) self_type = self_type or fill_typevars(info) if is_classmethod or is_new: first = [Argument(Var('_cls'), TypeType.make_normalized(self_type), None, ARG_POS)] # elif is_staticmethod: # first = [] else: self_type = self_type or fill_typevars(info) first = [Argument(Var('__pydantic_self__'), self_type, None, ARG_POS)] args = first + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, 'All arguments must be fully typed.' arg_types.append(arg.type_annotation) arg_names.append(get_name(arg.variable)) arg_kinds.append(arg.kind) function_type = ctx.api.named_type(f'{BUILTINS_NAME}.function') signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func.is_class = is_classmethod # func.is_static = is_staticmethod func._fullname = get_fullname(info) + '.' + name func.line = info.line # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] if is_classmethod: # or is_staticmethod: func.is_decorated = True v = Var(name, func.type) v.info = info v._fullname = func._fullname # if is_classmethod: v.is_classmethod = True dec = Decorator(func, [NameExpr('classmethod')], v) # else: # v.is_staticmethod = True # dec = Decorator(func, [NameExpr('staticmethod')], v) dec.line = info.line sym = SymbolTableNode(MDEF, dec) else: sym = SymbolTableNode(MDEF, func) sym.plugin_generated = True info.names[name] = sym info.defn.defs.body.append(func)
def visit_FunctionDef(self, n: ast27.FunctionDef) -> Statement: self.class_and_function_stack.append('F') lineno = n.lineno converter = TypeConverter(self.errors, line=lineno, assume_str_is_unicode=self.unicode_literals) args, decompose_stmts = self.transform_args(n.args, lineno) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] # type: List[Optional[str]] arg_names = [ None if argument_elide_name(name) else name for name in arg_names ] if special_function_elide_names(n.name): arg_names = [None] * len(arg_names) arg_types = [] # type: List[Optional[Type]] type_comment = n.type_comment if (n.decorator_list and any( is_no_type_check_decorator(d) for d in n.decorator_list)): arg_types = [None] * len(args) return_type = None elif type_comment is not None and len(type_comment) > 0: try: func_type_ast = ast3_parse(type_comment, '<func_type>', 'func_type') assert isinstance(func_type_ast, ast3.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance( func_type_ast.argtypes[0], ast3.Ellipsis)): arg_types = [ a.type_annotation if a.type_annotation is not None else AnyType(TypeOfAny.unannotated) for a in args ] else: # PEP 484 disallows both type annotations and type comments if any(a.type_annotation is not None for a in args): self.fail(message_registry.DUPLICATE_TYPE_SIGNATURES, lineno, n.col_offset) arg_types = [ a if a is not None else AnyType(TypeOfAny.unannotated) for a in converter.translate_expr_list( func_type_ast.argtypes) ] return_type = converter.visit(func_type_ast.returns) # add implicit self type if self.in_method_scope() and len(arg_types) < len(args): arg_types.insert(0, AnyType(TypeOfAny.special_form)) except SyntaxError: stripped_type = type_comment.split("#", 2)[0].strip() err_msg = "{} '{}'".format(TYPE_COMMENT_SYNTAX_ERROR, stripped_type) self.fail(err_msg, lineno, n.col_offset) arg_types = [AnyType(TypeOfAny.from_error)] * len(args) return_type = AnyType(TypeOfAny.from_error) else: arg_types = [a.type_annotation for a in args] return_type = converter.visit(None) for arg, arg_type in zip(args, arg_types): self.set_type_optional(arg_type, arg.initializer) func_type = None if any(arg_types) or return_type: if len(arg_types) != 1 and any( isinstance(t, EllipsisType) for t in arg_types): self.fail( "Ellipses cannot accompany other argument types " "in function type signature.", lineno, 0) elif len(arg_types) > len(arg_kinds): self.fail('Type signature has too many arguments', lineno, 0, blocker=False) elif len(arg_types) < len(arg_kinds): self.fail('Type signature has too few arguments', lineno, 0, blocker=False) else: any_type = AnyType(TypeOfAny.unannotated) func_type = CallableType( [a if a is not None else any_type for a in arg_types], arg_kinds, arg_names, return_type if return_type is not None else any_type, _dummy_fallback) body = self.as_required_block(n.body, lineno) if decompose_stmts: body.body = decompose_stmts + body.body func_def = FuncDef(n.name, args, body, func_type) if isinstance(func_def.type, CallableType): # semanal.py does some in-place modifications we want to avoid func_def.unanalyzed_type = func_def.type.copy_modified() if func_type is not None: func_type.definition = func_def func_type.line = lineno if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) dec = Decorator(func_def, self.translate_expr_list(n.decorator_list), var) dec.set_line(lineno, n.col_offset) retval = dec # type: Statement else: # Overrides set_line -- can't use self.set_line func_def.set_line(lineno, n.col_offset) retval = func_def self.class_and_function_stack.pop() return retval
def add_class_method( ctx: ClassDefContext, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarDef] = None, ) -> None: """Adds a new class method to a class. """ info = ctx.cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in the semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): ctx.cls.defs.body.remove(sym.node) self_type = self_type or fill_typevars(info) function_type = ctx.api.named_type('__builtins__.function') args = [ Argument(Var('_cls'), TypeType.make_normalized(self_type), None, ARG_POS) ] + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, 'All arguments must be fully typed.' arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name) arg_kinds.append(arg.kind) signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func.is_class = True func._fullname = info.fullname + '.' + name func.line = info.line # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] func.is_decorated = True v = Var(name, func.type) v.info = info v._fullname = func._fullname v.is_classmethod = True dec = Decorator(func, [NameExpr('classmethod')], v) dec.line = info.line sym = SymbolTableNode(MDEF, dec) sym.plugin_generated = True info.names[name] = sym info.defn.defs.body.append(func)
def get_return_types(typemap: Dict[Expression, Type], func: FuncDef) -> List[Type]: """Find all the types returned by return statements in func.""" finder = ReturnFinder(typemap) func.accept(finder) return finder.return_types
def deserialize_funcdefs(stmts): return [ FuncDef.deserialize(stmt) for stmt in stmts if stmt[".class"] == "FuncDef" ]
def visit_func_def(self, func: FuncDef) -> None: if self.current_info is not None: func.info = self.current_info if func.type is not None: func.type.accept(self.type_fixer)
def visit_FunctionDef(self, n: ast27.FunctionDef) -> Statement: converter = TypeConverter(self.errors, line=n.lineno) args, decompose_stmts = self.transform_args(n.args, n.lineno) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] # type: List[Optional[str]] arg_names = [ None if argument_elide_name(name) else name for name in arg_names ] if special_function_elide_names(n.name): arg_names = [None] * len(arg_names) arg_types = None # type: List[Type] if (n.decorator_list and any( is_no_type_check_decorator(d) for d in n.decorator_list)): arg_types = [None] * len(args) return_type = None elif n.type_comment is not None and len(n.type_comment) > 0: try: func_type_ast = ast3.parse(n.type_comment, '<func_type>', 'func_type') assert isinstance(func_type_ast, ast3.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance( func_type_ast.argtypes[0], ast3.Ellipsis)): arg_types = [ a.type_annotation if a.type_annotation is not None else AnyType() for a in args ] else: # PEP 484 disallows both type annotations and type comments if any(a.type_annotation is not None for a in args): self.fail(messages.DUPLICATE_TYPE_SIGNATURES, n.lineno, n.col_offset) arg_types = [ a if a is not None else AnyType() for a in converter.translate_expr_list(func_type_ast.argtypes) ] return_type = converter.visit(func_type_ast.returns) # add implicit self type if self.in_class() and len(arg_types) < len(args): arg_types.insert(0, AnyType()) except SyntaxError: self.fail(TYPE_COMMENT_SYNTAX_ERROR, n.lineno, n.col_offset) arg_types = [AnyType()] * len(args) return_type = AnyType() else: arg_types = [a.type_annotation for a in args] return_type = converter.visit(None) for arg, arg_type in zip(args, arg_types): self.set_type_optional(arg_type, arg.initializer) func_type = None if any(arg_types) or return_type: if len(arg_types) != 1 and any( isinstance(t, EllipsisType) for t in arg_types): self.fail( "Ellipses cannot accompany other argument types " "in function type signature.", n.lineno, 0) elif len(arg_types) > len(arg_kinds): self.fail('Type signature has too many arguments', n.lineno, 0) elif len(arg_types) < len(arg_kinds): self.fail('Type signature has too few arguments', n.lineno, 0) else: func_type = CallableType( [a if a is not None else AnyType() for a in arg_types], arg_kinds, arg_names, return_type if return_type is not None else AnyType(), None) body = self.as_block(n.body, n.lineno) if decompose_stmts: body.body = decompose_stmts + body.body func_def = FuncDef(n.name, args, body, func_type) if func_type is not None: func_type.definition = func_def func_type.line = n.lineno if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(n.lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) return Decorator(func_def, self.translate_expr_list(n.decorator_list), var) else: return func_def
def visit_func_def(self, node: FuncDef) -> None: node.expanded = [] node.type = node.unanalyzed_type with self.enter_class(node.info) if node.info else nothing(): super().visit_func_def(node)
'line': self.line, 'traceback_name': self.traceback_name, } @classmethod def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> 'FuncIR': return FuncIR( FuncDecl.deserialize(data['decl'], ctx), [], [], data['line'], data['traceback_name'], ) INVALID_FUNC_DEF: Final = FuncDef("<INVALID_FUNC_DEF>", [], Block([])) def all_values(args: List[Register], blocks: List[BasicBlock]) -> List[Value]: """Return the set of all values that may be initialized in the blocks. This omits registers that are only read. """ values: List[Value] = list(args) seen_registers = set(args) for block in blocks: for op in block.ops: if not isinstance(op, ControlOp): if isinstance(op, (Assign, AssignMulti)): if op.dest not in seen_registers:
def _add_bool_dunder(self, type_info: TypeInfo) -> None: signature = CallableType([], [], [], Instance(self.bool_type_info, []), self.function) bool_func = FuncDef('__bool__', [], Block([])) bool_func.type = set_callable_name(signature, bool_func) type_info.names[bool_func.name] = SymbolTableNode(MDEF, bool_func)
def _add_method( ctx: ClassDefContext, name: str, args: List[Argument], return_type: mypy.types.Type, self_type: Optional[mypy.types.Type] = None, tvar_def: Optional[mypy.types.TypeVarDef] = None, is_classmethod: bool = False, ) -> None: """Adds a new method to a class. """ info = ctx.cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in new semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): ctx.cls.defs.body.remove(sym.node) if is_classmethod: first = Argument( Var('cls'), # Working around python/mypy#5416. # This should be: mypy.types.TypeType.make_normalized(self_type) mypy.types.AnyType(mypy.types.TypeOfAny.implementation_artifact), None, ARG_POS) else: self_type = self_type or fill_typevars(info) first = Argument(Var('self'), self_type, None, ARG_POS) args = [first] + args function_type = ctx.api.named_type('__builtins__.function') arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, 'All arguments must be fully typed.' arg_types.append(arg.type_annotation) arg_names.append(get_name(arg.variable)) arg_kinds.append(arg.kind) signature = mypy.types.CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.is_class = is_classmethod func.type = set_callable_name(signature, func) func._fullname = get_fullname(info) + '.' + name func.line = info.line # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] info.defn.defs.body.append(func) info.names[name] = SymbolTableNode(MDEF, func, plugin_generated=True)
@property def ret_type(self) -> RType: return self.sig.ret_type def cname(self, names: NameGenerator) -> str: name = self.name if self.class_name: name += '_' + self.class_name return names.private_name(self.module_name, name) def __str__(self) -> str: return '\n'.join(format_func(self)) INVALID_FUNC_DEF = FuncDef('<INVALID_FUNC_DEF>', [], Block([])) # Some notes on the vtable layout: # Each concrete class has a vtable that contains function pointers for its # methods and for getters/setters of its attributes. So that subclasses # may be efficiently used when their parent class is expected, the layout # of child vtables must be an extension of their base class's vtable. # # This makes multiple inheritance tricky, since obviously we cannot be # an extension of multiple parent classes. We solve this by requriing # all but one parent to be "traits", which we can operate on in a # somewhat less efficient way. For each trait implemented by a class, # we generate a separate vtable for the methods in that trait. # We then store an array of (trait type, trait vtable) pointers alongside # a class's main vtable. When we want to call a trait method, we # (at runtime!) search the array of trait vtables to find the correct one,
'line': self.line, 'traceback_name': self.traceback_name, } @classmethod def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> 'FuncIR': return FuncIR( FuncDecl.deserialize(data['decl'], ctx), [], [], data['line'], data['traceback_name'], ) INVALID_FUNC_DEF = FuncDef('<INVALID_FUNC_DEF>', [], Block([])) # type: Final def all_values(args: List[Register], blocks: List[BasicBlock]) -> List[Value]: """Return the set of all values that may be initialized in the blocks. This omits registers that are only read. """ values = list(args) # type: List[Value] seen_registers = set(args) for block in blocks: for op in block.ops: if not isinstance(op, ControlOp): if isinstance(op, (Assign, AssignMulti)): if op.dest not in seen_registers:
def visit_FunctionDef(self, n: ast27.FunctionDef) -> Node: converter = TypeConverter(line=n.lineno) args = self.transform_args(n.args, n.lineno) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] arg_types = None # type: List[Type] if n.type_comment is not None and len(n.type_comment) > 0: try: func_type_ast = ast35.parse(n.type_comment, '<func_type>', 'func_type') except SyntaxError: raise TypeCommentParseError(TYPE_COMMENT_SYNTAX_ERROR, n.lineno) assert isinstance(func_type_ast, ast35.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance(func_type_ast.argtypes[0], ast35.Ellipsis)): arg_types = [ a.type_annotation if a.type_annotation is not None else AnyType() for a in args ] else: arg_types = [ a if a is not None else AnyType() for a in converter.visit_list(func_type_ast.argtypes) ] return_type = converter.visit(func_type_ast.returns) # add implicit self type if self.in_class() and len(arg_types) < len(args): arg_types.insert(0, AnyType()) else: arg_types = [a.type_annotation for a in args] return_type = converter.visit(None) for arg, arg_type in zip(args, arg_types): self.set_type_optional(arg_type, arg.initializer) if isinstance(return_type, UnboundType): return_type.is_ret_type = True func_type = None if any(arg_types) or return_type: func_type = CallableType( [a if a is not None else AnyType() for a in arg_types], arg_kinds, arg_names, return_type if return_type is not None else AnyType(), None) func_def = FuncDef(n.name, args, self.as_block(n.body, n.lineno), func_type) if func_type is not None: func_type.definition = func_def func_type.line = n.lineno if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(n.lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) return Decorator(func_def, self.visit_list(n.decorator_list), var) else: return func_def
def add_classmethod_to_class( api: SemanticAnalyzerPluginInterface, cls: ClassDef, name: str, args: List[Argument], return_type: Type, cls_type: Optional[Type] = None, tvar_def: Optional[Any] = None, ) -> None: """ Adds a new classmethod to a class definition. :param api: :param cls: :param name: :param args: :param return_type: :param cls_type: :param tvar_def: """ info = cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in the semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): cls.defs.body.remove(sym.node) cls_type = cls_type or fill_typevars(info) function_type = api.named_type(f"{_builtins}.function") args = [Argument(Var("cls"), cls_type, None, ARG_POS)] + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, "All arguments must be fully typed." arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name) arg_kinds.append(arg.kind) signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.is_class = True func.info = info func.type = set_callable_name(signature, func) func._fullname = info.fullname + '.' + name func.line = info.line # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] info.names[name] = SymbolTableNode(MDEF, func, plugin_generated=True) info.defn.defs.body.append(func)
def make_init_wrapper(self, tdef: ClassDef) -> List[Node]: """Make and return an implicit __init__ if class needs it. Otherwise, return an empty list. We include an implicit __init__ if the class is generic or if it extends a generic class and if it does not define __init__. The __init__ of a generic class requires one or more extra type variable arguments. The inherited __init__ may not accept these. For example, assume these definitions: . class A(Generic[T]): pass . class B(A[int]): pass The constructor for B will be (equivalent to) . def __init__(self: B) -> None: . self.__tv = <int> . super().__init__(<int>) """ # FIX overloading, default args / varargs, keyword args info = tdef.info if '__init__' not in info.names and ( tdef.is_generic() or (info.bases and info.mro[1].is_generic())): # Generic class with no explicit __init__ method # (i.e. __init__ inherited from superclass). Generate a # wrapper that initializes type variable slots and calls # the superclass __init__ method. base = info.mro[1] selftype = self_type(info) callee_type = cast(Callable, analyse_member_access( '__init__', selftype, None, False, True, None, None, base)) # Now the callee type may contain the type variables of a # grandparent as bound type variables, but we want the # type variables of the parent class. Explicitly set the # bound type variables. callee_type = self.fix_bound_init_tvars(callee_type, map_instance_to_supertype(selftype, base)) super_init = cast(FuncDef, base.get_method('__init__')) # Build argument list. args = [Var('self')] for i in range(1, len(super_init.args)): args.append(Var(super_init.args[i].name())) args[-1].type = callee_type.arg_types[i - 1] selft = self_type(self.tf.type_context()) callee_type = prepend_arg_type(callee_type, selft) creat = FuncDef('__init__', args, super_init.arg_kinds, [None] * len(args), Block([])) creat.info = tdef.info creat.type = callee_type creat.is_implicit = False tdef.info.names['__init__'] = SymbolTableNode(MDEF, creat, typ=creat.type) # Insert a call to superclass constructor. If the # superclass is object, the constructor does nothing => # omit the call. if base.fullname() != 'builtins.object': creat.body.body.append( self.make_superclass_constructor_call(tdef.info, callee_type)) # Implicit cast from FuncDef[] to Node[] is safe below. return Any(self.func_tf.transform_method(creat)) else: return []
def visit_func_def(self, o: FuncDef) -> None: if self.is_private_name(o.name()): return if self.is_not_in_all(o.name()): return if self.is_recorded_name(o.name()): return if not self._indent and self._state not in (EMPTY, FUNC): self.add('\n') if not self.is_top_level(): self_inits = find_self_initializers(o) for init, value in self_inits: init_code = self.get_init(init, value) if init_code: self.add(init_code) self.add("%sdef %s(" % (self._indent, o.name())) self.record_name(o.name()) args = [] # type: List[str] for i, arg_ in enumerate(o.arguments): var = arg_.variable kind = arg_.kind name = var.name() annotated_type = o.type.arg_types[i] if isinstance(o.type, CallableType) else None is_self_arg = i == 0 and name == 'self' is_cls_arg = i == 0 and name == 'cls' if (annotated_type is None and not arg_.initializer and not is_self_arg and not is_cls_arg): self.add_typing_import("Any") annotation = ": Any" elif annotated_type and not is_self_arg: annotation = ": {}".format(self.print_annotation(annotated_type)) else: annotation = "" if arg_.initializer: initializer = '...' if kind in (ARG_NAMED, ARG_NAMED_OPT) and not any(arg.startswith('*') for arg in args): args.append('*') if not annotation: typename = self.get_str_type_of_node(arg_.initializer, True) annotation = ': {} = ...'.format(typename) else: annotation += '={}'.format(initializer) arg = name + annotation elif kind == ARG_STAR: arg = '*%s%s' % (name, annotation) elif kind == ARG_STAR2: arg = '**%s%s' % (name, annotation) else: arg = name + annotation args.append(arg) retname = None if isinstance(o.type, CallableType): retname = self.print_annotation(o.type.ret_type) elif o.name() == '__init__' or not has_return_statement(o): retname = 'None' retfield = '' if retname is not None: retfield = ' -> ' + retname self.add(', '.join(args)) self.add("){}: ...\n".format(retfield)) self._state = FUNC
def do_func_def(self, n: Union[ast3.FunctionDef, ast3.AsyncFunctionDef], is_coroutine: bool = False) -> Union[FuncDef, Decorator]: """Helper shared between visit_FunctionDef and visit_AsyncFunctionDef.""" no_type_check = bool( n.decorator_list and any(is_no_type_check_decorator(d) for d in n.decorator_list)) args = self.transform_args(n.args, n.lineno, no_type_check=no_type_check) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] # type: List[Optional[str]] arg_names = [ None if argument_elide_name(name) else name for name in arg_names ] if special_function_elide_names(n.name): arg_names = [None] * len(arg_names) arg_types = None # type: List[Type] if no_type_check: arg_types = [None] * len(args) return_type = None elif n.type_comment is not None: try: func_type_ast = ast3.parse(n.type_comment, '<func_type>', 'func_type') assert isinstance(func_type_ast, ast3.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance( func_type_ast.argtypes[0], ast3.Ellipsis)): if n.returns: # PEP 484 disallows both type annotations and type comments self.fail(messages.DUPLICATE_TYPE_SIGNATURES, n.lineno, n.col_offset) arg_types = [ a.type_annotation if a.type_annotation is not None else AnyType(TypeOfAny.implicit) for a in args ] else: # PEP 484 disallows both type annotations and type comments if n.returns or any(a.type_annotation is not None for a in args): self.fail(messages.DUPLICATE_TYPE_SIGNATURES, n.lineno, n.col_offset) translated_args = (TypeConverter( self.errors, line=n.lineno).translate_expr_list( func_type_ast.argtypes)) arg_types = [ a if a is not None else AnyType(TypeOfAny.implicit) for a in translated_args ] return_type = TypeConverter(self.errors, line=n.lineno).visit( func_type_ast.returns) # add implicit self type if self.in_class() and len(arg_types) < len(args): arg_types.insert(0, AnyType(TypeOfAny.special_form)) except SyntaxError: self.fail(TYPE_COMMENT_SYNTAX_ERROR, n.lineno, n.col_offset) arg_types = [AnyType(TypeOfAny.from_error)] * len(args) return_type = AnyType(TypeOfAny.from_error) else: arg_types = [a.type_annotation for a in args] return_type = TypeConverter( self.errors, line=n.returns.lineno if n.returns else n.lineno).visit( n.returns) for arg, arg_type in zip(args, arg_types): self.set_type_optional(arg_type, arg.initializer) func_type = None if any(arg_types) or return_type: if len(arg_types) != 1 and any( isinstance(t, EllipsisType) for t in arg_types): self.fail( "Ellipses cannot accompany other argument types " "in function type signature.", n.lineno, 0) elif len(arg_types) > len(arg_kinds): self.fail('Type signature has too many arguments', n.lineno, 0) elif len(arg_types) < len(arg_kinds): self.fail('Type signature has too few arguments', n.lineno, 0) else: func_type = CallableType([ a if a is not None else AnyType(TypeOfAny.implicit) for a in arg_types ], arg_kinds, arg_names, return_type if return_type is not None else AnyType(TypeOfAny.implicit), None) func_def = FuncDef(n.name, args, self.as_block(n.body, n.lineno), func_type) if is_coroutine: # A coroutine is also a generator, mostly for internal reasons. func_def.is_generator = func_def.is_coroutine = True if func_type is not None: func_type.definition = func_def func_type.line = n.lineno if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(n.lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) return Decorator(func_def, self.translate_expr_list(n.decorator_list), var) else: return func_def
def visit_func_def(self, fdef: FuncDef) -> None: self.errors.push_function(fdef.name()) self.analyze(fdef.type, fdef) super().visit_func_def(fdef) self.errors.pop_function()