diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index a511e714ac6b4..731cc2c3533d1 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -602,6 +602,13 @@ def collect_attributes(self) -> list[DataclassAttribute] | None: assert isinstance(node, Var), node + if node is not lhs.node: + # We create new Vars in _propertize_callables(), so it is OK. + if not isinstance(get_proper_type(node.type), CallableType): + # There is an invalid redefinition. Avoid temptation to guess, + # and skip both, see testDataclassVariableBadRedefine. + continue + # x: ClassVar[int] is ignored by dataclasses. if node.is_classvar: continue diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index f43ac255373e6..4fc2521941d29 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -670,6 +670,86 @@ reveal_type(x) # N: Revealed type is "typing._SpecialForm" [builtins fixtures/dataclasses.pyi] [typing fixtures/typing-medium.pyi] +[case testFunctionAliasDataclassNoCrash] +from dataclasses import dataclass + +def fn(a: int) -> int: + return a + +@dataclass +class Test: + foo = fn + foo: int = 42 # E: Name "foo" already defined on line 8 + +reveal_type(Test.foo) # N: Revealed type is "def (a: builtins.int) -> builtins.int" +[builtins fixtures/dataclasses.pyi] +[typing fixtures/typing-medium.pyi] + +[case testDataclassVariableBadRedefine] +from dataclasses import dataclass + +@dataclass +class Test: + foo = "test" + foo: int = 42 # E: Name "foo" already defined on line 5 + +reveal_type(Test) # N: Revealed type is "def () -> __main__.Test" +reveal_type(Test.foo) # N: Revealed type is "builtins.str" +[builtins fixtures/dataclasses.pyi] +[typing fixtures/typing-medium.pyi] + +[case testDataclassCallableAttrRegular] +from dataclasses import dataclass +from typing import Callable + +@dataclass +class Defer(Parent): + b: int + +@dataclass +class Parent: + key: Callable[[], str] + a: int + +reveal_type(Parent) # N: Revealed type is "def (key: def () -> builtins.str, a: builtins.int) -> __main__.Parent" +[builtins fixtures/dataclasses.pyi] +[typing fixtures/typing-medium.pyi] + +[case testDataclassCallableAttrDefault] +from dataclasses import dataclass +from typing import Callable + +@dataclass +class Defer(Parent): + b: int = 0 + +@dataclass +class Parent: + key: Callable[[], str] = lambda: "yes" + a: int = 0 + +reveal_type(Parent) # N: Revealed type is "def (key: def () -> builtins.str =, a: builtins.int =) -> __main__.Parent" +reveal_type(Parent.key) # N: Revealed type is "def () -> builtins.str" +[builtins fixtures/dataclasses.pyi] +[typing fixtures/typing-medium.pyi] + +[case testDataclassCallableAttrFrozen] +from dataclasses import dataclass +from typing import Callable + +@dataclass(frozen=True) +class Defer(Parent): + b: int + +@dataclass(frozen=True) +class Parent: + key: Callable[[], str] + a: int + +reveal_type(Parent) # N: Revealed type is "def (key: def () -> builtins.str, a: builtins.int) -> __main__.Parent" +[builtins fixtures/dataclasses.pyi] +[typing fixtures/typing-medium.pyi] + [case testDataclassOrdering] from dataclasses import dataclass @@ -1702,6 +1782,34 @@ A(a=func).a() A(a=func).a = func # E: Property "a" defined in "A" is read-only [builtins fixtures/dataclasses.pyi] +[case testDataclassesCallableFrozenOverride] +from dataclasses import dataclass +from typing import Callable +@dataclass(frozen=True) +class A: + a: Callable[[int], str] = lambda x: "yes" + +def foo(x: int) -> str: + return "no" + +@dataclass(frozen=True) +class Sub(A): + # This is no-op for attribute access on instances, + # so we effectively check the subtyping as if accessed on class. + a = foo +[builtins fixtures/dataclasses.pyi] + +[case testDataclassesCallableFrozenSelf] +from dataclasses import dataclass +from typing import Self, Callable +@dataclass(frozen=True) +class A: + a: Callable[[Self], str] + +a: A +reveal_type(a.a) # N: Revealed type is "def (__main__.A) -> builtins.str" +[builtins fixtures/dataclasses.pyi] + [case testDataclassInFunctionDoesNotCrash] from dataclasses import dataclass