Skip to content

Commit cc59b56

Browse files
authored
typeanal: add error codes for many errors (#13550)
1 parent 91a6613 commit cc59b56

File tree

4 files changed

+98
-41
lines changed

4 files changed

+98
-41
lines changed

mypy/errorcodes.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ def __str__(self) -> str:
3333
CALL_OVERLOAD: Final = ErrorCode(
3434
"call-overload", "Check that an overload variant matches arguments", "General"
3535
)
36-
VALID_TYPE: Final = ErrorCode("valid-type", "Check that type (annotation) is valid", "General")
36+
VALID_TYPE: Final[ErrorCode] = ErrorCode(
37+
"valid-type", "Check that type (annotation) is valid", "General"
38+
)
3739
VAR_ANNOTATED: Final = ErrorCode(
3840
"var-annotated", "Require variable annotation if type can't be inferred", "General"
3941
)

mypy/message_registry.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
2626

2727

2828
# Invalid types
29-
INVALID_TYPE_RAW_ENUM_VALUE: Final = "Invalid type: try using Literal[{}.{}] instead?"
29+
INVALID_TYPE_RAW_ENUM_VALUE: Final = ErrorMessage(
30+
"Invalid type: try using Literal[{}.{}] instead?", codes.VALID_TYPE
31+
)
3032

3133
# Type checker error message constants
3234
NO_RETURN_VALUE_EXPECTED: Final = ErrorMessage("No return value expected", codes.RETURN_VALUE)

mypy/typeanal.py

+92-36
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def analyze_type_alias(
138138
try:
139139
type = expr_to_unanalyzed_type(node, options, api.is_stub_file)
140140
except TypeTranslationError:
141-
api.fail("Invalid type alias: expression is not a valid type", node)
141+
api.fail("Invalid type alias: expression is not a valid type", node, code=codes.VALID_TYPE)
142142
return None
143143
analyzer = TypeAnalyser(
144144
api,
@@ -281,11 +281,13 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
281281
tvar_def = self.tvar_scope.get_binding(sym)
282282
if isinstance(sym.node, ParamSpecExpr):
283283
if tvar_def is None:
284-
self.fail(f'ParamSpec "{t.name}" is unbound', t)
284+
self.fail(f'ParamSpec "{t.name}" is unbound', t, code=codes.VALID_TYPE)
285285
return AnyType(TypeOfAny.from_error)
286286
assert isinstance(tvar_def, ParamSpecType)
287287
if len(t.args) > 0:
288-
self.fail(f'ParamSpec "{t.name}" used with arguments', t)
288+
self.fail(
289+
f'ParamSpec "{t.name}" used with arguments', t, code=codes.VALID_TYPE
290+
)
289291
# Change the line number
290292
return ParamSpecType(
291293
tvar_def.name,
@@ -298,15 +300,17 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
298300
)
299301
if isinstance(sym.node, TypeVarExpr) and tvar_def is not None and self.defining_alias:
300302
self.fail(
301-
'Can\'t use bound type variable "{}"'
302-
" to define generic alias".format(t.name),
303+
f'Can\'t use bound type variable "{t.name}" to define generic alias',
303304
t,
305+
code=codes.VALID_TYPE,
304306
)
305307
return AnyType(TypeOfAny.from_error)
306308
if isinstance(sym.node, TypeVarExpr) and tvar_def is not None:
307309
assert isinstance(tvar_def, TypeVarType)
308310
if len(t.args) > 0:
309-
self.fail(f'Type variable "{t.name}" used with arguments', t)
311+
self.fail(
312+
f'Type variable "{t.name}" used with arguments', t, code=codes.VALID_TYPE
313+
)
310314
# Change the line number
311315
return TypeVarType(
312316
tvar_def.name,
@@ -322,18 +326,20 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
322326
tvar_def is not None and self.defining_alias
323327
):
324328
self.fail(
325-
'Can\'t use bound type variable "{}"'
326-
" to define generic alias".format(t.name),
329+
f'Can\'t use bound type variable "{t.name}" to define generic alias',
327330
t,
331+
code=codes.VALID_TYPE,
328332
)
329333
return AnyType(TypeOfAny.from_error)
330334
if isinstance(sym.node, TypeVarTupleExpr):
331335
if tvar_def is None:
332-
self.fail(f'TypeVarTuple "{t.name}" is unbound', t)
336+
self.fail(f'TypeVarTuple "{t.name}" is unbound', t, code=codes.VALID_TYPE)
333337
return AnyType(TypeOfAny.from_error)
334338
assert isinstance(tvar_def, TypeVarTupleType)
335339
if len(t.args) > 0:
336-
self.fail(f'Type variable "{t.name}" used with arguments', t)
340+
self.fail(
341+
f'Type variable "{t.name}" used with arguments', t, code=codes.VALID_TYPE
342+
)
337343
# Change the line number
338344
return TypeVarTupleType(
339345
tvar_def.name,
@@ -401,19 +407,23 @@ def cannot_resolve_type(self, t: UnboundType) -> None:
401407

402408
def apply_concatenate_operator(self, t: UnboundType) -> Type:
403409
if len(t.args) == 0:
404-
self.api.fail("Concatenate needs type arguments", t)
410+
self.api.fail("Concatenate needs type arguments", t, code=codes.VALID_TYPE)
405411
return AnyType(TypeOfAny.from_error)
406412

407413
# last argument has to be ParamSpec
408414
ps = self.anal_type(t.args[-1], allow_param_spec=True)
409415
if not isinstance(ps, ParamSpecType):
410-
self.api.fail("The last parameter to Concatenate needs to be a ParamSpec", t)
416+
self.api.fail(
417+
"The last parameter to Concatenate needs to be a ParamSpec",
418+
t,
419+
code=codes.VALID_TYPE,
420+
)
411421
return AnyType(TypeOfAny.from_error)
412422

413423
# TODO: this may not work well with aliases, if those worked.
414424
# Those should be special-cased.
415425
elif ps.prefix.arg_types:
416-
self.api.fail("Nested Concatenates are invalid", t)
426+
self.api.fail("Nested Concatenates are invalid", t, code=codes.VALID_TYPE)
417427

418428
args = self.anal_array(t.args[:-1])
419429
pre = ps.prefix
@@ -437,7 +447,9 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
437447
return AnyType(TypeOfAny.explicit)
438448
elif fullname in FINAL_TYPE_NAMES:
439449
self.fail(
440-
"Final can be only used as an outermost qualifier in a variable annotation", t
450+
"Final can be only used as an outermost qualifier in a variable annotation",
451+
t,
452+
code=codes.VALID_TYPE,
441453
)
442454
return AnyType(TypeOfAny.from_error)
443455
elif fullname == "typing.Tuple" or (
@@ -468,7 +480,9 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
468480
return UnionType.make_union(items)
469481
elif fullname == "typing.Optional":
470482
if len(t.args) != 1:
471-
self.fail("Optional[...] must have exactly one type argument", t)
483+
self.fail(
484+
"Optional[...] must have exactly one type argument", t, code=codes.VALID_TYPE
485+
)
472486
return AnyType(TypeOfAny.from_error)
473487
item = self.anal_type(t.args[0])
474488
return make_optional_type(item)
@@ -488,19 +502,25 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
488502
return None
489503
if len(t.args) != 1:
490504
type_str = "Type[...]" if fullname == "typing.Type" else "type[...]"
491-
self.fail(type_str + " must have exactly one type argument", t)
505+
self.fail(
506+
type_str + " must have exactly one type argument", t, code=codes.VALID_TYPE
507+
)
492508
item = self.anal_type(t.args[0])
493509
if bad_type_type_item(item):
494-
self.fail("Type[...] can't contain another Type[...]", t)
510+
self.fail("Type[...] can't contain another Type[...]", t, code=codes.VALID_TYPE)
495511
item = AnyType(TypeOfAny.from_error)
496512
return TypeType.make_normalized(item, line=t.line, column=t.column)
497513
elif fullname == "typing.ClassVar":
498514
if self.nesting_level > 0:
499-
self.fail("Invalid type: ClassVar nested inside other type", t)
515+
self.fail(
516+
"Invalid type: ClassVar nested inside other type", t, code=codes.VALID_TYPE
517+
)
500518
if len(t.args) == 0:
501519
return AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column)
502520
if len(t.args) != 1:
503-
self.fail("ClassVar[...] must have at most one type argument", t)
521+
self.fail(
522+
"ClassVar[...] must have at most one type argument", t, code=codes.VALID_TYPE
523+
)
504524
return AnyType(TypeOfAny.from_error)
505525
return self.anal_type(t.args[0])
506526
elif fullname in NEVER_NAMES:
@@ -513,23 +533,36 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
513533
"Annotated[...] must have exactly one type argument"
514534
" and at least one annotation",
515535
t,
536+
code=codes.VALID_TYPE,
516537
)
517538
return AnyType(TypeOfAny.from_error)
518539
return self.anal_type(t.args[0])
519540
elif fullname in ("typing_extensions.Required", "typing.Required"):
520541
if not self.allow_required:
521-
self.fail("Required[] can be only used in a TypedDict definition", t)
542+
self.fail(
543+
"Required[] can be only used in a TypedDict definition",
544+
t,
545+
code=codes.VALID_TYPE,
546+
)
522547
return AnyType(TypeOfAny.from_error)
523548
if len(t.args) != 1:
524-
self.fail("Required[] must have exactly one type argument", t)
549+
self.fail(
550+
"Required[] must have exactly one type argument", t, code=codes.VALID_TYPE
551+
)
525552
return AnyType(TypeOfAny.from_error)
526553
return RequiredType(self.anal_type(t.args[0]), required=True)
527554
elif fullname in ("typing_extensions.NotRequired", "typing.NotRequired"):
528555
if not self.allow_required:
529-
self.fail("NotRequired[] can be only used in a TypedDict definition", t)
556+
self.fail(
557+
"NotRequired[] can be only used in a TypedDict definition",
558+
t,
559+
code=codes.VALID_TYPE,
560+
)
530561
return AnyType(TypeOfAny.from_error)
531562
if len(t.args) != 1:
532-
self.fail("NotRequired[] must have exactly one type argument", t)
563+
self.fail(
564+
"NotRequired[] must have exactly one type argument", t, code=codes.VALID_TYPE
565+
)
533566
return AnyType(TypeOfAny.from_error)
534567
return RequiredType(self.anal_type(t.args[0]), required=False)
535568
elif self.anal_type_guard_arg(t, fullname) is not None:
@@ -627,7 +660,11 @@ def analyze_type_with_type_info(
627660
)
628661

629662
if info.fullname == "types.NoneType":
630-
self.fail("NoneType should not be used as a type, please use None instead", ctx)
663+
self.fail(
664+
"NoneType should not be used as a type, please use None instead",
665+
ctx,
666+
code=codes.VALID_TYPE,
667+
)
631668
return NoneType(ctx.line, ctx.column)
632669

633670
return instance
@@ -680,7 +717,7 @@ def analyze_unbound_type_without_type_info(
680717
msg = message_registry.INVALID_TYPE_RAW_ENUM_VALUE.format(
681718
base_enum_short_name, value
682719
)
683-
self.fail(msg, t)
720+
self.fail(msg.value, t, code=msg.code)
684721
return AnyType(TypeOfAny.from_error)
685722
return LiteralType(
686723
value=value,
@@ -763,12 +800,14 @@ def visit_type_list(self, t: TypeList) -> Type:
763800
else:
764801
return AnyType(TypeOfAny.from_error)
765802
else:
766-
self.fail('Bracketed expression "[...]" is not valid as a type', t)
803+
self.fail(
804+
'Bracketed expression "[...]" is not valid as a type', t, code=codes.VALID_TYPE
805+
)
767806
self.note('Did you mean "List[...]"?', t)
768807
return AnyType(TypeOfAny.from_error)
769808

770809
def visit_callable_argument(self, t: CallableArgument) -> Type:
771-
self.fail("Invalid type", t)
810+
self.fail("Invalid type", t, code=codes.VALID_TYPE)
772811
return AnyType(TypeOfAny.from_error)
773812

774813
def visit_instance(self, t: Instance) -> Type:
@@ -831,7 +870,9 @@ def anal_type_guard(self, t: Type) -> Type | None:
831870
def anal_type_guard_arg(self, t: UnboundType, fullname: str) -> Type | None:
832871
if fullname in ("typing_extensions.TypeGuard", "typing.TypeGuard"):
833872
if len(t.args) != 1:
834-
self.fail("TypeGuard must have exactly one type argument", t)
873+
self.fail(
874+
"TypeGuard must have exactly one type argument", t, code=codes.VALID_TYPE
875+
)
835876
return AnyType(TypeOfAny.from_error)
836877
return self.anal_type(t.args[0])
837878
return None
@@ -848,11 +889,19 @@ def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type:
848889
if kind == ARG_STAR:
849890
make_paramspec = paramspec_args
850891
if components[-1] != "args":
851-
self.fail(f'Use "{tvar_name}.args" for variadic "*" parameter', t)
892+
self.fail(
893+
f'Use "{tvar_name}.args" for variadic "*" parameter',
894+
t,
895+
code=codes.VALID_TYPE,
896+
)
852897
elif kind == ARG_STAR2:
853898
make_paramspec = paramspec_kwargs
854899
if components[-1] != "kwargs":
855-
self.fail(f'Use "{tvar_name}.kwargs" for variadic "**" parameter', t)
900+
self.fail(
901+
f'Use "{tvar_name}.kwargs" for variadic "**" parameter',
902+
t,
903+
code=codes.VALID_TYPE,
904+
)
856905
else:
857906
assert False, kind
858907
return make_paramspec(
@@ -966,7 +1015,7 @@ def visit_union_type(self, t: UnionType) -> Type:
9661015
and not self.always_allow_new_syntax
9671016
and not self.options.python_version >= (3, 10)
9681017
):
969-
self.fail("X | Y syntax for unions requires Python 3.10", t)
1018+
self.fail("X | Y syntax for unions requires Python 3.10", t, code=codes.SYNTAX)
9701019
return UnionType(self.anal_array(t.items), t.line)
9711020

9721021
def visit_partial_type(self, t: PartialType) -> Type:
@@ -1096,6 +1145,7 @@ def analyze_callable_type(self, t: UnboundType) -> Type:
10961145
"The first argument to Callable must be a "
10971146
'list of types, parameter specification, or "..."',
10981147
t,
1148+
code=codes.VALID_TYPE,
10991149
)
11001150
self.note(
11011151
"See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas", # noqa: E501
@@ -1149,7 +1199,7 @@ def analyze_callable_args(
11491199

11501200
def analyze_literal_type(self, t: UnboundType) -> Type:
11511201
if len(t.args) == 0:
1152-
self.fail("Literal[...] must have at least one parameter", t)
1202+
self.fail("Literal[...] must have at least one parameter", t, code=codes.VALID_TYPE)
11531203
return AnyType(TypeOfAny.from_error)
11541204

11551205
output: list[Type] = []
@@ -1210,7 +1260,7 @@ def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type]
12101260
msg = f'Parameter {idx} of Literal[...] cannot be of type "{name}"'
12111261
else:
12121262
msg = "Invalid type: Literal[...] cannot contain arbitrary expressions"
1213-
self.fail(msg, ctx)
1263+
self.fail(msg, ctx, code=codes.VALID_TYPE)
12141264
# Note: we deliberately ignore arg.note here: the extra info might normally be
12151265
# helpful, but it generally won't make sense in the context of a Literal[...].
12161266
return None
@@ -1296,7 +1346,11 @@ def bind_function_type_variables(
12961346
defs: list[TypeVarLikeType] = []
12971347
for name, tvar in typevars:
12981348
if not self.tvar_scope.allow_binding(tvar.fullname):
1299-
self.fail(f'Type variable "{name}" is bound by an outer class', defn)
1349+
self.fail(
1350+
f'Type variable "{name}" is bound by an outer class',
1351+
defn,
1352+
code=codes.VALID_TYPE,
1353+
)
13001354
self.tvar_scope.bind_new(name, tvar)
13011355
binding = self.tvar_scope.get_binding(tvar.fullname)
13021356
assert binding is not None
@@ -1335,10 +1389,12 @@ def anal_type(self, t: Type, nested: bool = True, *, allow_param_spec: bool = Fa
13351389
and analyzed.flavor == ParamSpecFlavor.BARE
13361390
):
13371391
if analyzed.prefix.arg_types:
1338-
self.fail("Invalid location for Concatenate", t)
1392+
self.fail("Invalid location for Concatenate", t, code=codes.VALID_TYPE)
13391393
self.note("You can use Concatenate as the first argument to Callable", t)
13401394
else:
1341-
self.fail(f'Invalid location for ParamSpec "{analyzed.name}"', t)
1395+
self.fail(
1396+
f'Invalid location for ParamSpec "{analyzed.name}"', t, code=codes.VALID_TYPE
1397+
)
13421398
self.note(
13431399
"You can use ParamSpec as the first argument to Callable, e.g., "
13441400
"'Callable[{}, int]'".format(analyzed.name),

test-data/unit/check-errorcodes.test

-3
Original file line numberDiff line numberDiff line change
@@ -632,9 +632,6 @@ def g() -> int:
632632
[case testErrorCodeIgnoreNamedDefinedNote]
633633
x: List[int] # type: ignore[name-defined]
634634

635-
[case testErrorCodeIgnoreMiscNote]
636-
x: [int] # type: ignore[misc]
637-
638635
[case testErrorCodeProtocolProblemsIgnore]
639636
from typing_extensions import Protocol
640637

0 commit comments

Comments
 (0)