diff --git a/mypy/semanal_pass3.py b/mypy/semanal_pass3.py index 6a554557f2b1..5aaeb16ac2d8 100644 --- a/mypy/semanal_pass3.py +++ b/mypy/semanal_pass3.py @@ -700,7 +700,7 @@ def visit_instance(self, t: Instance) -> Type: self.fail('Type argument "{}" of "{}" must be ' 'a subtype of "{}"'.format( arg, info.name(), tvar.upper_bound), t) - return t + return super().visit_instance(t) def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: str, valids: List[Type], arg_number: int, context: Context) -> None: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 841dd51dad95..51af7826222d 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -708,28 +708,7 @@ def visit_instance(self, t: Instance) -> None: self.indicator['synthetic'] = True # Check type argument count. if len(t.args) != len(info.type_vars): - if len(t.args) == 0: - any_type = AnyType(TypeOfAny.from_omitted_generics, - line=t.line, column=t.column) - t.args = [any_type] * len(info.type_vars) - return - # Invalid number of type parameters. - n = len(info.type_vars) - s = '{} type arguments'.format(n) - if n == 0: - s = 'no type arguments' - elif n == 1: - s = '1 type argument' - act = str(len(t.args)) - if act == '0': - act = 'none' - self.fail('"{}" expects {}, but {} given'.format( - info.name(), s, act), t) - # Construct the correct number of type arguments, as - # otherwise the type checker may crash as it expects - # things to be right. - t.args = [AnyType(TypeOfAny.from_error) for _ in info.type_vars] - t.invalid = True + fix_instance(t, self.fail) elif info.defn.type_vars: # Check type argument values. This is postponed to the end of semantic analysis # since we need full MROs and resolved forward references. @@ -808,6 +787,8 @@ def visit_forwardref_type(self, t: ForwardRef) -> None: if t.resolved is None: resolved = self.anal_type(t.unbound) t.resolve(resolved) + assert t.resolved is not None + t.resolved.accept(self) def anal_type(self, tp: UnboundType) -> Type: tpan = TypeAnalyser(self.api, @@ -824,6 +805,35 @@ def anal_type(self, tp: UnboundType) -> Type: TypeVarList = List[Tuple[str, TypeVarExpr]] +def fix_instance(t: Instance, fail: Callable[[str, Context], None]) -> None: + """Fix a malformed instance by replacing all type arguments with Any. + + Also emit a suitable error if this is not due to implicit Any's. + """ + if len(t.args) == 0: + any_type = AnyType(TypeOfAny.from_omitted_generics, + line=t.line, column=t.column) + t.args = [any_type] * len(t.type.type_vars) + return + # Invalid number of type parameters. + n = len(t.type.type_vars) + s = '{} type arguments'.format(n) + if n == 0: + s = 'no type arguments' + elif n == 1: + s = '1 type argument' + act = str(len(t.args)) + if act == '0': + act = 'none' + fail('"{}" expects {}, but {} given'.format( + t.type.name(), s, act), t) + # Construct the correct number of type arguments, as + # otherwise the type checker may crash as it expects + # things to be right. + t.args = [AnyType(TypeOfAny.from_error) for _ in t.type.type_vars] + t.invalid = True + + def expand_type_alias(target: Type, alias_tvars: List[str], args: List[Type], fail: Callable[[str, Context], None], no_args: bool, ctx: Context) -> Type: """Expand a (generic) type alias target following the rules outlined in TypeAlias docstring. diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 8dfe4717a479..b573bafc7316 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -4498,3 +4498,54 @@ from typing import Any def __getattr__(attr: str) -> Any: ... [builtins fixtures/module.pyi] [out] + +[case testForwardInstanceWithWrongArgCount] +from typing import TypeVar, Generic + +T = TypeVar('T') +class G(Generic[T]): ... + +A = G +x: A[B[int, int]] # E: "G" expects 1 type argument, but 2 given +B = G +[out] + +[case testForwardInstanceWithNoArgs] +from typing import TypeVar, Generic + +T = TypeVar('T') +class G(Generic[T]): ... + +A = G +x: A[B] +reveal_type(x) # E: Revealed type is '__main__.G[__main__.G[Any]]' +B = G +[out] + +[case testForwardInstanceWithBound] +from typing import TypeVar, Generic + +T = TypeVar('T', bound=str) +class G(Generic[T]): ... + +A = G +x: A[B[int]] +B = G +[out] +main:7: error: Type argument "builtins.int" of "G" must be a subtype of "builtins.str" +main:7: error: Type argument "__main__.G[builtins.int]" of "G" must be a subtype of "builtins.str" + +[case testExtremeForwardReferencing] +from typing import TypeVar, Generic + +T = TypeVar('T') +class B(Generic[T]): ... + +y: A +z: A[int] +x = [y, z] +reveal_type(x) # E: Revealed type is 'builtins.list[__main__.B*[Any]]' + +A = B +[builtins fixtures/list.pyi] +[out] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index daefaadddcfe..606b2bd47e01 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1306,3 +1306,14 @@ def print_custom_table() -> None: reveal_type(row) [out] _testLoadsOfOverloads.py:24: error: Revealed type is 'builtins.str*' + +[case testReduceWithAnyInstance] +from typing import Iterable +from functools import reduce +M = Iterable +def f(m1: M, m2): + ... +def g(ms: 'T[M]') -> None: + reduce(f, ms) +T = Iterable +[out]