Skip to content

Commit 9db2368

Browse files
Use more precise context for invalid type argument errors (#18290)
Fixes #12274 Uses the actual invalid type argument as the error context when possible. Given: ```python # flags: --pretty --show-column-number class Foo[S, T: int]: pass x: Foo[str, str] ``` Before: ``` main.py:3:4: error: Type argument "str" of "Foo" must be a subtype of "int" [type-var] x: Foo[str, str] ^ ``` After: ``` main.py:3:13: error: Type argument "str" of "Foo" must be a subtype of "int" [type-var] x: Foo[str, str] ^ ```
1 parent c4f5056 commit 9db2368

File tree

5 files changed

+25
-13
lines changed

5 files changed

+25
-13
lines changed

mypy/semanal_typeargs.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,17 +148,18 @@ def validate_args(
148148
is_error = False
149149
is_invalid = False
150150
for (i, arg), tvar in zip(enumerate(args), type_vars):
151+
context = ctx if arg.line < 0 else arg
151152
if isinstance(tvar, TypeVarType):
152153
if isinstance(arg, ParamSpecType):
153154
is_invalid = True
154155
self.fail(
155156
INVALID_PARAM_SPEC_LOCATION.format(format_type(arg, self.options)),
156-
ctx,
157+
context,
157158
code=codes.VALID_TYPE,
158159
)
159160
self.note(
160161
INVALID_PARAM_SPEC_LOCATION_NOTE.format(arg.name),
161-
ctx,
162+
context,
162163
code=codes.VALID_TYPE,
163164
)
164165
continue
@@ -167,7 +168,7 @@ def validate_args(
167168
self.fail(
168169
f"Cannot use {format_type(arg, self.options)} for regular type variable,"
169170
" only for ParamSpec",
170-
ctx,
171+
context,
171172
code=codes.VALID_TYPE,
172173
)
173174
continue
@@ -182,13 +183,15 @@ def validate_args(
182183
is_error = True
183184
self.fail(
184185
message_registry.INVALID_TYPEVAR_AS_TYPEARG.format(arg.name, name),
185-
ctx,
186+
context,
186187
code=codes.TYPE_VAR,
187188
)
188189
continue
189190
else:
190191
arg_values = [arg]
191-
if self.check_type_var_values(name, arg_values, tvar.name, tvar.values, ctx):
192+
if self.check_type_var_values(
193+
name, arg_values, tvar.name, tvar.values, context
194+
):
192195
is_error = True
193196
# Check against upper bound. Since it's object the vast majority of the time,
194197
# add fast path to avoid a potentially slow subtype check.
@@ -209,7 +212,7 @@ def validate_args(
209212
name,
210213
format_type(upper_bound, self.options),
211214
),
212-
ctx,
215+
context,
213216
code=codes.TYPE_VAR,
214217
)
215218
elif isinstance(tvar, ParamSpecType):
@@ -220,7 +223,7 @@ def validate_args(
220223
self.fail(
221224
"Can only replace ParamSpec with a parameter types list or"
222225
f" another ParamSpec, got {format_type(arg, self.options)}",
223-
ctx,
226+
context,
224227
code=codes.VALID_TYPE,
225228
)
226229
if is_invalid:

test-data/unit/check-classes.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6112,8 +6112,8 @@ A = G
61126112
x: A[B[int]] # E
61136113
B = G
61146114
[out]
6115-
main:8:4: error: Type argument "G[int]" of "G" must be a subtype of "str"
6116-
main:8:6: error: Type argument "int" of "G" must be a subtype of "str"
6115+
main:8:6: error: Type argument "G[int]" of "G" must be a subtype of "str"
6116+
main:8:8: error: Type argument "int" of "G" must be a subtype of "str"
61176117

61186118
[case testExtremeForwardReferencing]
61196119
from typing import TypeVar, Generic

test-data/unit/check-columns.test

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,18 @@ T = TypeVar('T', int, str)
310310
class C(Generic[T]):
311311
pass
312312

313-
def f(c: C[object]) -> None: pass # E:10: Value of type variable "T" of "C" cannot be "object"
313+
def f(c: C[object]) -> None: pass # E:12: Value of type variable "T" of "C" cannot be "object"
314314
(C[object]()) # E:2: Value of type variable "T" of "C" cannot be "object"
315315

316+
[case testColumnInvalidLocationForParamSpec]
317+
from typing import List
318+
from typing_extensions import ParamSpec
319+
320+
P = ParamSpec('P')
321+
def foo(x: List[P]): pass # E:17: Invalid location for ParamSpec "P" \
322+
# N:17: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]"
323+
[builtins fixtures/list.pyi]
324+
316325
[case testColumnSyntaxErrorInTypeAnnotation]
317326
if int():
318327
def f(x # type: int,

test-data/unit/check-generics.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ reveal_type(a) # N: Revealed type is "other.array[Any, other.dtype[builtins.floa
671671
[out]
672672
main:3: error: Type argument "float" of "Array" must be a subtype of "generic" [type-var]
673673
a: other.Array[float]
674-
^
674+
^
675675
[file other.py]
676676
from typing import Any, Generic, TypeVar
677677

test-data/unit/check-newsemanal.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,8 +1666,8 @@ T = TypeVar('T', bound=int)
16661666
class C(Generic[T]): pass
16671667
class C2(Generic[T]): pass
16681668

1669-
A = C[str] # E: Type argument "str" of "C" must be a subtype of "int" \
1670-
# E: Value of type variable "T" of "C" cannot be "str"
1669+
A = C[str] # E: Value of type variable "T" of "C" cannot be "str" \
1670+
# E: Type argument "str" of "C" must be a subtype of "int"
16711671
B = Union[C[str], int] # E: Type argument "str" of "C" must be a subtype of "int"
16721672
S = TypeVar('S', bound=C[str]) # E: Type argument "str" of "C" must be a subtype of "int"
16731673
U = TypeVar('U', C[str], str) # E: Type argument "str" of "C" must be a subtype of "int"

0 commit comments

Comments
 (0)