Skip to content

Fix crash with type alias inside __init__ in incremental mode #10432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2796,15 +2796,18 @@ def f(x: B[T]) -> T: ... # without T, Any would be used here
itself an alias), while the second cannot be subscripted because of
Python runtime limitation.
line and column: Line an column on the original alias definition.
eager: If True, immediately expand alias when referred to (useful for aliases
within functions that can't be looked up from the symbol table)
"""
__slots__ = ('target', '_fullname', 'alias_tvars', 'no_args', 'normalized',
'line', 'column', '_is_recursive')
'line', 'column', '_is_recursive', 'eager')

def __init__(self, target: 'mypy.types.Type', fullname: str, line: int, column: int,
*,
alias_tvars: Optional[List[str]] = None,
no_args: bool = False,
normalized: bool = False) -> None:
normalized: bool = False,
eager: bool = False) -> None:
self._fullname = fullname
self.target = target
if alias_tvars is None:
Expand All @@ -2815,6 +2818,7 @@ def __init__(self, target: 'mypy.types.Type', fullname: str, line: int, column:
# This attribute is manipulated by TypeAliasType. If non-None,
# it is the cached value.
self._is_recursive = None # type: Optional[bool]
self.eager = eager
super().__init__(line, column)

@property
Expand Down
15 changes: 12 additions & 3 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2593,10 +2593,19 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
# Note: with the new (lazy) type alias representation we only need to set no_args to True
# if the expected number of arguments is non-zero, so that aliases like A = List work.
# However, eagerly expanding aliases like Text = str is a nice performance optimization.
no_args = isinstance(res, Instance) and not res.args # type: ignore
no_args = isinstance(res, Instance) and not res.args # type: ignore[misc]
fix_instance_types(res, self.fail, self.note, self.options.python_version)
alias_node = TypeAlias(res, self.qualified_name(lvalue.name), s.line, s.column,
alias_tvars=alias_tvars, no_args=no_args)
# Aliases defined within functions can't be accessed outside
# the function, since the symbol table will no longer
# exist. Work around by expanding them eagerly when used.
eager = self.is_func_scope()
alias_node = TypeAlias(res,
self.qualified_name(lvalue.name),
s.line,
s.column,
alias_tvars=alias_tvars,
no_args=no_args,
eager=eager)
if isinstance(s.rvalue, (IndexExpr, CallExpr)): # CallExpr is for `void = type(None)`
s.rvalue.analyzed = TypeAliasExpr(alias_node)
s.rvalue.analyzed.line = s.line
Expand Down
3 changes: 3 additions & 0 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
python_version=self.options.python_version,
use_generic_error=True,
unexpanded_type=t)
if node.eager:
# TODO: Generate error if recursive (once we have recursive types)
res = get_proper_type(res)
return res
elif isinstance(node, TypeInfo):
return self.analyze_type_with_type_info(node, t.args, t)
Expand Down
29 changes: 29 additions & 0 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -5532,3 +5532,32 @@ def g() -> None:
n: NT = NT(y='x')

[builtins fixtures/tuple.pyi]

[case testIncrementalNestedTypeAlias]
import a

[file a.py]
import b

[file a.py.2]
import b
reveal_type(b.C().x)
reveal_type(b.D().x)

[file b.py]
from typing import List

class C:
def __init__(self) -> None:
Alias = List[int]
self.x = [] # type: Alias

class D:
def __init__(self) -> None:
Alias = List[str]
self.x = [] # type: Alias

[builtins fixtures/list.pyi]
[out2]
tmp/a.py:2: note: Revealed type is "builtins.list[builtins.int]"
tmp/a.py:3: note: Revealed type is "builtins.list[builtins.str]"