Skip to content

Commit

Permalink
Generate error if mixing old-style and new-style type vars
Browse files Browse the repository at this point in the history
  • Loading branch information
JukkaL committed May 20, 2024
1 parent 9258de3 commit 268eec8
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 6 deletions.
4 changes: 4 additions & 0 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2421,6 +2421,10 @@ def annotation_in_unchecked_function(self, context: Context) -> None:
code=codes.ANNOTATION_UNCHECKED,
)

def type_parameters_should_be_declared(self, undeclared: list[str], context: Context) -> None:
names = ", ".join('"' + n + '"' for n in undeclared)
self.fail(f"All type parameters should be declared ({names} not declared)", context)


def quote_type_string(type_string: str) -> str:
"""Quotes a type representation for use in messages."""
Expand Down
24 changes: 19 additions & 5 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,14 @@ def update_function_type_variables(self, fun_type: CallableType, defn: FuncItem)
fun_type.variables, has_self_type = a.bind_function_type_variables(fun_type, defn)
if has_self_type and self.type is not None:
self.setup_self_type()
if defn.type_args:
bound_fullnames = {v.fullname for v in fun_type.variables}
declared_fullnames = {self.qualified_name(p.name) for p in defn.type_args}
extra = sorted(bound_fullnames - declared_fullnames)
if extra:
self.msg.type_parameters_should_be_declared(
[n.split(".")[-1] for n in extra], defn
)
return has_self_type

def setup_self_type(self) -> None:
Expand Down Expand Up @@ -2086,11 +2094,17 @@ class Foo(Bar, Generic[T]): ...
self.fail("Duplicate type variables in Generic[...] or Protocol[...]", context)
declared_tvars = remove_dups(declared_tvars)
if not set(all_tvars).issubset(set(declared_tvars)):
self.fail(
"If Generic[...] or Protocol[...] is present"
" it should list all type variables",
context,
)
if defn.type_args:
undeclared = sorted(set(all_tvars) - set(declared_tvars))
self.msg.type_parameters_should_be_declared(
[tv[0] for tv in undeclared], context
)
else:
self.fail(
"If Generic[...] or Protocol[...] is present"
" it should list all type variables",
context,
)
# In case of error, Generic tvars will go first
declared_tvars = remove_dups(declared_tvars + all_tvars)
else:
Expand Down
22 changes: 21 additions & 1 deletion test-data/unit/check-python312.test
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,6 @@ class C[T]:

[case testPEP695InvalidGenericOrProtocolBaseClass]
# mypy: enable-incomplete-feature=NewGenericSyntax

from typing import Generic, Protocol, TypeVar

S = TypeVar("S")
Expand All @@ -1181,3 +1180,24 @@ class P[T](Protocol[T]): # E: No arguments expected for "Protocol" base class
pass
class P2[T](Protocol[S]): # E: No arguments expected for "Protocol" base class
pass

[case testPEP695MixNewAndOldStyleGenerics]
# mypy: enable-incomplete-feature=NewGenericSyntax
from typing import TypeVar

S = TypeVar("S")
U = TypeVar("U")

def f[T](x: T, y: S) -> T | S: ... # E: All type parameters should be declared ("S" not declared)
def g[T](x: S, y: U) -> T | S | U: ... # E: All type parameters should be declared ("S", "U" not declared)

def h[S: int](x: S) -> S:
a: int = x
return x

class C[T]:
def m[X, S](self, x: S, y: U) -> X | S | U: ... # E: All type parameters should be declared ("U" not declared)
def m2(self, x: T, y: S) -> T | S: ...

class D[T](C[S]): # E: All type parameters should be declared ("S" not declared)
pass

0 comments on commit 268eec8

Please sign in to comment.