Skip to content

Commit 61d17ff

Browse files
ilevkivskyimsullivan
authored andcommitted
Visit resolved forward refs by third pass type visitor (#5433)
1 parent 970e8d8 commit 61d17ff

File tree

4 files changed

+95
-23
lines changed

4 files changed

+95
-23
lines changed

mypy/semanal_pass3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ def visit_instance(self, t: Instance) -> Type:
700700
self.fail('Type argument "{}" of "{}" must be '
701701
'a subtype of "{}"'.format(
702702
arg, info.name(), tvar.upper_bound), t)
703-
return t
703+
return super().visit_instance(t)
704704

705705
def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: str,
706706
valids: List[Type], arg_number: int, context: Context) -> None:

mypy/typeanal.py

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -708,28 +708,7 @@ def visit_instance(self, t: Instance) -> None:
708708
self.indicator['synthetic'] = True
709709
# Check type argument count.
710710
if len(t.args) != len(info.type_vars):
711-
if len(t.args) == 0:
712-
any_type = AnyType(TypeOfAny.from_omitted_generics,
713-
line=t.line, column=t.column)
714-
t.args = [any_type] * len(info.type_vars)
715-
return
716-
# Invalid number of type parameters.
717-
n = len(info.type_vars)
718-
s = '{} type arguments'.format(n)
719-
if n == 0:
720-
s = 'no type arguments'
721-
elif n == 1:
722-
s = '1 type argument'
723-
act = str(len(t.args))
724-
if act == '0':
725-
act = 'none'
726-
self.fail('"{}" expects {}, but {} given'.format(
727-
info.name(), s, act), t)
728-
# Construct the correct number of type arguments, as
729-
# otherwise the type checker may crash as it expects
730-
# things to be right.
731-
t.args = [AnyType(TypeOfAny.from_error) for _ in info.type_vars]
732-
t.invalid = True
711+
fix_instance(t, self.fail)
733712
elif info.defn.type_vars:
734713
# Check type argument values. This is postponed to the end of semantic analysis
735714
# since we need full MROs and resolved forward references.
@@ -808,6 +787,8 @@ def visit_forwardref_type(self, t: ForwardRef) -> None:
808787
if t.resolved is None:
809788
resolved = self.anal_type(t.unbound)
810789
t.resolve(resolved)
790+
assert t.resolved is not None
791+
t.resolved.accept(self)
811792

812793
def anal_type(self, tp: UnboundType) -> Type:
813794
tpan = TypeAnalyser(self.api,
@@ -824,6 +805,35 @@ def anal_type(self, tp: UnboundType) -> Type:
824805
TypeVarList = List[Tuple[str, TypeVarExpr]]
825806

826807

808+
def fix_instance(t: Instance, fail: Callable[[str, Context], None]) -> None:
809+
"""Fix a malformed instance by replacing all type arguments with Any.
810+
811+
Also emit a suitable error if this is not due to implicit Any's.
812+
"""
813+
if len(t.args) == 0:
814+
any_type = AnyType(TypeOfAny.from_omitted_generics,
815+
line=t.line, column=t.column)
816+
t.args = [any_type] * len(t.type.type_vars)
817+
return
818+
# Invalid number of type parameters.
819+
n = len(t.type.type_vars)
820+
s = '{} type arguments'.format(n)
821+
if n == 0:
822+
s = 'no type arguments'
823+
elif n == 1:
824+
s = '1 type argument'
825+
act = str(len(t.args))
826+
if act == '0':
827+
act = 'none'
828+
fail('"{}" expects {}, but {} given'.format(
829+
t.type.name(), s, act), t)
830+
# Construct the correct number of type arguments, as
831+
# otherwise the type checker may crash as it expects
832+
# things to be right.
833+
t.args = [AnyType(TypeOfAny.from_error) for _ in t.type.type_vars]
834+
t.invalid = True
835+
836+
827837
def expand_type_alias(target: Type, alias_tvars: List[str], args: List[Type],
828838
fail: Callable[[str, Context], None], no_args: bool, ctx: Context) -> Type:
829839
"""Expand a (generic) type alias target following the rules outlined in TypeAlias docstring.

test-data/unit/check-classes.test

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4498,3 +4498,54 @@ from typing import Any
44984498
def __getattr__(attr: str) -> Any: ...
44994499
[builtins fixtures/module.pyi]
45004500
[out]
4501+
4502+
[case testForwardInstanceWithWrongArgCount]
4503+
from typing import TypeVar, Generic
4504+
4505+
T = TypeVar('T')
4506+
class G(Generic[T]): ...
4507+
4508+
A = G
4509+
x: A[B[int, int]] # E: "G" expects 1 type argument, but 2 given
4510+
B = G
4511+
[out]
4512+
4513+
[case testForwardInstanceWithNoArgs]
4514+
from typing import TypeVar, Generic
4515+
4516+
T = TypeVar('T')
4517+
class G(Generic[T]): ...
4518+
4519+
A = G
4520+
x: A[B]
4521+
reveal_type(x) # E: Revealed type is '__main__.G[__main__.G[Any]]'
4522+
B = G
4523+
[out]
4524+
4525+
[case testForwardInstanceWithBound]
4526+
from typing import TypeVar, Generic
4527+
4528+
T = TypeVar('T', bound=str)
4529+
class G(Generic[T]): ...
4530+
4531+
A = G
4532+
x: A[B[int]]
4533+
B = G
4534+
[out]
4535+
main:7: error: Type argument "builtins.int" of "G" must be a subtype of "builtins.str"
4536+
main:7: error: Type argument "__main__.G[builtins.int]" of "G" must be a subtype of "builtins.str"
4537+
4538+
[case testExtremeForwardReferencing]
4539+
from typing import TypeVar, Generic
4540+
4541+
T = TypeVar('T')
4542+
class B(Generic[T]): ...
4543+
4544+
y: A
4545+
z: A[int]
4546+
x = [y, z]
4547+
reveal_type(x) # E: Revealed type is 'builtins.list[__main__.B*[Any]]'
4548+
4549+
A = B
4550+
[builtins fixtures/list.pyi]
4551+
[out]

test-data/unit/pythoneval.test

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,3 +1306,14 @@ def print_custom_table() -> None:
13061306
reveal_type(row)
13071307
[out]
13081308
_testLoadsOfOverloads.py:24: error: Revealed type is 'builtins.str*'
1309+
1310+
[case testReduceWithAnyInstance]
1311+
from typing import Iterable
1312+
from functools import reduce
1313+
M = Iterable
1314+
def f(m1: M, m2):
1315+
...
1316+
def g(ms: 'T[M]') -> None:
1317+
reduce(f, ms)
1318+
T = Iterable
1319+
[out]

0 commit comments

Comments
 (0)