From 9e9863a8f9afe94667d13329f2bfebed16a5c4ce Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 30 May 2022 12:09:44 +0100 Subject: [PATCH] Run dataclass plugin before checking type var bounds The plugin may add attributes that are needed to perform the bound check. Fixes #12876. --- mypy/semanal_main.py | 7 +++---- test-data/unit/check-dataclasses.test | 18 +++++++++++++++++ test-data/unit/fine-grained.test | 28 +++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index bb0af8edc46f..305d1a058d76 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -82,10 +82,10 @@ def semantic_analysis_for_scc(graph: 'Graph', scc: List[str], errors: Errors) -> # We use patch callbacks to fix up things when we expect relatively few # callbacks to be required. apply_semantic_analyzer_patches(patches) - # This pass might need fallbacks calculated above. - check_type_arguments(graph, scc, errors) # Run class decorator hooks (they requite complete MROs and no placeholders). apply_class_plugin_hooks(graph, scc, errors) + # This pass might need fallbacks calculated above and the results of hooks. + check_type_arguments(graph, scc, errors) calculate_class_properties(graph, scc, errors) check_blockers(graph, scc) # Clean-up builtins, so that TypeVar etc. are not accessible without importing. @@ -133,10 +133,9 @@ def semantic_analysis_for_targets( process_top_level_function(analyzer, state, state.id, n.node.fullname, n.node, n.active_typeinfo, patches) apply_semantic_analyzer_patches(patches) - + apply_class_plugin_hooks(graph, [state.id], state.manager.errors) check_type_arguments_in_targets(nodes, state, state.manager.errors) calculate_class_properties(graph, [state.id], state.manager.errors) - apply_class_plugin_hooks(graph, [state.id], state.manager.errors) def restore_saved_attrs(saved_attrs: SavedAttributes) -> None: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 972cc4d40a1e..fb1b4a1e8b46 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1772,3 +1772,21 @@ c = C() c2 = C(x=1) c.x # E: "C" has no attribute "x" [builtins fixtures/dataclasses.pyi] + +[case testDataclassCheckTypeVarBounds] +# flags: --python-version 3.7 +from dataclasses import dataclass +from typing import Protocol, Dict, TypeVar, Generic + +class DataclassProtocol(Protocol): + __dataclass_fields__: Dict + +T = TypeVar("T", bound=DataclassProtocol) + +@dataclass +class MyDataclass: + x: int = 1 + +class MyGeneric(Generic[T]): ... +class MyClass(MyGeneric[MyDataclass]): ... +[builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index fa6dc52262dd..c2bd67320f3f 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -9734,6 +9734,7 @@ class C: [out] == main:5: error: Unsupported left operand type for + ("str") + [case testNoneAttribute] from typing import Generic, TypeVar @@ -9759,3 +9760,30 @@ class ExampleClass(Generic[T]): self.example_attribute = None [out] == + +[case testDataclassCheckTypeVarBoundsInReprocess] +# flags: --python-version 3.7 +from dataclasses import dataclass +from typing import Protocol, Dict, TypeVar, Generic +from m import x + +class DataclassProtocol(Protocol): + __dataclass_fields__: Dict + +T = TypeVar("T", bound=DataclassProtocol) + +@dataclass +class MyDataclass: + x: int = 1 + +class MyGeneric(Generic[T]): ... +class MyClass(MyGeneric[MyDataclass]): ... + +[file m.py] +x: int +[file m.py.2] +x: str + +[builtins fixtures/dataclasses.pyi] +[out] +==