Skip to content

Commit 8d90d97

Browse files
committed
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.
1 parent 04d44c1 commit 8d90d97

File tree

4 files changed

+48
-4
lines changed

4 files changed

+48
-4
lines changed

mypy/checkexpr.py

+3
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,9 @@ def visit_ellipsis(self, e: EllipsisExpr) -> Type:
28472847

28482848
def visit_op_expr(self, e: OpExpr) -> Type:
28492849
"""Type check a binary operator expression."""
2850+
if e.analyzed:
2851+
# It's actually a type expression X | Y.
2852+
return self.accept(e.analyzed)
28502853
if e.op == "and" or e.op == "or":
28512854
return self.check_boolean_op(e, e)
28522855
if e.op == "*" and isinstance(e.left, ListExpr):

mypy/nodes.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -1968,10 +1968,20 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T:
19681968

19691969

19701970
class OpExpr(Expression):
1971-
"""Binary operation (other than . or [] or comparison operators,
1972-
which have specific nodes)."""
1971+
"""Binary operation.
19731972
1974-
__slots__ = ("op", "left", "right", "method_type", "right_always", "right_unreachable")
1973+
The dot (.), [] and comparison operators have more specific nodes.
1974+
"""
1975+
1976+
__slots__ = (
1977+
"op",
1978+
"left",
1979+
"right",
1980+
"method_type",
1981+
"right_always",
1982+
"right_unreachable",
1983+
"analyzed",
1984+
)
19751985

19761986
__match_args__ = ("left", "op", "right")
19771987

@@ -1984,6 +1994,8 @@ class OpExpr(Expression):
19841994
right_always: bool
19851995
# Per static analysis only: Is the right side unreachable?
19861996
right_unreachable: bool
1997+
# Used for expressions that represent type X | Y in some contexts
1998+
analyzed: TypeAliasExpr | None
19871999

19882000
def __init__(self, op: str, left: Expression, right: Expression) -> None:
19892001
super().__init__()
@@ -1993,6 +2005,7 @@ def __init__(self, op: str, left: Expression, right: Expression) -> None:
19932005
self.method_type = None
19942006
self.right_always = False
19952007
self.right_unreachable = False
2008+
self.analyzed = None
19962009

19972010
def accept(self, visitor: ExpressionVisitor[T]) -> T:
19982011
return visitor.visit_op_expr(self)

mypy/semanal.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3440,7 +3440,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
34403440
no_args=no_args,
34413441
eager=eager,
34423442
)
3443-
if isinstance(s.rvalue, (IndexExpr, CallExpr)): # CallExpr is for `void = type(None)`
3443+
if isinstance(s.rvalue, (IndexExpr, CallExpr, OpExpr)): # CallExpr is for `void = type(None)`
34443444
s.rvalue.analyzed = TypeAliasExpr(alias_node)
34453445
s.rvalue.analyzed.line = s.line
34463446
# we use the column from resulting target, to get better location for errors

test-data/unit/pythoneval.test

+28
Original file line numberDiff line numberDiff line change
@@ -1682,8 +1682,19 @@ Opt4 = float | None
16821682

16831683
A = Type[int] | str
16841684
B: TypeAlias = Type[int] | str
1685+
C = type[int] | str
1686+
1687+
D = type[int] | str
1688+
x: D
1689+
reveal_type(x)
1690+
E: TypeAlias = type[int] | str
1691+
y: E
1692+
reveal_type(y)
1693+
F = list[type[int] | str]
16851694
[out]
16861695
_testTypeAliasWithNewStyleUnion.py:5: note: Revealed type is "typing._SpecialForm"
1696+
_testTypeAliasWithNewStyleUnion.py:24: note: Revealed type is "Union[Type[builtins.int], builtins.str]"
1697+
_testTypeAliasWithNewStyleUnion.py:27: note: Revealed type is "Union[Type[builtins.int], builtins.str]"
16871698

16881699
[case testTypeAliasWithNewStyleUnionInStub]
16891700
# flags: --python-version 3.7
@@ -1735,3 +1746,20 @@ _testEnumNameWorkCorrectlyOn311.py:12: note: Revealed type is "Union[Literal[1]?
17351746
_testEnumNameWorkCorrectlyOn311.py:13: note: Revealed type is "Literal['X']?"
17361747
_testEnumNameWorkCorrectlyOn311.py:14: note: Revealed type is "builtins.int"
17371748
_testEnumNameWorkCorrectlyOn311.py:15: note: Revealed type is "builtins.int"
1749+
1750+
[case testTypeAliasNotSupportedWithNewStyleUnion]
1751+
# flags: --python-version 3.9
1752+
from typing_extensions import TypeAlias
1753+
A = type[int] | str
1754+
B = str | type[int]
1755+
C = str | int
1756+
D: TypeAlias = str | int
1757+
[out]
1758+
_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Invalid type alias: expression is not a valid type
1759+
_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Value of type "Type[type]" is not indexable
1760+
_testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Invalid type alias: expression is not a valid type
1761+
_testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Value of type "Type[type]" is not indexable
1762+
_testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Invalid type alias: expression is not a valid type
1763+
_testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Unsupported left operand type for | ("Type[str]")
1764+
_testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Invalid type alias: expression is not a valid type
1765+
_testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Unsupported left operand type for | ("Type[str]")

0 commit comments

Comments
 (0)