From 847c2dc7999c11bfa618847f95ddba596cbc8df9 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 18 Jul 2019 11:45:04 +0100 Subject: [PATCH 1/2] Fix the logic in active self-type calculation for current scope --- mypy/checker.py | 14 ++++++++++++-- test-data/unit/check-classes.test | 12 ++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index dc744c6d3467..7a131abb37be 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -877,8 +877,10 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str]) # Store argument types. for i in range(len(typ.arg_types)): arg_type = typ.arg_types[i] - - ref_type = self.scope.active_self_type() # type: Optional[Type] + with self.scope.push_function(defn): + # We temporary push the definition to get the self type as + # visible from *inside* of this function/method. + ref_type = self.scope.active_self_type() # type: Optional[Type] if (isinstance(defn, FuncDef) and ref_type is not None and i == 0 and not defn.is_static and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2]): @@ -4456,6 +4458,7 @@ def active_class(self) -> Optional[TypeInfo]: return None def enclosing_class(self) -> Optional[TypeInfo]: + """Is there a class *directly* enclosing this function?""" top = self.top_function() assert top, "This method must be called from inside a function" index = self.stack.index(top) @@ -4466,7 +4469,14 @@ def enclosing_class(self) -> Optional[TypeInfo]: return None def active_self_type(self) -> Optional[Union[Instance, TupleType]]: + """An instance or tuple type representing the current class. + + This returns None unless we are in class body or in a method. + In particular, inside a function nested in method this returns None. + """ info = self.active_class() + if not info and self.top_function(): + info = self.enclosing_class() if info: return fill_typevars(info) return None diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 9a113ace0df1..9b68ed3d186f 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6077,3 +6077,15 @@ TX = TypeVar('TX', bound='X') class X: def __new__(cls, x: TX) -> TX: # E: "__new__" must return a class instance (got "TX") pass + +[case testGenericOverride] +from typing import Generic, TypeVar, Any + +T = TypeVar('T') + +class B(Generic[T]): + x: T + +class C(B): + def __init__(self) -> None: + self.x: Any From f4f9020a7140c2d40dc5c1162e6bc6cbb7a7743f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 18 Jul 2019 15:16:09 +0100 Subject: [PATCH 2/2] Add more tests --- test-data/unit/check-classes.test | 53 +++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 9b68ed3d186f..bfe057e1de02 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6089,3 +6089,56 @@ class B(Generic[T]): class C(B): def __init__(self) -> None: self.x: Any + +[case testGenericOverridePreciseInvalid] +from typing import Generic, TypeVar, Any + +T = TypeVar('T') + +class B(Generic[T]): + x: T + +class C(B[str]): + def __init__(self) -> None: + self.x: int # E: Incompatible types in assignment (expression has type "int", base class "B" defined the type as "str") + +[case testGenericOverridePreciseValid] +from typing import Generic, TypeVar + +T = TypeVar('T') + +class B(Generic[T]): + x: T + +class C(B[float]): + def __init__(self) -> None: + self.x: int # We currently allow covariant overriding. + +[case testGenericOverrideGeneric] +from typing import Generic, TypeVar, List + +T = TypeVar('T') + +class B(Generic[T]): + x: T + +class C(B[T]): + def __init__(self) -> None: + self.x: List[T] # E: Incompatible types in assignment (expression has type "List[T]", base class "B" defined the type as "T") +[builtins fixtures/list.pyi] + +[case testGenericOverrideGenericChained] +from typing import Generic, TypeVar, Tuple + +T = TypeVar('T') +S = TypeVar('S') + +class A(Generic[T]): + x: T + +class B(A[Tuple[T, S]]): ... + +class C(B[int, T]): + def __init__(self) -> None: + # TODO: error message could be better. + self.x: Tuple[str, T] # E: Incompatible types in assignment (expression has type "Tuple[str, T]", base class "A" defined the type as "Tuple[int, T]")