diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 25f75d3c474e7..7899aa12c2044 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -736,7 +736,14 @@ def analyze_class_attribute_access(itype: Instance, mx.not_ready_callback(name, mx.context) return AnyType(TypeOfAny.from_error) else: - return function_type(cast(FuncBase, node.node), mx.builtin_type('builtins.function')) + assert isinstance(node.node, FuncBase) + typ = function_type(node.node, mx.builtin_type('builtins.function')) + # Note: if we are accessing class method on class object, the cls argument is bound. + # Annotated and/or explicit class methods go through other code paths above, for + # unannotated implicit class methods we do this here. + if node.node.is_class: + typ = bind_self(typ, is_classmethod=True) + return typ def add_class_tvars(t: ProperType, itype: Instance, isuper: Optional[Instance], diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index addcc520f61a8..a208e9ef09e17 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6387,6 +6387,42 @@ class MidBase(Base): pass [file init_subclass/__init__.py] [builtins fixtures/object_with_init_subclass.pyi] +[case testInitSubclassUnannotated] +class A: + def __init_subclass__(cls, *args, **kwargs): + super().__init_subclass__(*args, **kwargs) + +class B(A): + pass + +reveal_type(A.__init_subclass__) # N: Revealed type is 'def (*args: Any, **kwargs: Any) -> Any' +[builtins fixtures/object_with_init_subclass.pyi] + +[case testInitSubclassUnannotatedMulti] +from typing import ClassVar, List, Type + +class A: + registered_classes: ClassVar[List[Type[A]]] = [] + def __init_subclass__(cls, *args, register=True, **kwargs): + if register: + cls.registered_classes.append(cls) + super().__init_subclass__(*args, **kwargs) + +class B(A): ... +class C(A, register=False): ... +class D(C): ... +[builtins fixtures/object_with_init_subclass.pyi] + +[case testClassMethodUnannotated] +class C: + def __new__(cls): ... + @classmethod + def meth(cls): ... + +reveal_type(C.meth) # N: Revealed type is 'def () -> Any' +reveal_type(C.__new__) # N: Revealed type is 'def (cls: Type[__main__.C]) -> Any' +[builtins fixtures/classmethod.pyi] + [case testOverrideGenericSelfClassMethod] from typing import Generic, TypeVar, Type, List diff --git a/test-data/unit/fixtures/object_with_init_subclass.pyi b/test-data/unit/fixtures/object_with_init_subclass.pyi index 407e387bc8c05..8f2f5b9f7ee83 100644 --- a/test-data/unit/fixtures/object_with_init_subclass.pyi +++ b/test-data/unit/fixtures/object_with_init_subclass.pyi @@ -1,4 +1,4 @@ -from typing import Sequence, Iterator, TypeVar, Mapping, Iterable, Optional, Union, overload, Tuple, Generic +from typing import Sequence, Iterator, TypeVar, Mapping, Iterable, Optional, Union, overload, Tuple, Generic, List class object: def __init__(self) -> None: ... @@ -34,6 +34,15 @@ class tuple(Generic[T]): pass class function: pass class ellipsis: pass +# copy-pasted from list.pyi +class list(Sequence[T]): + def __iter__(self) -> Iterator[T]: pass + def __mul__(self, x: int) -> list[T]: pass + def __setitem__(self, x: int, v: T) -> None: pass + def __getitem__(self, x: int) -> T: pass + def __add__(self, x: List[T]) -> T: pass + def __contains__(self, item: object) -> bool: pass + # copy-pasted from dict.pyi class dict(Mapping[KT, VT]): @overload