From 8d90d97081906e0af00d3019ef97abe50c6767d0 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 4 Nov 2022 16:42:41 +0000 Subject: [PATCH 01/14] Fix issues with type aliases and new style unions Fix aliases like this and other aliases involving new-style unions: ``` A = type[int] | str ``` Fixes #12392. Fixes #14158. --- mypy/checkexpr.py | 3 +++ mypy/nodes.py | 19 ++++++++++++++++--- mypy/semanal.py | 2 +- test-data/unit/pythoneval.test | 28 ++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 78ae412072f52..16fb261e6cb29 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2847,6 +2847,9 @@ def visit_ellipsis(self, e: EllipsisExpr) -> Type: def visit_op_expr(self, e: OpExpr) -> Type: """Type check a binary operator expression.""" + if e.analyzed: + # It's actually a type expression X | Y. + return self.accept(e.analyzed) if e.op == "and" or e.op == "or": return self.check_boolean_op(e, e) if e.op == "*" and isinstance(e.left, ListExpr): diff --git a/mypy/nodes.py b/mypy/nodes.py index ebf2f5cb271a4..4d5ea621f8fda 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1968,10 +1968,20 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: class OpExpr(Expression): - """Binary operation (other than . or [] or comparison operators, - which have specific nodes).""" + """Binary operation. - __slots__ = ("op", "left", "right", "method_type", "right_always", "right_unreachable") + The dot (.), [] and comparison operators have more specific nodes. + """ + + __slots__ = ( + "op", + "left", + "right", + "method_type", + "right_always", + "right_unreachable", + "analyzed", + ) __match_args__ = ("left", "op", "right") @@ -1984,6 +1994,8 @@ class OpExpr(Expression): right_always: bool # Per static analysis only: Is the right side unreachable? right_unreachable: bool + # Used for expressions that represent type X | Y in some contexts + analyzed: TypeAliasExpr | None def __init__(self, op: str, left: Expression, right: Expression) -> None: super().__init__() @@ -1993,6 +2005,7 @@ def __init__(self, op: str, left: Expression, right: Expression) -> None: self.method_type = None self.right_always = False self.right_unreachable = False + self.analyzed = None def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_op_expr(self) diff --git a/mypy/semanal.py b/mypy/semanal.py index a5ddcc70eed63..45e895e668b72 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3440,7 +3440,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: no_args=no_args, eager=eager, ) - if isinstance(s.rvalue, (IndexExpr, CallExpr)): # CallExpr is for `void = type(None)` + if isinstance(s.rvalue, (IndexExpr, CallExpr, OpExpr)): # CallExpr is for `void = type(None)` s.rvalue.analyzed = TypeAliasExpr(alias_node) s.rvalue.analyzed.line = s.line # we use the column from resulting target, to get better location for errors diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 3f669246bb4e5..466d7346ec9d4 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1682,8 +1682,19 @@ Opt4 = float | None A = Type[int] | str B: TypeAlias = Type[int] | str +C = type[int] | str + +D = type[int] | str +x: D +reveal_type(x) +E: TypeAlias = type[int] | str +y: E +reveal_type(y) +F = list[type[int] | str] [out] _testTypeAliasWithNewStyleUnion.py:5: note: Revealed type is "typing._SpecialForm" +_testTypeAliasWithNewStyleUnion.py:24: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnion.py:27: note: Revealed type is "Union[Type[builtins.int], builtins.str]" [case testTypeAliasWithNewStyleUnionInStub] # flags: --python-version 3.7 @@ -1735,3 +1746,20 @@ _testEnumNameWorkCorrectlyOn311.py:12: note: Revealed type is "Union[Literal[1]? _testEnumNameWorkCorrectlyOn311.py:13: note: Revealed type is "Literal['X']?" _testEnumNameWorkCorrectlyOn311.py:14: note: Revealed type is "builtins.int" _testEnumNameWorkCorrectlyOn311.py:15: note: Revealed type is "builtins.int" + +[case testTypeAliasNotSupportedWithNewStyleUnion] +# flags: --python-version 3.9 +from typing_extensions import TypeAlias +A = type[int] | str +B = str | type[int] +C = str | int +D: TypeAlias = str | int +[out] +_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Invalid type alias: expression is not a valid type +_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Value of type "Type[type]" is not indexable +_testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Invalid type alias: expression is not a valid type +_testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Value of type "Type[type]" is not indexable +_testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Invalid type alias: expression is not a valid type +_testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Unsupported left operand type for | ("Type[str]") +_testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Invalid type alias: expression is not a valid type +_testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Unsupported left operand type for | ("Type[str]") From acd93db0dc5af94d4a54f114f2c31376b24e8bcc Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 7 Nov 2022 13:36:17 +0000 Subject: [PATCH 02/14] Black --- mypy/semanal.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 45e895e668b72..d9602abdf8586 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3440,7 +3440,9 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: no_args=no_args, eager=eager, ) - if isinstance(s.rvalue, (IndexExpr, CallExpr, OpExpr)): # CallExpr is for `void = type(None)` + if isinstance( + s.rvalue, (IndexExpr, CallExpr, OpExpr) + ): # CallExpr is for `void = type(None)` s.rvalue.analyzed = TypeAliasExpr(alias_node) s.rvalue.analyzed.line = s.line # we use the column from resulting target, to get better location for errors From 51660b0ae44aedf665226635eacc8ed02115defc Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 7 Nov 2022 17:28:04 +0000 Subject: [PATCH 03/14] Fix test case --- test-data/unit/pythoneval.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 466d7346ec9d4..b874055e7a66b 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1693,8 +1693,8 @@ reveal_type(y) F = list[type[int] | str] [out] _testTypeAliasWithNewStyleUnion.py:5: note: Revealed type is "typing._SpecialForm" -_testTypeAliasWithNewStyleUnion.py:24: note: Revealed type is "Union[Type[builtins.int], builtins.str]" -_testTypeAliasWithNewStyleUnion.py:27: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnion.py:25: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnion.py:28: note: Revealed type is "Union[Type[builtins.int], builtins.str]" [case testTypeAliasWithNewStyleUnionInStub] # flags: --python-version 3.7 From f73ee515091eb63d4167e6473c47e8ef7d308c52 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 7 Nov 2022 17:29:09 +0000 Subject: [PATCH 04/14] WIP add failing cases --- test-data/unit/pythoneval.test | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index b874055e7a66b..665a20d2d81d6 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1721,6 +1721,10 @@ Opt4 = float | None A = Type[int] | str B: TypeAlias = Type[int] | str +C = type[int] | str +D: TypeAlias = type[int] | str +E = str | type[int] +F: TypeAlias = str | type[int] [out] m.pyi:5: note: Revealed type is "typing._SpecialForm" From 096d8bd17f0fd8742579f19917c4bb983d5e3a17 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 24 Nov 2022 13:08:48 +0000 Subject: [PATCH 05/14] Fix aliases in stubs --- mypy/checker.py | 21 +-------------------- mypy/semanal.py | 8 +++++--- test-data/unit/pythoneval.test | 2 ++ 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 431fde299dc0f..b18f5d659d0b5 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2668,26 +2668,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.msg.annotation_in_unchecked_function(context=s) def check_type_alias_rvalue(self, s: AssignmentStmt) -> None: - if not (self.is_stub and isinstance(s.rvalue, OpExpr) and s.rvalue.op == "|"): - # We do this mostly for compatibility with old semantic analyzer. - # TODO: should we get rid of this? - alias_type = self.expr_checker.accept(s.rvalue) - else: - # Avoid type checking 'X | Y' in stubs, since there can be errors - # on older Python targets. - alias_type = AnyType(TypeOfAny.special_form) - - def accept_items(e: Expression) -> None: - if isinstance(e, OpExpr) and e.op == "|": - accept_items(e.left) - accept_items(e.right) - else: - # Nested union types have been converted to type context - # in semantic analysis (such as in 'list[int | str]'), - # so we don't need to deal with them here. - self.expr_checker.accept(e) - - accept_items(s.rvalue) + alias_type = self.expr_checker.accept(s.rvalue) self.store_type(s.lvalues[-1], alias_type) def check_assignment( diff --git a/mypy/semanal.py b/mypy/semanal.py index d9602abdf8586..72fcc54482004 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3440,9 +3440,11 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: no_args=no_args, eager=eager, ) - if isinstance( - s.rvalue, (IndexExpr, CallExpr, OpExpr) - ): # CallExpr is for `void = type(None)` + if isinstance(s.rvalue, (IndexExpr, CallExpr, OpExpr)) and ( + not isinstance(rvalue, OpExpr) + or (self.options.python_version >= (3, 10) or self.is_stub_file) + ): + # Note: CallExpr is for `void = type(None)` and OpExpr is for X | Y union syntax. s.rvalue.analyzed = TypeAliasExpr(alias_node) s.rvalue.analyzed.line = s.line # we use the column from resulting target, to get better location for errors diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 665a20d2d81d6..dea49b5838a77 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1722,11 +1722,13 @@ Opt4 = float | None A = Type[int] | str B: TypeAlias = Type[int] | str C = type[int] | str +reveal_type(C) D: TypeAlias = type[int] | str E = str | type[int] F: TypeAlias = str | type[int] [out] m.pyi:5: note: Revealed type is "typing._SpecialForm" +m.pyi:22: note: Revealed type is "typing._SpecialForm" [case testEnumNameWorkCorrectlyOn311] # flags: --python-version 3.11 From 3e4e8099c5aa719f633437a093f5e0f95430c8af Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 24 Nov 2022 13:11:53 +0000 Subject: [PATCH 06/14] Update test case --- test-data/unit/pythoneval.test | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index dea49b5838a77..043d18b0befe1 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1699,6 +1699,18 @@ _testTypeAliasWithNewStyleUnion.py:28: note: Revealed type is "Union[Type[builti [case testTypeAliasWithNewStyleUnionInStub] # flags: --python-version 3.7 import m +a: m.A +reveal_type(a) +b: m.B +reveal_type(b) +c: m.C +reveal_type(c) +d: m.D +reveal_type(d) +e: m.E +reveal_type(e) +f: m.F +reveal_type(f) [file m.pyi] from typing import Type @@ -1729,6 +1741,12 @@ F: TypeAlias = str | type[int] [out] m.pyi:5: note: Revealed type is "typing._SpecialForm" m.pyi:22: note: Revealed type is "typing._SpecialForm" +_testTypeAliasWithNewStyleUnionInStub.py:4: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:6: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:8: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:10: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:12: note: Revealed type is "Union[builtins.str, Type[builtins.int]]" +_testTypeAliasWithNewStyleUnionInStub.py:14: note: Revealed type is "Union[builtins.str, Type[builtins.int]]" [case testEnumNameWorkCorrectlyOn311] # flags: --python-version 3.11 From a732e7a88b91b25a3b9dc3ad05c3ed5750c96de1 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 24 Nov 2022 13:19:10 +0000 Subject: [PATCH 07/14] Add test case covering #14158 --- test-data/unit/pythoneval.test | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 043d18b0befe1..4529ca01dfbda 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1713,7 +1713,7 @@ f: m.F reveal_type(f) [file m.pyi] -from typing import Type +from typing import Type, Callable from typing_extensions import Literal, TypeAlias Foo = Literal[1, 2] @@ -1738,6 +1738,11 @@ reveal_type(C) D: TypeAlias = type[int] | str E = str | type[int] F: TypeAlias = str | type[int] + +CU1 = int | Callable[[], str | bool] +CU2: TypeAlias = int | Callable[[], str | bool] +CU3 = int | Callable[[str | bool], str] +CU4: TypeAlias = int | Callable[[str | bool], str] [out] m.pyi:5: note: Revealed type is "typing._SpecialForm" m.pyi:22: note: Revealed type is "typing._SpecialForm" From 41cd38383e5753dcc15178ef8fe130a7405166bd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 24 Nov 2022 13:21:55 +0000 Subject: [PATCH 08/14] Minor tweaks --- mypy/nodes.py | 2 +- mypy/semanal.py | 2 +- test-data/unit/pythoneval.test | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 4d5ea621f8fda..cc5db670f8c03 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1994,7 +1994,7 @@ class OpExpr(Expression): right_always: bool # Per static analysis only: Is the right side unreachable? right_unreachable: bool - # Used for expressions that represent type X | Y in some contexts + # Used for expressions that represent a type "X | Y" in some contexts analyzed: TypeAliasExpr | None def __init__(self, op: str, left: Expression, right: Expression) -> None: diff --git a/mypy/semanal.py b/mypy/semanal.py index 72fcc54482004..bc672ccd34579 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3444,7 +3444,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: not isinstance(rvalue, OpExpr) or (self.options.python_version >= (3, 10) or self.is_stub_file) ): - # Note: CallExpr is for `void = type(None)` and OpExpr is for X | Y union syntax. + # Note: CallExpr is for "void = type(None)" and OpExpr is for "X | Y" union syntax. s.rvalue.analyzed = TypeAliasExpr(alias_node) s.rvalue.analyzed.line = s.line # we use the column from resulting target, to get better location for errors diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 4529ca01dfbda..2a895dd1f44ae 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1738,6 +1738,8 @@ reveal_type(C) D: TypeAlias = type[int] | str E = str | type[int] F: TypeAlias = str | type[int] +G = list[type[int] | str] +H = list[str | type[int]] CU1 = int | Callable[[], str | bool] CU2: TypeAlias = int | Callable[[], str | bool] From afc87549a15f8e3535e0f86a7a5950cc1e708874 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 24 Nov 2022 15:33:59 +0000 Subject: [PATCH 09/14] Fix TreeTransform --- mypy/nodes.py | 4 ++-- mypy/treetransform.py | 7 ++++++- test-data/unit/lib-stub/asyncio.pyi | 0 test-data/unit/pythoneval.test | 8 +++++++- 4 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 test-data/unit/lib-stub/asyncio.pyi diff --git a/mypy/nodes.py b/mypy/nodes.py index cc5db670f8c03..670739c85363c 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1997,7 +1997,7 @@ class OpExpr(Expression): # Used for expressions that represent a type "X | Y" in some contexts analyzed: TypeAliasExpr | None - def __init__(self, op: str, left: Expression, right: Expression) -> None: + def __init__(self, op: str, left: Expression, right: Expression, analyzed: TypeAliasExpr | None = None) -> None: super().__init__() self.op = op self.left = left @@ -2005,7 +2005,7 @@ def __init__(self, op: str, left: Expression, right: Expression) -> None: self.method_type = None self.right_always = False self.right_unreachable = False - self.analyzed = None + self.analyzed = analyzed def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_op_expr(self) diff --git a/mypy/treetransform.py b/mypy/treetransform.py index 2f678b89b1e6e..432baf7d73b78 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -519,7 +519,12 @@ def visit_call_expr(self, node: CallExpr) -> CallExpr: ) def visit_op_expr(self, node: OpExpr) -> OpExpr: - new = OpExpr(node.op, self.expr(node.left), self.expr(node.right)) + new = OpExpr( + node.op, + self.expr(node.left), + self.expr(node.right), + cast(Optional[TypeAliasExpr], self.optional_expr(node.analyzed)), + ) new.method_type = self.optional_type(node.method_type) return new diff --git a/test-data/unit/lib-stub/asyncio.pyi b/test-data/unit/lib-stub/asyncio.pyi new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 2a895dd1f44ae..98e099333f426 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1663,7 +1663,7 @@ _testNarrowTypeForDictKeys.py:16: note: Revealed type is "Union[builtins.str, No [case testTypeAliasWithNewStyleUnion] # flags: --python-version 3.10 -from typing import Literal, Type, TypeAlias +from typing import Literal, Type, TypeAlias, TypeVar Foo = Literal[1, 2] reveal_type(Foo) @@ -1691,6 +1691,12 @@ E: TypeAlias = type[int] | str y: E reveal_type(y) F = list[type[int] | str] + +T = TypeVar("T", int, str) +def foo(x: T) -> T: + A = type[int] | str + a: A + return x [out] _testTypeAliasWithNewStyleUnion.py:5: note: Revealed type is "typing._SpecialForm" _testTypeAliasWithNewStyleUnion.py:25: note: Revealed type is "Union[Type[builtins.int], builtins.str]" From ceee87bee619341f0ba9852a4fde09116a79c06f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 24 Nov 2022 15:40:32 +0000 Subject: [PATCH 10/14] Fix aststrip --- mypy/server/aststrip.py | 4 ++++ test-data/unit/fine-grained.test | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/mypy/server/aststrip.py b/mypy/server/aststrip.py index 87ce63e9d5433..1a9d59c893f60 100644 --- a/mypy/server/aststrip.py +++ b/mypy/server/aststrip.py @@ -222,6 +222,10 @@ def visit_index_expr(self, node: IndexExpr) -> None: node.analyzed = None # May have been an alias or type application. super().visit_index_expr(node) + def visit_op_expr(self, node: OpExpr) -> None: + node.analyzed = None # May have been an alias + super().visit_op_expr(node) + def strip_ref_expr(self, node: RefExpr) -> None: node.kind = None node.node = None diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index c162f402486aa..1a318b52a0823 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10277,3 +10277,34 @@ A = str m.py:5: error: Invalid statement in TypedDict definition; expected "field_name: field_type" == m.py:5: error: Invalid statement in TypedDict definition; expected "field_name: field_type" + +[case testTypeAliasWithNewStyleUnionChangedToVariable] +# flags: --python-version 3.10 +import a + +[file a.py] +from b import C, D +A = C | D +a: A +reveal_type(a) + +[file b.py] +C = int +D = str + +[file b.py.2] +C = "x" +D = "y" + +[file b.py.3] +C = str +D = int +[out] +a.py:4: note: Revealed type is "Union[builtins.int, builtins.str]" +== +a.py:2: error: Unsupported left operand type for | ("str") +a.py:3: error: Variable "a.A" is not valid as a type +a.py:3: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +a.py:4: note: Revealed type is "A?" +== +a.py:4: note: Revealed type is "Union[builtins.str, builtins.int]" From 8666b5550dea0e0206ebf0aabebcee7010cb9d11 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 24 Nov 2022 15:48:14 +0000 Subject: [PATCH 11/14] A few additional fixes + add test case --- mypy/strconv.py | 2 ++ mypy/traverser.py | 2 ++ test-data/unit/check-type-aliases.test | 11 +++++++++++ 3 files changed, 15 insertions(+) diff --git a/mypy/strconv.py b/mypy/strconv.py index f1aa6819e2b72..861a7c9b7fa08 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -413,6 +413,8 @@ def visit_call_expr(self, o: mypy.nodes.CallExpr) -> str: return self.dump(a + extra, o) def visit_op_expr(self, o: mypy.nodes.OpExpr) -> str: + if o.analyzed: + return o.analyzed.accept(self) return self.dump([o.op, o.left, o.right], o) def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr) -> str: diff --git a/mypy/traverser.py b/mypy/traverser.py index 3c4f21601b880..378d44c67f47a 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -262,6 +262,8 @@ def visit_call_expr(self, o: CallExpr) -> None: def visit_op_expr(self, o: OpExpr) -> None: o.left.accept(self) o.right.accept(self) + if o.analyzed is not None: + o.analyzed.accept(self) def visit_comparison_expr(self, o: ComparisonExpr) -> None: for operand in o.operands: diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index fab372976ab27..921183ccc895e 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -947,3 +947,14 @@ c.SpecialImplicit = 4 c.SpecialExplicit = 4 [builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] + +[case testNewStyleUnionInTypeAliasWithMalformedInstance] +# flags: --python-version 3.10 +from typing import List + +A = List[int, str] | int # E: "list" expects 1 type argument, but 2 given +B = int | list[int, str] # E: "list" expects 1 type argument, but 2 given +a: A +b: B +reveal_type(a) # N: Revealed type is "Union[builtins.list[Any], builtins.int]" +reveal_type(b) # N: Revealed type is "Union[builtins.int, builtins.list[Any]]" From 7a7284b16ccf97cf4dfd5e274e8b533997c533b4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 24 Nov 2022 15:50:00 +0000 Subject: [PATCH 12/14] Fix type check --- mypy/server/aststrip.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/server/aststrip.py b/mypy/server/aststrip.py index 1a9d59c893f60..83d90f31e8c43 100644 --- a/mypy/server/aststrip.py +++ b/mypy/server/aststrip.py @@ -54,6 +54,7 @@ MypyFile, NameExpr, Node, + OpExpr, OverloadedFuncDef, RefExpr, StarExpr, From 0716501895be389b8270080eb50ddfa994b04269 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 24 Nov 2022 16:03:53 +0000 Subject: [PATCH 13/14] Black --- mypy/nodes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 670739c85363c..840f69aa4ecbd 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1997,7 +1997,9 @@ class OpExpr(Expression): # Used for expressions that represent a type "X | Y" in some contexts analyzed: TypeAliasExpr | None - def __init__(self, op: str, left: Expression, right: Expression, analyzed: TypeAliasExpr | None = None) -> None: + def __init__( + self, op: str, left: Expression, right: Expression, analyzed: TypeAliasExpr | None = None + ) -> None: super().__init__() self.op = op self.left = left From ac2df6c3aa52c50caa7756694a64a8a50ea23a56 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 24 Nov 2022 16:35:03 +0000 Subject: [PATCH 14/14] Remove accidentally added test stub --- test-data/unit/lib-stub/asyncio.pyi | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test-data/unit/lib-stub/asyncio.pyi diff --git a/test-data/unit/lib-stub/asyncio.pyi b/test-data/unit/lib-stub/asyncio.pyi deleted file mode 100644 index e69de29bb2d1d..0000000000000