diff --git a/mypy/checker.py b/mypy/checker.py index be365dcdb2aa..7faefa84307b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -34,7 +34,7 @@ Type, AnyType, CallableType, FunctionLike, Overloaded, TupleType, TypedDictType, Instance, NoneType, strip_type, TypeType, TypeOfAny, UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType, TypeVarDef, - function_type, is_named_instance, union_items, TypeQuery, LiteralType, + is_named_instance, union_items, TypeQuery, LiteralType, is_optional, remove_optional, TypeTranslator, StarType, get_proper_type, ProperType, get_proper_types, is_literal_type ) @@ -50,7 +50,7 @@ from mypy.typeops import ( map_type_from_supertype, bind_self, erase_to_bound, make_simplified_union, erase_def_to_union_or_bound, erase_to_union_or_bound, - true_only, false_only, + true_only, false_only, function_type, ) from mypy import message_registry from mypy.subtypes import ( @@ -535,6 +535,11 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: else: impl = impl_type + # Prevent extra noise from inconsistent use of @classmethod by copying + # the first arg from the method being checked against. + if sig1.arg_types and defn.info: + impl = impl.copy_modified(arg_types=[sig1.arg_types[0]] + impl.arg_types[1:]) + # Is the overload alternative's arguments subtypes of the implementation's? if not is_callable_compatible(impl, sig1, is_compat=is_subtype_no_promote, @@ -3934,7 +3939,14 @@ def enter_partial_types(self, *, is_function: bool = False, self.partial_types.append(PartialTypeScope({}, is_function, is_local)) yield - permissive = (self.options.allow_untyped_globals and not is_local) + # Don't complain about not being able to infer partials if it is + # at the toplevel (with allow_untyped_globals) or if it is in an + # untyped function being checked with check_untyped_defs. + permissive = (self.options.allow_untyped_globals and not is_local) or ( + self.options.check_untyped_defs + and self.dynamic_funcs + and self.dynamic_funcs[-1] + ) partial_types, _, _ = self.partial_types.pop() if not self.current_node_deferred: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index b4ae6acd5d2a..21e6b89f1803 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -17,7 +17,7 @@ Type, AnyType, CallableType, Overloaded, NoneType, TypeVarDef, TupleType, TypedDictType, Instance, TypeVarType, ErasedType, UnionType, PartialType, DeletedType, UninhabitedType, TypeType, TypeOfAny, LiteralType, LiteralValue, - is_named_instance, function_type, callable_type, FunctionLike, + is_named_instance, FunctionLike, StarType, is_optional, remove_optional, is_generic_instance, get_proper_type, ProperType, get_proper_types ) @@ -59,6 +59,7 @@ from mypy.plugin import Plugin, MethodContext, MethodSigContext, FunctionContext from mypy.typeops import ( tuple_fallback, make_simplified_union, true_only, false_only, erase_to_union_or_bound, + function_type, callable_type, ) import mypy.errorcodes as codes diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 2f8b57f11d6c..25f75d3c474e 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -6,7 +6,7 @@ from mypy.types import ( Type, Instance, AnyType, TupleType, TypedDictType, CallableType, FunctionLike, TypeVarDef, Overloaded, TypeVarType, UnionType, PartialType, TypeOfAny, LiteralType, - DeletedType, NoneType, TypeType, function_type, get_type_vars, get_proper_type, ProperType + DeletedType, NoneType, TypeType, get_type_vars, get_proper_type, ProperType ) from mypy.nodes import ( TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context, MypyFile, TypeVarExpr, @@ -24,7 +24,7 @@ from mypy import meet from mypy.typeops import ( tuple_fallback, bind_self, erase_to_bound, class_callable, type_object_type_from_function, - make_simplified_union, + make_simplified_union, function_type, ) if TYPE_CHECKING: # import for forward declaration only diff --git a/mypy/semanal.py b/mypy/semanal.py index 63077f14e849..790c0c6d96d7 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -84,11 +84,12 @@ from mypy.errorcodes import ErrorCode from mypy import message_registry, errorcodes as codes from mypy.types import ( - FunctionLike, UnboundType, TypeVarDef, TupleType, UnionType, StarType, function_type, + FunctionLike, UnboundType, TypeVarDef, TupleType, UnionType, StarType, CallableType, Overloaded, Instance, Type, AnyType, LiteralType, LiteralValue, TypeTranslator, TypeOfAny, TypeType, NoneType, PlaceholderType, TPDICT_NAMES, ProperType, get_proper_type, get_proper_types ) +from mypy.typeops import function_type from mypy.type_visitor import TypeQuery from mypy.nodes import implicit_module_attrs from mypy.typeanal import ( @@ -615,11 +616,11 @@ def prepare_method_signature(self, func: FuncDef, info: TypeInfo) -> None: # Only non-static methods are special. functype = func.type if not func.is_static: + if func.name() == '__init_subclass__': + func.is_class = True if not func.arguments: self.fail('Method must have at least one argument', func) elif isinstance(functype, CallableType): - if func.name() == '__init_subclass__': - func.is_class = True self_type = get_proper_type(functype.arg_types[0]) if isinstance(self_type, AnyType): leading_type = fill_typevars(info) # type: Type diff --git a/mypy/semanal_infer.py b/mypy/semanal_infer.py index 8c7d2926cce8..622ba275da3e 100644 --- a/mypy/semanal_infer.py +++ b/mypy/semanal_infer.py @@ -4,8 +4,9 @@ from mypy.nodes import Expression, Decorator, CallExpr, FuncDef, RefExpr, Var, ARG_POS from mypy.types import ( - Type, CallableType, AnyType, TypeOfAny, TypeVarType, function_type, ProperType, get_proper_type + Type, CallableType, AnyType, TypeOfAny, TypeVarType, ProperType, get_proper_type ) +from mypy.typeops import function_type from mypy.typevars import has_no_typevars from mypy.semanal_shared import SemanticAnalyzerInterface diff --git a/mypy/subtypes.py b/mypy/subtypes.py index a1ae2ec5946d..9f4c001264e1 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -4,7 +4,7 @@ from typing_extensions import Final from mypy.types import ( - Type, AnyType, UnboundType, TypeVisitor, FormalArgument, NoneType, function_type, + Type, AnyType, UnboundType, TypeVisitor, FormalArgument, NoneType, Instance, TypeVarType, CallableType, TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType, DeletedType, UninhabitedType, TypeType, is_named_instance, FunctionLike, TypeOfAny, LiteralType, ProperType, get_proper_type @@ -616,8 +616,8 @@ def find_node_type(node: Union[Var, FuncBase], itype: Instance, subtype: Type) - from mypy.typeops import bind_self if isinstance(node, FuncBase): - typ = function_type(node, - fallback=Instance(itype.type.mro[-1], [])) # type: Optional[Type] + typ = mypy.typeops.function_type( + node, fallback=Instance(itype.type.mro[-1], [])) # type: Optional[Type] else: typ = node.type typ = get_proper_type(typ) diff --git a/mypy/typeops.py b/mypy/typeops.py index 91548fb77608..715eacd61234 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -10,13 +10,14 @@ from mypy.types import ( TupleType, Instance, FunctionLike, Type, CallableType, TypeVarDef, Overloaded, TypeVarType, TypeType, UninhabitedType, FormalArgument, UnionType, NoneType, - ProperType, get_proper_type, get_proper_types, copy_type + AnyType, TypeOfAny, TypeType, ProperType, get_proper_type, get_proper_types, copy_type ) from mypy.nodes import ( - TypeInfo, TypeVar, ARG_STAR, + FuncBase, FuncItem, OverloadedFuncDef, TypeInfo, TypeVar, ARG_STAR, ARG_STAR2, ) from mypy.maptype import map_instance_to_supertype from mypy.expandtype import expand_type_by_instance, expand_type +from mypy.sharedparse import argument_elide_name from mypy.typevars import fill_typevars @@ -369,3 +370,50 @@ def erase_to_union_or_bound(typ: TypeVarType) -> ProperType: return make_simplified_union(typ.values) else: return get_proper_type(typ.upper_bound) + + +def function_type(func: FuncBase, fallback: Instance) -> FunctionLike: + if func.type: + assert isinstance(func.type, FunctionLike) + return func.type + else: + # Implicit type signature with dynamic types. + if isinstance(func, FuncItem): + return callable_type(func, fallback) + else: + # Broken overloads can have self.type set to None. + # TODO: should we instead always set the type in semantic analyzer? + assert isinstance(func, OverloadedFuncDef) + any_type = AnyType(TypeOfAny.from_error) + dummy = CallableType([any_type, any_type], + [ARG_STAR, ARG_STAR2], + [None, None], any_type, + fallback, + line=func.line, is_ellipsis_args=True) + # Return an Overloaded, because some callers may expect that + # an OverloadedFuncDef has an Overloaded type. + return Overloaded([dummy]) + + +def callable_type(fdef: FuncItem, fallback: Instance, + ret_type: Optional[Type] = None) -> CallableType: + # TODO: somewhat unfortunate duplication with prepare_method_signature in semanal + if fdef.info and not fdef.is_static: + self_type = fill_typevars(fdef.info) # type: Type + if fdef.is_class or fdef.name() == '__new__': + self_type = TypeType.make_normalized(self_type) + args = [self_type] + [AnyType(TypeOfAny.unannotated)] * (len(fdef.arg_names)-1) + else: + args = [AnyType(TypeOfAny.unannotated)] * len(fdef.arg_names) + + return CallableType( + args, + fdef.arg_kinds, + [None if argument_elide_name(n) else n for n in fdef.arg_names], + ret_type or AnyType(TypeOfAny.unannotated), + fallback, + name=fdef.name(), + line=fdef.line, + column=fdef.column, + implicit=True, + ) diff --git a/mypy/types.py b/mypy/types.py index fb84152008b4..73f7a20aec12 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -17,7 +17,6 @@ INVARIANT, SymbolNode, ARG_POS, ARG_OPT, ARG_STAR, ARG_STAR2, ARG_NAMED, ARG_NAMED_OPT, FuncDef, ) -from mypy.sharedparse import argument_elide_name from mypy.util import IdMapper from mypy.bogus_type import Bogus @@ -2081,44 +2080,6 @@ def copy_type(t: TP) -> TP: return copy.copy(t) -def function_type(func: mypy.nodes.FuncBase, fallback: Instance) -> FunctionLike: - if func.type: - assert isinstance(func.type, FunctionLike) - return func.type - else: - # Implicit type signature with dynamic types. - if isinstance(func, mypy.nodes.FuncItem): - return callable_type(func, fallback) - else: - # Broken overloads can have self.type set to None. - # TODO: should we instead always set the type in semantic analyzer? - assert isinstance(func, mypy.nodes.OverloadedFuncDef) - any_type = AnyType(TypeOfAny.from_error) - dummy = CallableType([any_type, any_type], - [ARG_STAR, ARG_STAR2], - [None, None], any_type, - fallback, - line=func.line, is_ellipsis_args=True) - # Return an Overloaded, because some callers may expect that - # an OverloadedFuncDef has an Overloaded type. - return Overloaded([dummy]) - - -def callable_type(fdef: mypy.nodes.FuncItem, fallback: Instance, - ret_type: Optional[Type] = None) -> CallableType: - return CallableType( - [AnyType(TypeOfAny.unannotated)] * len(fdef.arg_names), - fdef.arg_kinds, - [None if argument_elide_name(n) else n for n in fdef.arg_names], - ret_type or AnyType(TypeOfAny.unannotated), - fallback, - name=fdef.name(), - line=fdef.line, - column=fdef.column, - implicit=True, - ) - - def replace_alias_tvars(tp: Type, vars: List[str], subs: List[Type], newline: int, newcolumn: int) -> ProperType: """Replace type variables in a generic type alias tp with substitutions subs diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 90968f75697e..321a78898dd6 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2610,7 +2610,7 @@ t = Test() t.crash = 'test' # E: "Test" has no attribute "crash" class A: - def __setattr__(self): ... # E: Invalid signature "def (self: Any) -> Any" for "__setattr__" + def __setattr__(self): ... # E: Invalid signature "def (self: __main__.A) -> Any" for "__setattr__" a = A() a.test = 4 # E: "A" has no attribute "test" @@ -6178,3 +6178,48 @@ class B(A): def meth(cls: Type[T]) -> List[T]: ... [builtins fixtures/isinstancelist.pyi] + +[case testCheckUntypedDefsSelf1] +# flags: --check-untyped-defs + +from typing import Generic, TypeVar +T = TypeVar('T') + +class Desc: + def __get__(self, x, y): + # type: (...) -> bool + pass + +class Foo: + y = Desc() + + def __init__(self): + self.x = 0 + + def foo(self): + reveal_type(self.x) # N: Revealed type is 'builtins.int' + reveal_type(self.y) # N: Revealed type is 'builtins.bool' + self.bar() + self.baz() # E: "Foo" has no attribute "baz" + + @classmethod + def bar(cls): + cls.baz() # E: "Type[Foo]" has no attribute "baz" + +class C(Generic[T]): + x: T + def meth(self): + self.x + 1 # E: Unsupported left operand type for + ("T") +[builtins fixtures/classmethod.pyi] + +[case testCheckUntypedDefsSelf2] +# flags: --check-untyped-defs + +class Foo: + def __init__(self): + self.x = None + self.y = [] + +reveal_type(Foo().x) # N: Revealed type is 'Union[Any, None]' +reveal_type(Foo().y) # N: Revealed type is 'builtins.list[Any]' +[builtins fixtures/list.pyi] diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index d56e35720149..cfd91c26307f 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -893,7 +893,7 @@ class A: def f(self): pass A.f [out] -MemberExpr(5) : def (self: Any) -> Any +MemberExpr(5) : def (self: A) -> Any [case testOverloadedUnboundMethod] ## MemberExpr @@ -921,7 +921,7 @@ class A: def f(self, *args): pass A.f [out] -MemberExpr(10) : Overload(def (self: Any) -> Any, def (self: Any, Any) -> Any) +MemberExpr(10) : Overload(def (self: A) -> Any, def (self: A, Any) -> Any) [case testUnboundMethodWithInheritance] ## MemberExpr @@ -986,7 +986,7 @@ class A(Generic[t]): def f(self, x): pass A.f(None, None) [out] -MemberExpr(7) : def (self: Any, x: Any) -> Any +MemberExpr(7) : def (self: A[t`1], x: Any) -> Any [case testGenericMethodOfGenericClass] ## MemberExpr