Skip to content

Commit

Permalink
Fix types of inherited attributes in generic dataclasses (#12656)
Browse files Browse the repository at this point in the history
Fixes #12633.
  • Loading branch information
JukkaL committed Apr 25, 2022
1 parent 40bbfb5 commit cb6581a
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 5 deletions.
13 changes: 8 additions & 5 deletions mypy/plugins/dataclasses.py
Expand Up @@ -19,6 +19,7 @@
get_proper_type, AnyType, TypeOfAny,
)
from mypy.server.trigger import make_wildcard_trigger
from mypy.state import state

# The set of decorators that generate dataclasses.
dataclass_makers: Final = {
Expand Down Expand Up @@ -101,10 +102,8 @@ def deserialize(
def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None:
"""Expands type vars in the context of a subtype when an attribute is inherited
from a generic super type."""
if not isinstance(self.type, TypeVarType):
return

self.type = map_type_from_supertype(self.type, sub_type, self.info)
if self.type is not None:
self.type = map_type_from_supertype(self.type, sub_type, self.info)


class DataclassTransformer:
Expand Down Expand Up @@ -402,7 +401,11 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]:
name: str = data["name"]
if name not in known_attrs:
attr = DataclassAttribute.deserialize(info, data, ctx.api)
attr.expand_typevar_from_subtype(ctx.cls.info)
# TODO: We shouldn't be performing type operations during the main
# semantic analysis pass, since some TypeInfo attributes might
# still be in flux. This should be performed in a later phase.
with state.strict_optional_set(ctx.api.options.strict_optional):
attr.expand_typevar_from_subtype(ctx.cls.info)
known_attrs.add(name)
super_attrs.append(attr)
elif all_attrs:
Expand Down
51 changes: 51 additions & 0 deletions test-data/unit/check-dataclasses.test
Expand Up @@ -1568,3 +1568,54 @@ class B:
class Derived(A, B):
pass
[builtins fixtures/dataclasses.pyi]

[case testDataclassGenericInheritance2]
# flags: --python-version 3.7
from dataclasses import dataclass
from typing import Any, Callable, Generic, TypeVar, List

T = TypeVar("T")
S = TypeVar("S")

@dataclass
class Parent(Generic[T]):
f: Callable[[T], Any]

@dataclass
class Child(Parent[T]): ...

class A: ...
def func(obj: A) -> bool: ...

reveal_type(Child[A](func).f) # N: Revealed type is "def (__main__.A) -> Any"

@dataclass
class Parent2(Generic[T]):
a: List[T]

@dataclass
class Child2(Generic[T, S], Parent2[S]):
b: List[T]

reveal_type(Child2([A()], [1]).a) # N: Revealed type is "builtins.list[__main__.A]"
reveal_type(Child2[int, A]([A()], [1]).b) # N: Revealed type is "builtins.list[builtins.int]"
[builtins fixtures/dataclasses.pyi]

[case testDataclassInheritOptionalType]
# flags: --python-version 3.7 --strict-optional
from dataclasses import dataclass
from typing import Any, Callable, Generic, TypeVar, List, Optional

T = TypeVar("T")

@dataclass
class Parent(Generic[T]):
x: Optional[str]
@dataclass
class Child(Parent):
y: Optional[int]
Child(x=1, y=1) # E: Argument "x" to "Child" has incompatible type "int"; expected "Optional[str]"
Child(x='', y='') # E: Argument "y" to "Child" has incompatible type "str"; expected "Optional[int]"
Child(x='', y=1)
Child(x=None, y=None)
[builtins fixtures/dataclasses.pyi]

0 comments on commit cb6581a

Please sign in to comment.