diff --git a/misc/proper_plugin.py b/misc/proper_plugin.py index c30999448387..d33fab3c4441 100644 --- a/misc/proper_plugin.py +++ b/misc/proper_plugin.py @@ -1,3 +1,4 @@ +from mypy import message_registry from mypy.plugin import Plugin, FunctionContext from mypy.types import ( Type, Instance, CallableType, UnionType, get_proper_type, ProperType, @@ -40,8 +41,7 @@ def isinstance_proper_hook(ctx: FunctionContext) -> Type: isinstance(get_proper_type(arg), AnyType) and is_dangerous_target(right)): if is_special_target(right): return ctx.default_return_type - ctx.api.fail('Never apply isinstance() to unexpanded types;' - ' use mypy.types.get_proper_type() first', ctx.context) + ctx.api.fail(message_registry.ISINSTANCE_ON_UNEXPANDED_TYPE, ctx.context) ctx.api.note('If you pass on the original type' # type: ignore[attr-defined] ' after the check, always use its unexpanded version', ctx.context) return ctx.default_return_type @@ -109,7 +109,7 @@ def proper_type_hook(ctx: FunctionContext) -> Type: # Minimize amount of spurious errors from overload machinery. # TODO: call the hook on the overload as a whole? if isinstance(arg_type, (UnionType, Instance)): - ctx.api.fail('Redundant call to get_proper_type()', ctx.context) + ctx.api.fail(message_registry.REDUNDANT_GET_PROPER_TYPE, ctx.context) return ctx.default_return_type @@ -122,7 +122,7 @@ def proper_types_hook(ctx: FunctionContext) -> Type: item_type = UnionType.make_union([NoneTyp(), proper_type]) ok_type = ctx.api.named_generic_type('typing.Iterable', [item_type]) if is_proper_subtype(arg_type, ok_type): - ctx.api.fail('Redundant call to get_proper_types()', ctx.context) + ctx.api.fail(message_registry.REDUNDANT_GET_PROPER_TYPE, ctx.context) return ctx.default_return_type diff --git a/mypy/checker.py b/mypy/checker.py index ba020f5d97d5..8ed9ceb95c3c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -55,6 +55,7 @@ is_literal_type_like, ) from mypy import message_registry +from mypy.message_registry import ErrorMessage from mypy.subtypes import ( is_subtype, is_equivalent, is_proper_subtype, is_more_precise, restrict_subtype_away, is_subtype_ignoring_tvars, is_callable_compatible, @@ -1000,10 +1001,9 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str]) # entirely pass/Ellipsis/raise NotImplementedError. if isinstance(return_type, UninhabitedType): # This is a NoReturn function - self.msg.fail(message_registry.INVALID_IMPLICIT_RETURN, defn) + self.fail(message_registry.INVALID_IMPLICIT_RETURN, defn) else: - self.msg.fail(message_registry.MISSING_RETURN_STATEMENT, defn, - code=codes.RETURN) + self.fail(message_registry.MISSING_RETURN_STATEMENT, defn) self.return_types.pop() @@ -1059,31 +1059,25 @@ def is_unannotated_any(t: Type) -> bool: if fdef.type is None and self.options.disallow_untyped_defs: if (not fdef.arguments or (len(fdef.arguments) == 1 and (fdef.arg_names[0] == 'self' or fdef.arg_names[0] == 'cls'))): - self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef, - code=codes.NO_UNTYPED_DEF) + self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef) if not has_return_statement(fdef) and not fdef.is_generator: self.note('Use "-> None" if function does not return a value', fdef, code=codes.NO_UNTYPED_DEF) else: - self.fail(message_registry.FUNCTION_TYPE_EXPECTED, fdef, - code=codes.NO_UNTYPED_DEF) + self.fail(message_registry.FUNCTION_TYPE_EXPECTED, fdef) elif isinstance(fdef.type, CallableType): ret_type = get_proper_type(fdef.type.ret_type) if is_unannotated_any(ret_type): - self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef, - code=codes.NO_UNTYPED_DEF) + self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef) elif fdef.is_generator: if is_unannotated_any(self.get_generator_return_type(ret_type, fdef.is_coroutine)): - self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef, - code=codes.NO_UNTYPED_DEF) + self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef) elif fdef.is_coroutine and isinstance(ret_type, Instance): if is_unannotated_any(self.get_coroutine_return_type(ret_type)): - self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef, - code=codes.NO_UNTYPED_DEF) + self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef) if any(is_unannotated_any(t) for t in fdef.type.arg_types): - self.fail(message_registry.ARGUMENT_TYPE_EXPECTED, fdef, - code=codes.NO_UNTYPED_DEF) + self.fail(message_registry.ARGUMENT_TYPE_EXPECTED, fdef) def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None: self_type = fill_typevars_with_any(fdef.info) @@ -1358,7 +1352,7 @@ def check_getattr_method(self, typ: Type, context: Context, name: str) -> None: if len(self.scope.stack) == 1: # module scope if name == '__getattribute__': - self.msg.fail(message_registry.MODULE_LEVEL_GETATTRIBUTE, context) + self.fail(message_registry.MODULE_LEVEL_GETATTRIBUTE, context) return # __getattr__ is fine at the module level as of Python 3.7 (PEP 562). We could # show an error for Python < 3.7, but that would be annoying in code that supports @@ -2544,16 +2538,16 @@ def check_assignment_to_multiple_lvalues(self, lvalues: List[Lvalue], rvalue: Ex elif self.type_is_iterable(typs) and isinstance(typs, Instance): if (iterable_type is not None and iterable_type != self.iterable_item_type(typs)): - self.fail("Contiguous iterable with same type expected", context) + self.fail(message_registry.CONTIGUOUS_ITERABLE_EXPECTED, context) else: if last_idx is None or last_idx + 1 == idx_rval: rvalues.append(rval) last_idx = idx_rval iterable_type = self.iterable_item_type(typs) else: - self.fail("Contiguous iterable with same type expected", context) + self.fail(message_registry.CONTIGUOUS_ITERABLE_EXPECTED, context) else: - self.fail("Invalid type '{}' for *expr (iterable expected)".format(typs), + self.fail(message_registry.ITERABLE_TYPE_EXPECTED.format(typs), context) else: rvalues.append(rval) @@ -3085,7 +3079,7 @@ def check_member_assignment(self, instance_type: Type, attribute_type: Type, dunder_set = attribute_type.type.get_method('__set__') if dunder_set is None: - self.msg.fail(message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format(attribute_type), + self.fail(message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format(attribute_type), context) return AnyType(TypeOfAny.from_error), get_type, False @@ -3269,8 +3263,7 @@ def check_return_stmt(self, s: ReturnStmt) -> None: # Functions returning a value of type None are allowed to have a None return. if is_lambda or isinstance(typ, NoneType): return - self.fail(message_registry.NO_RETURN_VALUE_EXPECTED, s, - code=codes.RETURN_VALUE) + self.fail(message_registry.NO_RETURN_VALUE_EXPECTED, s) else: self.check_subtype( subtype_label='got', @@ -3292,7 +3285,7 @@ def check_return_stmt(self, s: ReturnStmt) -> None: return if self.in_checked_function(): - self.fail(message_registry.RETURN_VALUE_EXPECTED, s, code=codes.RETURN_VALUE) + self.fail(message_registry.RETURN_VALUE_EXPECTED, s) def visit_if_stmt(self, s: IfStmt) -> None: """Type check an if statement.""" @@ -4164,7 +4157,7 @@ def find_isinstance_check_helper(self, node: Expression) -> Tuple[TypeMap, TypeM if node.callee.type_guard is not None: # TODO: Follow keyword args or *args, **kwargs if node.arg_kinds[0] != nodes.ARG_POS: - self.fail("Type guard requires positional argument", node) + self.fail(message_registry.TYPE_GUARD_POS_ARG_REQUIRED, node) return {}, {} if literal(expr) == LITERAL_TYPE: return {expr: TypeGuardType(node.callee.type_guard)}, {} @@ -4685,7 +4678,7 @@ def check_subtype(self, subtype: Type, supertype: Type, context: Context, - msg: str = message_registry.INCOMPATIBLE_TYPES, + msg: Union[str, ErrorMessage] = message_registry.INCOMPATIBLE_TYPES, subtype_label: Optional[str] = None, supertype_label: Optional[str] = None, *, @@ -4695,9 +4688,14 @@ def check_subtype(self, if is_subtype(subtype, supertype): return True + if isinstance(msg, ErrorMessage): + msg_text = msg.value + code = msg.code + else: + msg_text = msg subtype = get_proper_type(subtype) supertype = get_proper_type(supertype) - if self.msg.try_report_long_tuple_assignment_error(subtype, supertype, context, msg, + if self.msg.try_report_long_tuple_assignment_error(subtype, supertype, context, msg_text, subtype_label, supertype_label, code=code): return False if self.should_suppress_optional_error([subtype]): @@ -4716,8 +4714,9 @@ def check_subtype(self, if isinstance(subtype, Instance) and isinstance(supertype, Instance): notes = append_invariance_notes([], subtype, supertype) if extra_info: - msg += ' (' + ', '.join(extra_info) + ')' - self.fail(msg, context, code=code) + msg_text += ' (' + ', '.join(extra_info) + ')' + + self.fail(ErrorMessage(msg_text, code=code), context) for note in notes: self.msg.note(note, context, code=code) if note_msg: @@ -4988,9 +4987,9 @@ def temp_node(self, t: Type, context: Optional[Context] = None) -> TempNode: """Create a temporary node with the given, fixed type.""" return TempNode(t, context=context) - def fail(self, msg: str, context: Context, *, code: Optional[ErrorCode] = None) -> None: + def fail(self, msg: ErrorMessage, context: Context, *, code: Optional[ErrorCode] = None) -> None: """Produce an error message.""" - self.msg.fail(msg, context, code=code) + self.msg.fail(msg, context) def note(self, msg: str, diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 5aef881aa2ff..4fc85921a02e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1637,8 +1637,7 @@ def check_overload_call(self, callable_name=callable_name, object_type=object_type) if union_interrupted: - self.chk.fail("Not all union combinations were tried" - " because there are too many unions", context) + self.chk.fail(message_registry.TOO_MANY_UNION_COMBINATIONS, context) return result def plausible_overload_call_targets(self, @@ -3641,7 +3640,7 @@ def _super_arg_types(self, e: SuperExpr) -> Union[Type, Tuple[Type, Type]]: return AnyType(TypeOfAny.unannotated) elif len(e.call.args) == 0: if self.chk.options.python_version[0] == 2: - self.chk.fail(message_registry.TOO_FEW_ARGS_FOR_SUPER, e, code=codes.CALL_ARG) + self.chk.fail(message_registry.TOO_FEW_ARGS_FOR_SUPER, e) return AnyType(TypeOfAny.from_error) elif not e.info: # This has already been reported by the semantic analyzer. diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index e1c4f71fa61b..dd9747c9dcbb 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -177,13 +177,11 @@ def parse_format_value(format_value: str, ctx: Context, msg: MessageBuilder, custom_match, start_pos=start_pos, non_standard_format_spec=True) else: - msg.fail('Invalid conversion specifier in format string', - ctx, code=codes.STRING_FORMATTING) + msg.fail(message_registry.FORMAT_STR_INVALID_SPECIFIER, ctx) return None if conv_spec.key and ('{' in conv_spec.key or '}' in conv_spec.key): - msg.fail('Conversion value must not contain { or }', - ctx, code=codes.STRING_FORMATTING) + msg.fail(message_registry.FORMAT_STR_BRACES_IN_SPECIFIER, ctx) return None result.append(conv_spec) @@ -191,8 +189,7 @@ def parse_format_value(format_value: str, ctx: Context, msg: MessageBuilder, if (conv_spec.format_spec and conv_spec.non_standard_format_spec and ('{' in conv_spec.format_spec or '}' in conv_spec.format_spec)): if nested: - msg.fail('Formatting nesting must be at most two levels deep', - ctx, code=codes.STRING_FORMATTING) + msg.fail(message_registry.FORMAT_STR_NESTING_ATMOST_TWO_LEVELS, ctx) return None sub_conv_specs = parse_format_value(conv_spec.format_spec, ctx, msg, nested=True) @@ -230,8 +227,7 @@ def find_non_escaped_targets(format_value: str, ctx: Context, if pos < len(format_value) - 1 and format_value[pos + 1] == '}': pos += 1 else: - msg.fail('Invalid conversion specifier in format string:' - ' unexpected }', ctx, code=codes.STRING_FORMATTING) + msg.fail(message_registry.FORMAT_STR_UNEXPECTED_RBRACE, ctx) return None else: # Adjust nesting level, then either continue adding chars or move on. @@ -246,8 +242,7 @@ def find_non_escaped_targets(format_value: str, ctx: Context, next_spec = '' pos += 1 if nesting: - msg.fail('Invalid conversion specifier in format string:' - ' unmatched {', ctx, code=codes.STRING_FORMATTING) + msg.fail(message_registry.FORMAT_STR_UNMATCHED_LBRACE, ctx) return None return result @@ -326,9 +321,9 @@ def check_specs_in_format_call(self, call: CallExpr, if (not custom_special_method(actual_type, '__format__', check_all=True) or spec.conversion): # TODO: add support for some custom specs like datetime? - self.msg.fail('Unrecognized format' - ' specification "{}"'.format(spec.format_spec[1:]), - call, code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.UNRECOGNIZED_FORMAT_SPEC + .format(spec.format_spec[1:]), + call) continue # Adjust expected and actual types. if not spec.conv_type: @@ -344,9 +339,9 @@ def check_specs_in_format_call(self, call: CallExpr, if spec.conversion is not None: # If the explicit conversion is given, then explicit conversion is called _first_. if spec.conversion[1] not in 'rsa': - self.msg.fail('Invalid conversion type "{}",' - ' must be one of "r", "s" or "a"'.format(spec.conversion[1]), - call, code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_INVALID_CONVERSION_TYPE + .format(spec.conversion[1]), + call) actual_type = self.named_type('builtins.str') # Perform the checks for given types. @@ -379,18 +374,14 @@ def perform_special_format_checks(self, spec: ConversionSpecifier, call: CallExp if self.chk.options.python_version >= (3, 0): if (has_type_component(actual_type, 'builtins.bytes') and not custom_special_method(actual_type, '__str__')): - self.msg.fail( - "On Python 3 '{}'.format(b'abc') produces \"b'abc'\", not 'abc'; " - "use '{!r}'.format(b'abc') if this is desired behavior", - call, code=codes.STR_BYTES_PY3) + self.msg.fail(message_registry.FORMAT_STR_BYTES_USE_REPR, call) if spec.flags: numeric_types = UnionType([self.named_type('builtins.int'), self.named_type('builtins.float')]) if (spec.conv_type and spec.conv_type not in NUMERIC_TYPES_NEW or not spec.conv_type and not is_subtype(actual_type, numeric_types) and not custom_special_method(actual_type, '__format__')): - self.msg.fail('Numeric flags are only allowed for numeric types', call, - code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_INVALID_NUMERIC_FLAG, call) def find_replacements_in_call(self, call: CallExpr, keys: List[str]) -> List[Expression]: @@ -404,16 +395,15 @@ def find_replacements_in_call(self, call: CallExpr, if key.isdecimal(): expr = self.get_expr_by_position(int(key), call) if not expr: - self.msg.fail('Cannot find replacement for positional' - ' format specifier {}'.format(key), call, - code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_REPLACEMENT_NOT_FOUND.format(key), + call) expr = TempNode(AnyType(TypeOfAny.from_error)) else: expr = self.get_expr_by_name(key, call) if not expr: - self.msg.fail('Cannot find replacement for named' - ' format specifier "{}"'.format(key), call, - code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_NAMED_REPLACEMENT_NOT_FOUND + .format(key), + call) expr = TempNode(AnyType(TypeOfAny.from_error)) result.append(expr) if not isinstance(expr, TempNode): @@ -482,8 +472,7 @@ def auto_generate_keys(self, all_specs: List[ConversionSpecifier], some_defined = any(s.key and s.key.isdecimal() for s in all_specs) all_defined = all(bool(s.key) for s in all_specs) if some_defined and not all_defined: - self.msg.fail('Cannot combine automatic field numbering and' - ' manual field specification', ctx, code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_PARTIAL_FIELD_NUMBERING, ctx) return False if all_defined: return True @@ -518,8 +507,7 @@ def apply_field_accessors(self, spec: ConversionSpecifier, repl: Expression, dummy, fnam="", module=None, options=self.chk.options, errors=temp_errors ) if temp_errors.is_errors(): - self.msg.fail('Syntax error in format specifier "{}"'.format(spec.field), - ctx, code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_SYNTAX_ERROR.format(spec.field), ctx) return TempNode(AnyType(TypeOfAny.from_error)) # These asserts are guaranteed by the original regexp. @@ -552,9 +540,8 @@ class User(TypedDict): '{[id]:d} -> {[name]}'.format(u) """ if not isinstance(temp_ast, (MemberExpr, IndexExpr)): - self.msg.fail('Only index and member expressions are allowed in' - ' format field accessors; got "{}"'.format(spec.field), - ctx, code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_INVALID_ACCESSOR_EXPR.format(spec.field), + ctx) return False if isinstance(temp_ast, MemberExpr): node = temp_ast.expr @@ -563,9 +550,9 @@ class User(TypedDict): if not isinstance(temp_ast.index, (NameExpr, IntExpr)): assert spec.key, "Call this method only after auto-generating keys!" assert spec.field - self.msg.fail('Invalid index expression in format field' - ' accessor "{}"'.format(spec.field[len(spec.key):]), ctx, - code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_INVALID_INDEX_ACCESSOR + .format(spec.field[len(spec.key):]), + ctx) return False if isinstance(temp_ast.index, NameExpr): temp_ast.index = StrExpr(temp_ast.index.name) @@ -594,8 +581,7 @@ def check_str_interpolation(self, specifiers = parse_conversion_specifiers(expr.value) has_mapping_keys = self.analyze_conversion_specifiers(specifiers, expr) if isinstance(expr, BytesExpr) and (3, 0) <= self.chk.options.python_version < (3, 5): - self.msg.fail('Bytes formatting is only supported in Python 3.5 and later', - replacements, code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_BYTES_ABOVE_PY35, replacements) return AnyType(TypeOfAny.from_error) self.unicode_upcast = False @@ -691,8 +677,8 @@ def check_mapping_str_interpolation(self, specifiers: List[ConversionSpecifier], if self.chk.options.python_version >= (3, 0) and isinstance(expr, BytesExpr): # Special case: for bytes formatting keys must be bytes. if not isinstance(k, BytesExpr): - self.msg.fail('Dictionary keys in bytes formatting must be bytes,' - ' not strings', expr, code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_BYTES_DICT_KEYS_MUST_BE_BYTES, + expr) key_str = cast(FormatStringExpr, k).value mapping[key_str] = self.accept(v) @@ -835,10 +821,7 @@ def check_s_special_cases(self, expr: FormatStringExpr, typ: Type, context: Cont # Couple special cases for string formatting. if self.chk.options.python_version >= (3, 0): if has_type_component(typ, 'builtins.bytes'): - self.msg.fail( - "On Python 3 '%s' % b'abc' produces \"b'abc'\", not 'abc'; " - "use '%r' % b'abc' if this is desired behavior", - context, code=codes.STR_BYTES_PY3) + self.msg.fail(message_registry.FORMAT_STR_BYTES_USE_REPR_OLD, context) return False if self.chk.options.python_version < (3, 0): if has_type_component(typ, 'builtins.unicode'): @@ -847,8 +830,7 @@ def check_s_special_cases(self, expr: FormatStringExpr, typ: Type, context: Cont # A special case for bytes formatting: b'%s' actually requires bytes on Python 3. if self.chk.options.python_version >= (3, 0): if has_type_component(typ, 'builtins.str'): - self.msg.fail("On Python 3 b'%s' requires bytes, not string", context, - code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_BYTES_REQUIRED_PY3, context) return False return True @@ -903,18 +885,15 @@ def conversion_type(self, p: str, context: Context, expr: FormatStringExpr, INT_TYPES = REQUIRE_INT_NEW if format_call else REQUIRE_INT_OLD if p == 'b' and not format_call: if self.chk.options.python_version < (3, 5): - self.msg.fail('Format character "b" is only supported in Python 3.5 and later', - context, code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_INVALID_BYTES_SPECIFIER_PY35, context) return None if not isinstance(expr, BytesExpr): - self.msg.fail('Format character "b" is only supported on bytes patterns', context, - code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_INVALID_BYTES_SPECIFIER, context) return None return self.named_type('builtins.bytes') elif p == 'a': if self.chk.options.python_version < (3, 0): - self.msg.fail('Format character "a" is only supported in Python 3', context, - code=codes.STRING_FORMATTING) + self.msg.fail(message_registry.FORMAT_STR_ASCII_SPECIFIER_PY3, context) return None # TODO: return type object? return AnyType(TypeOfAny.special_form) diff --git a/mypy/errors.py b/mypy/errors.py index cc66653aeca3..c49b13f96d58 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -4,13 +4,14 @@ from mypy.backports import OrderedDict from collections import defaultdict -from typing import Tuple, List, TypeVar, Set, Dict, Optional, TextIO, Callable +from typing import Tuple, List, TypeVar, Set, Dict, Optional, TextIO, Callable, Union from typing_extensions import Final from mypy.scope import Scope from mypy.options import Options from mypy.version import __version__ as mypy_version from mypy.errorcodes import ErrorCode, IMPORT +from mypy.message_registry import ErrorMessage from mypy import errorcodes as codes from mypy.util import DEFAULT_SOURCE_OFFSET, is_typeshed_file @@ -661,7 +662,10 @@ def render_messages(self, result.append((file, -1, -1, 'note', 'In class "{}":'.format(e.type), e.allow_dups, None)) - result.append((file, e.line, e.column, e.severity, e.message, e.allow_dups, e.code)) + if isinstance(e.message, ErrorMessage): + result.append((file, e.line, e.column, e.severity, e.message.value, e.allow_dups, e.code)) + else: + result.append((file, e.line, e.column, e.severity, e.message, e.allow_dups, e.code)) prev_import_context = e.import_ctx prev_function_or_member = e.function_or_member diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 3108d4602a8a..e31097bd833c 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -36,6 +36,7 @@ ) from mypy import defaults from mypy import message_registry, errorcodes as codes +from mypy.message_registry import ErrorMessage from mypy.errors import Errors from mypy.options import Options from mypy.reachability import mark_block_unreachable @@ -126,10 +127,6 @@ def ast3_parse(source: Union[str, bytes], filename: str, mode: str, MISSING_FALLBACK: Final = FakeInfo("fallback can't be filled out until semanal") _dummy_fallback: Final = Instance(MISSING_FALLBACK, [], -1) -TYPE_COMMENT_SYNTAX_ERROR: Final = "syntax error in type comment" - -INVALID_TYPE_IGNORE: Final = 'Invalid "type: ignore" comment' - TYPE_IGNORE_PATTERN = re.compile(r'[^#]*#\s*type:\s*ignore\s*(.*)') @@ -220,8 +217,8 @@ def parse_type_comment(type_comment: str, except SyntaxError: if errors is not None: stripped_type = type_comment.split("#", 2)[0].strip() - err_msg = '{} "{}"'.format(TYPE_COMMENT_SYNTAX_ERROR, stripped_type) - errors.report(line, column, err_msg, blocker=True, code=codes.SYNTAX) + err_msg = message_registry.TYPE_COMMENT_SYNTAX_ERROR_VALUE.format(stripped_type) + errors.report(line, column, err_msg.value, blocker=True, code=err_msg.code) return None, None else: raise @@ -233,7 +230,8 @@ def parse_type_comment(type_comment: str, ignored: Optional[List[str]] = parse_type_ignore_tag(tag) if ignored is None: if errors is not None: - errors.report(line, column, INVALID_TYPE_IGNORE, code=codes.SYNTAX) + err_msg = message_registry.INVALID_TYPE_IGNORE + errors.report(line, column, err_msg.value, code=err_msg.code) else: raise SyntaxError else: @@ -311,12 +309,12 @@ def note(self, msg: str, line: int, column: int) -> None: self.errors.report(line, column, msg, severity='note', code=codes.SYNTAX) def fail(self, - msg: str, + msg: ErrorMessage, line: int, column: int, blocker: bool = True) -> None: if blocker or not self.options.ignore_errors: - self.errors.report(line, column, msg, blocker=blocker, code=codes.SYNTAX) + self.errors.report(line, column, msg.value, blocker=blocker, code=msg.code) def visit(self, node: Optional[AST]) -> Any: if node is None: @@ -498,7 +496,7 @@ def visit_Module(self, mod: ast3.Module) -> MypyFile: if parsed is not None: self.type_ignores[ti.lineno] = parsed else: - self.fail(INVALID_TYPE_IGNORE, ti.lineno, -1) + self.fail(message_registry.INVALID_TYPE_IGNORE, ti.lineno, -1) body = self.fix_function_overloads(self.translate_stmt_list(mod.body, ismodule=True)) return MypyFile(body, self.imports, @@ -571,7 +569,7 @@ def do_func_def(self, n: Union[ast3.FunctionDef, ast3.AsyncFunctionDef], arg_types.insert(0, AnyType(TypeOfAny.special_form)) except SyntaxError: stripped_type = n.type_comment.split("#", 2)[0].strip() - err_msg = '{} "{}"'.format(TYPE_COMMENT_SYNTAX_ERROR, stripped_type) + err_msg = message_registry.TYPE_COMMENT_SYNTAX_ERROR_VALUE.format(stripped_type) self.fail(err_msg, lineno, n.col_offset) if n.type_comment and n.type_comment[0] not in ["(", "#"]: self.note('Suggestion: wrap argument types in parentheses', @@ -590,13 +588,12 @@ def do_func_def(self, n: Union[ast3.FunctionDef, ast3.AsyncFunctionDef], if any(arg_types) or return_type: if len(arg_types) != 1 and any(isinstance(t, EllipsisType) for t in arg_types): - self.fail("Ellipses cannot accompany other argument types " - "in function type signature", lineno, n.col_offset) + self.fail(message_registry.ELLIPSIS_WITH_OTHER_TYPEARGS, lineno, n.col_offset) elif len(arg_types) > len(arg_kinds): - self.fail('Type signature has too many arguments', lineno, n.col_offset, + self.fail(message_registry.TYPE_SIGNATURE_TOO_MANY_ARGS, lineno, n.col_offset, blocker=False) elif len(arg_types) < len(arg_kinds): - self.fail('Type signature has too few arguments', lineno, n.col_offset, + self.fail(message_registry.TYPE_SIGNATURE_TOO_FEW_ARGS, lineno, n.col_offset, blocker=False) else: func_type = CallableType([a if a is not None else @@ -724,7 +721,7 @@ def make_argument(self, arg: ast3.arg, default: Optional[ast3.expr], kind: ArgKi return Argument(Var(arg.arg), arg_type, self.visit(default), kind, pos_only) - def fail_arg(self, msg: str, arg: ast3.arg) -> None: + def fail_arg(self, msg: ErrorMessage, arg: ast3.arg) -> None: self.fail(msg, arg.lineno, arg.col_offset) # ClassDef(identifier name, @@ -1356,9 +1353,9 @@ def parent(self) -> Optional[AST]: return None return self.node_stack[-2] - def fail(self, msg: str, line: int, column: int) -> None: + def fail(self, msg: ErrorMessage, line: int, column: int) -> None: if self.errors: - self.errors.report(line, column, msg, blocker=True, code=codes.SYNTAX) + self.errors.report(line, column, msg.value, blocker=True, code=msg.code) def note(self, msg: str, line: int, column: int) -> None: if self.errors: @@ -1389,7 +1386,7 @@ def visit_Call(self, e: Call) -> Type: note = "Suggestion: use {0}[...] instead of {0}(...)".format(constructor) return self.invalid_type(e, note=note) if not constructor: - self.fail("Expected arg constructor name", e.lineno, e.col_offset) + self.fail(message_registry.ARG_CONSTRUCTOR_NAME_EXPECTED, e.lineno, e.col_offset) name: Optional[str] = None default_type = AnyType(TypeOfAny.special_form) @@ -1402,25 +1399,25 @@ def visit_Call(self, e: Call) -> Type: elif i == 1: name = self._extract_argument_name(arg) else: - self.fail("Too many arguments for argument constructor", + self.fail(message_registry.ARG_CONSTRUCTOR_TOO_MANY_ARGS, f.lineno, f.col_offset) for k in e.keywords: value = k.value if k.arg == "name": if name is not None: - self.fail('"{}" gets multiple values for keyword argument "name"'.format( + self.fail(message_registry.MULTIPLE_VALUES_FOR_NAME_KWARG.format( constructor), f.lineno, f.col_offset) name = self._extract_argument_name(value) elif k.arg == "type": if typ is not default_type: - self.fail('"{}" gets multiple values for keyword argument "type"'.format( + self.fail(message_registry.MULTIPLE_VALUES_FOR_TYPE_KWARG.format( constructor), f.lineno, f.col_offset) converted = self.visit(value) assert converted is not None typ = converted else: self.fail( - 'Unexpected argument "{}" for argument constructor'.format(k.arg), + message_registry.ARG_CONSTRUCTOR_UNEXPECTED_ARG.format(k.arg), value.lineno, value.col_offset) return CallableArgument(typ, name, constructor, e.lineno, e.col_offset) @@ -1432,7 +1429,7 @@ def _extract_argument_name(self, n: ast3.expr) -> Optional[str]: return n.s.strip() elif isinstance(n, NameConstant) and str(n.value) == 'None': return None - self.fail('Expected string literal for argument name, got {}'.format( + self.fail(message_registry.ARG_NAME_EXPECTED_STRING_LITERAL.format( type(n).__name__), self.line, 0) return None @@ -1559,12 +1556,12 @@ def visit_Subscript(self, n: ast3.Subscript) -> Type: if (isinstance(sliceval, ast3.Slice) or (isinstance(sliceval, ast3.Tuple) and any(isinstance(x, ast3.Slice) for x in sliceval.elts))): - self.fail(TYPE_COMMENT_SYNTAX_ERROR, self.line, getattr(n, 'col_offset', -1)) + self.fail(message_registry.TYPE_COMMENT_SYNTAX_ERROR, self.line, getattr(n, 'col_offset', -1)) return AnyType(TypeOfAny.from_error) else: # Python 3.8 or earlier use a different AST structure for subscripts if not isinstance(n.slice, Index): - self.fail(TYPE_COMMENT_SYNTAX_ERROR, self.line, getattr(n, 'col_offset', -1)) + self.fail(message_registry.TYPE_COMMENT_SYNTAX_ERROR, self.line, getattr(n, 'col_offset', -1)) return AnyType(TypeOfAny.from_error) sliceval = n.slice.value diff --git a/mypy/fastparse2.py b/mypy/fastparse2.py index bf5ec9663a78..a6b0eb641245 100644 --- a/mypy/fastparse2.py +++ b/mypy/fastparse2.py @@ -44,11 +44,12 @@ Type, CallableType, AnyType, UnboundType, EllipsisType, TypeOfAny, Instance, ProperType ) +from mypy.message_registry import ErrorMessage from mypy import message_registry, errorcodes as codes from mypy.errors import Errors from mypy.fastparse import ( TypeConverter, parse_type_comment, bytes_to_human_readable_repr, parse_type_ignore_tag, - TYPE_IGNORE_PATTERN, INVALID_TYPE_IGNORE + TYPE_IGNORE_PATTERN ) from mypy.options import Options from mypy.reachability import mark_block_unreachable @@ -175,9 +176,9 @@ def __init__(self, self.type_ignores: Dict[int, List[str]] = {} - def fail(self, msg: str, line: int, column: int, blocker: bool = True) -> None: + def fail(self, msg: ErrorMessage, line: int, column: int, blocker: bool = True) -> None: if blocker or not self.options.ignore_errors: - self.errors.report(line, column, msg, blocker=blocker, code=codes.SYNTAX) + self.errors.report(line, column, msg.value, blocker=blocker, code=msg.code) def visit(self, node: Optional[AST]) -> Any: # same as in typed_ast stub if node is None: @@ -351,7 +352,7 @@ def visit_Module(self, mod: ast27.Module) -> MypyFile: if parsed is not None: self.type_ignores[ti.lineno] = parsed else: - self.fail(INVALID_TYPE_IGNORE, ti.lineno, -1) + self.fail(message_registry.INVALID_TYPE_IGNORE, ti.lineno, -1) body = self.fix_function_overloads(self.translate_stmt_list(mod.body, module=True)) return MypyFile(body, self.imports, @@ -406,7 +407,7 @@ def visit_FunctionDef(self, n: ast27.FunctionDef) -> Statement: arg_types.insert(0, AnyType(TypeOfAny.special_form)) except SyntaxError: stripped_type = type_comment.split("#", 2)[0].strip() - err_msg = '{} "{}"'.format(TYPE_COMMENT_SYNTAX_ERROR, stripped_type) + err_msg = message_registry.TYPE_COMMENT_SYNTAX_ERROR_VALUE.format(stripped_type) self.fail(err_msg, lineno, n.col_offset) arg_types = [AnyType(TypeOfAny.from_error)] * len(args) return_type = AnyType(TypeOfAny.from_error) @@ -421,13 +422,12 @@ def visit_FunctionDef(self, n: ast27.FunctionDef) -> Statement: if any(arg_types) or return_type: if len(arg_types) != 1 and any(isinstance(t, EllipsisType) for t in arg_types): - self.fail("Ellipses cannot accompany other argument types " - "in function type signature", lineno, n.col_offset) + self.fail(message_registry.ELLIPSIS_WITH_OTHER_TYPEARGS, lineno, n.col_offset) elif len(arg_types) > len(arg_kinds): - self.fail('Type signature has too many arguments', lineno, n.col_offset, + self.fail(message_registry.TYPE_SIGNATURE_TOO_MANY_ARGS, lineno, n.col_offset, blocker=False) elif len(arg_types) < len(arg_kinds): - self.fail('Type signature has too few arguments', lineno, n.col_offset, + self.fail(message_registry.TYPE_SIGNATURE_TOO_FEW_ARGS, lineno, n.col_offset, blocker=False) else: any_type = AnyType(TypeOfAny.unannotated) @@ -524,7 +524,7 @@ def transform_args(self, arg.pos_only = True # We don't have any context object to give, but we have closed around the line num - def fail_arg(msg: str, arg: None) -> None: + def fail_arg(msg: ErrorMessage, arg: None) -> None: self.fail(msg, line, 0) check_arg_names(names, [None] * len(names), fail_arg) @@ -566,7 +566,7 @@ def get_type(self, tag: Optional[str] = cast(Any, extra_ignore).group(1) ignored = parse_type_ignore_tag(tag) if ignored is None: - self.fail(INVALID_TYPE_IGNORE, converter.line, -1) + self.fail(message_registry.INVALID_TYPE_IGNORE, converter.line, -1) else: self.type_ignores[converter.line] = ignored return typ @@ -703,7 +703,7 @@ def try_handler(self, elif isinstance(item.name, Name): vs.append(self.set_line(NameExpr(item.name.id), item)) else: - self.fail('Sorry, "except , " is not supported', + self.fail(message_registry.EXCEPT_EXPR_NOTNAME_UNSIPPORTED, item.lineno, item.col_offset) vs.append(None) types = [self.visit(h.type) for h in handlers] diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 48b53336f15d..23717cfaca74 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -6,36 +6,54 @@ add a method to MessageBuilder and call this instead. """ +from typing import NamedTuple, Optional from typing_extensions import Final +from mypy import errorcodes as codes + + +class ErrorMessage(NamedTuple): + value: str + code: Optional[codes.ErrorCode] = None + + def format(self, *args: object, **kwargs: object) -> "ErrorMessage": + return ErrorMessage(self.value.format(*args, **kwargs), code=self.code) + + # Invalid types -INVALID_TYPE_RAW_ENUM_VALUE: Final = "Invalid type: try using Literal[{}.{}] instead?" +INVALID_TYPE_RAW_ENUM_VALUE: Final = ErrorMessage( + "Invalid type: try using Literal[{}.{}] instead?" +) # Type checker error message constants -NO_RETURN_VALUE_EXPECTED: Final = "No return value expected" -MISSING_RETURN_STATEMENT: Final = "Missing return statement" -INVALID_IMPLICIT_RETURN: Final = "Implicit return in function which does not return" -INCOMPATIBLE_RETURN_VALUE_TYPE: Final = "Incompatible return value type" -RETURN_VALUE_EXPECTED: Final = "Return value expected" -NO_RETURN_EXPECTED: Final = "Return statement in function which does not return" -INVALID_EXCEPTION: Final = "Exception must be derived from BaseException" -INVALID_EXCEPTION_TYPE: Final = "Exception type must be derived from BaseException" -RETURN_IN_ASYNC_GENERATOR: Final = '"return" with value in async generator is not allowed' -INVALID_RETURN_TYPE_FOR_GENERATOR: Final = ( +NO_RETURN_VALUE_EXPECTED: Final = ErrorMessage("No return value expected", codes.RETURN_VALUE) +MISSING_RETURN_STATEMENT: Final = ErrorMessage("Missing return statement", codes.RETURN) +INVALID_IMPLICIT_RETURN: Final = ErrorMessage("Implicit return in function which does not return") +INCOMPATIBLE_RETURN_VALUE_TYPE: Final = ErrorMessage( + "Incompatible return value type", codes.RETURN_VALUE +) +RETURN_VALUE_EXPECTED: Final = ErrorMessage("Return value expected", codes.RETURN_VALUE) +NO_RETURN_EXPECTED: Final = ErrorMessage("Return statement in function which does not return") +INVALID_EXCEPTION: Final = ErrorMessage("Exception must be derived from BaseException") +INVALID_EXCEPTION_TYPE: Final = ErrorMessage("Exception type must be derived from BaseException") +RETURN_IN_ASYNC_GENERATOR: Final = ErrorMessage( + '"return" with value in async generator is not allowed' +) +INVALID_RETURN_TYPE_FOR_GENERATOR: Final = ErrorMessage( 'The return type of a generator function should be "Generator"' " or one of its supertypes" ) -INVALID_RETURN_TYPE_FOR_ASYNC_GENERATOR: Final = ( +INVALID_RETURN_TYPE_FOR_ASYNC_GENERATOR: Final = ErrorMessage( 'The return type of an async generator function should be "AsyncGenerator" or one of its ' "supertypes" ) -INVALID_GENERATOR_RETURN_ITEM_TYPE: Final = ( +INVALID_GENERATOR_RETURN_ITEM_TYPE: Final = ErrorMessage( "The return type of a generator function must be None in" " its third type parameter in Python 2" ) -YIELD_VALUE_EXPECTED: Final = "Yield value expected" -INCOMPATIBLE_TYPES: Final = "Incompatible types" -INCOMPATIBLE_TYPES_IN_ASSIGNMENT: Final = "Incompatible types in assignment" -INCOMPATIBLE_REDEFINITION: Final = "Incompatible redefinition" +YIELD_VALUE_EXPECTED: Final = ErrorMessage("Yield value expected") +INCOMPATIBLE_TYPES: Final = "Incompatible types" # TODO: make ErrorMessage +INCOMPATIBLE_TYPES_IN_ASSIGNMENT: Final = "Incompatible types in assignment" # TODO: ErrorMessage +INCOMPATIBLE_REDEFINITION: Final = ErrorMessage("Incompatible redefinition") INCOMPATIBLE_TYPES_IN_AWAIT: Final = 'Incompatible types in "await"' INCOMPATIBLE_TYPES_IN_ASYNC_WITH_AENTER: Final = ( 'Incompatible types in "async with" for "__aenter__"' @@ -48,112 +66,1015 @@ INCOMPATIBLE_TYPES_IN_YIELD: Final = 'Incompatible types in "yield"' INCOMPATIBLE_TYPES_IN_YIELD_FROM: Final = 'Incompatible types in "yield from"' INCOMPATIBLE_TYPES_IN_STR_INTERPOLATION: Final = "Incompatible types in string interpolation" -MUST_HAVE_NONE_RETURN_TYPE: Final = 'The return type of "{}" must be None' +MUST_HAVE_NONE_RETURN_TYPE: Final = ErrorMessage('The return type of "{}" must be None') INVALID_TUPLE_INDEX_TYPE: Final = "Invalid tuple index type" -TUPLE_INDEX_OUT_OF_RANGE: Final = "Tuple index out of range" +TUPLE_INDEX_OUT_OF_RANGE: Final = ErrorMessage("Tuple index out of range") INVALID_SLICE_INDEX: Final = "Slice index must be an integer or None" -CANNOT_INFER_LAMBDA_TYPE: Final = "Cannot infer type of lambda" -CANNOT_ACCESS_INIT: Final = 'Cannot access "__init__" directly' -NON_INSTANCE_NEW_TYPE: Final = '"__new__" must return a class instance (got {})' -INVALID_NEW_TYPE: Final = 'Incompatible return type for "__new__"' -BAD_CONSTRUCTOR_TYPE: Final = "Unsupported decorated constructor type" -CANNOT_ASSIGN_TO_METHOD: Final = "Cannot assign to a method" -CANNOT_ASSIGN_TO_TYPE: Final = "Cannot assign to a type" -INCONSISTENT_ABSTRACT_OVERLOAD: Final = ( +CANNOT_INFER_LAMBDA_TYPE: Final = ErrorMessage("Cannot infer type of lambda") +CANNOT_ACCESS_INIT: Final = ErrorMessage('Cannot access "__init__" directly') +NON_INSTANCE_NEW_TYPE: Final = ErrorMessage('"__new__" must return a class instance (got {})') +INVALID_NEW_TYPE: Final = ErrorMessage('Incompatible return type for "__new__"') +BAD_CONSTRUCTOR_TYPE: Final = ErrorMessage("Unsupported decorated constructor type") +CANNOT_ASSIGN_TO_METHOD: Final = ErrorMessage("Cannot assign to a method", codes.ASSIGNMENT) +CANNOT_ASSIGN_TO_TYPE: Final = ErrorMessage("Cannot assign to a type") +INCONSISTENT_ABSTRACT_OVERLOAD: Final = ErrorMessage( "Overloaded method has both abstract and non-abstract variants" ) -MULTIPLE_OVERLOADS_REQUIRED: Final = "Single overload definition, multiple required" -READ_ONLY_PROPERTY_OVERRIDES_READ_WRITE: Final = ( +MULTIPLE_OVERLOADS_REQUIRED: Final = ErrorMessage("Single overload definition, multiple required") +READ_ONLY_PROPERTY_OVERRIDES_READ_WRITE: Final = ErrorMessage( "Read-only property cannot override read-write property" ) FORMAT_REQUIRES_MAPPING: Final = "Format requires a mapping" -RETURN_TYPE_CANNOT_BE_CONTRAVARIANT: Final = ( +RETURN_TYPE_CANNOT_BE_CONTRAVARIANT: Final = ErrorMessage( "Cannot use a contravariant type variable as return type" ) -FUNCTION_PARAMETER_CANNOT_BE_COVARIANT: Final = ( +FUNCTION_PARAMETER_CANNOT_BE_COVARIANT: Final = ErrorMessage( "Cannot use a covariant type variable as a parameter" ) INCOMPATIBLE_IMPORT_OF: Final = "Incompatible import of" -FUNCTION_TYPE_EXPECTED: Final = "Function is missing a type annotation" -ONLY_CLASS_APPLICATION: Final = "Type application is only supported for generic classes" -RETURN_TYPE_EXPECTED: Final = "Function is missing a return type annotation" -ARGUMENT_TYPE_EXPECTED: Final = "Function is missing a type annotation for one or more arguments" -KEYWORD_ARGUMENT_REQUIRES_STR_KEY_TYPE: Final = ( +FUNCTION_TYPE_EXPECTED: Final = ErrorMessage( + "Function is missing a type annotation", codes.NO_UNTYPED_DEF +) +ONLY_CLASS_APPLICATION: Final = ErrorMessage( + "Type application is only supported for generic classes" +) +RETURN_TYPE_EXPECTED: Final = ErrorMessage( + "Function is missing a return type annotation", codes.NO_UNTYPED_DEF +) +ARGUMENT_TYPE_EXPECTED: Final = ErrorMessage( + "Function is missing a type annotation for one or more arguments", codes.NO_UNTYPED_DEF +) +KEYWORD_ARGUMENT_REQUIRES_STR_KEY_TYPE: Final = ErrorMessage( 'Keyword argument only valid with "str" key type in call to "dict"' ) -ALL_MUST_BE_SEQ_STR: Final = "Type of __all__ must be {}, not {}" -INVALID_TYPEDDICT_ARGS: Final = ( +ALL_MUST_BE_SEQ_STR: Final = ErrorMessage("Type of __all__ must be {}, not {}") +INVALID_TYPEDDICT_ARGS: Final = ErrorMessage( "Expected keyword arguments, {...}, or dict(...) in TypedDict constructor" ) -TYPEDDICT_KEY_MUST_BE_STRING_LITERAL: Final = "Expected TypedDict key to be string literal" -MALFORMED_ASSERT: Final = "Assertion is always true, perhaps remove parentheses?" -DUPLICATE_TYPE_SIGNATURES: Final = "Function has duplicate type signatures" -DESCRIPTOR_SET_NOT_CALLABLE: Final = "{}.__set__ is not callable" -DESCRIPTOR_GET_NOT_CALLABLE: Final = "{}.__get__ is not callable" -MODULE_LEVEL_GETATTRIBUTE: Final = "__getattribute__ is not valid at the module level" +TYPEDDICT_KEY_MUST_BE_STRING_LITERAL: Final = ErrorMessage( + "Expected TypedDict key to be string literal" +) +MALFORMED_ASSERT: Final = ErrorMessage("Assertion is always true, perhaps remove parentheses?") +DUPLICATE_TYPE_SIGNATURES: Final = ErrorMessage( + "Function has duplicate type signatures", codes.SYNTAX +) +DESCRIPTOR_SET_NOT_CALLABLE: Final = ErrorMessage("{}.__set__ is not callable") +DESCRIPTOR_GET_NOT_CALLABLE: Final = ErrorMessage("{}.__get__ is not callable") +MODULE_LEVEL_GETATTRIBUTE: Final = ErrorMessage( + "__getattribute__ is not valid at the module level" +) # Generic -GENERIC_INSTANCE_VAR_CLASS_ACCESS: Final = ( +GENERIC_INSTANCE_VAR_CLASS_ACCESS: Final = ErrorMessage( "Access to generic instance variables via class is ambiguous" ) -GENERIC_CLASS_VAR_ACCESS: Final = "Access to generic class variables is ambiguous" -BARE_GENERIC: Final = "Missing type parameters for generic type {}" -IMPLICIT_GENERIC_ANY_BUILTIN: Final = ( - 'Implicit generic "Any". Use "{}" and specify generic parameters' +GENERIC_CLASS_VAR_ACCESS: Final = ErrorMessage("Access to generic class variables is ambiguous") +BARE_GENERIC: Final = ErrorMessage("Missing type parameters for generic type {}", codes.TYPE_ARG) +IMPLICIT_GENERIC_ANY_BUILTIN: Final = ErrorMessage( + 'Implicit generic "Any". Use "{}" and specify generic parameters', codes.TYPE_ARG ) # TypeVar -INCOMPATIBLE_TYPEVAR_VALUE: Final = 'Value of type variable "{}" of {} cannot be {}' -CANNOT_USE_TYPEVAR_AS_EXPRESSION: Final = 'Type variable "{}.{}" cannot be used as an expression' +INCOMPATIBLE_TYPEVAR_VALUE: Final = ErrorMessage( + 'Value of type variable "{}" of {} cannot be {}', codes.TYPE_VAR +) +CANNOT_USE_TYPEVAR_AS_EXPRESSION: Final = ErrorMessage( + 'Type variable "{}.{}" cannot be used as an expression' +) # Super -TOO_MANY_ARGS_FOR_SUPER: Final = 'Too many arguments for "super"' -TOO_FEW_ARGS_FOR_SUPER: Final = 'Too few arguments for "super"' -SUPER_WITH_SINGLE_ARG_NOT_SUPPORTED: Final = '"super" with a single argument not supported' -UNSUPPORTED_ARG_1_FOR_SUPER: Final = 'Unsupported argument 1 for "super"' -UNSUPPORTED_ARG_2_FOR_SUPER: Final = 'Unsupported argument 2 for "super"' -SUPER_VARARGS_NOT_SUPPORTED: Final = 'Varargs not supported with "super"' -SUPER_POSITIONAL_ARGS_REQUIRED: Final = '"super" only accepts positional arguments' -SUPER_ARG_2_NOT_INSTANCE_OF_ARG_1: Final = 'Argument 2 for "super" not an instance of argument 1' -TARGET_CLASS_HAS_NO_BASE_CLASS: Final = "Target class has no base class" -SUPER_OUTSIDE_OF_METHOD_NOT_SUPPORTED: Final = "super() outside of a method is not supported" -SUPER_ENCLOSING_POSITIONAL_ARGS_REQUIRED: Final = ( +TOO_MANY_ARGS_FOR_SUPER: Final = ErrorMessage('Too many arguments for "super"') +TOO_FEW_ARGS_FOR_SUPER: Final = ErrorMessage('Too few arguments for "super"', codes.CALL_ARG) +SUPER_WITH_SINGLE_ARG_NOT_SUPPORTED: Final = ErrorMessage( + '"super" with a single argument not supported' +) +UNSUPPORTED_ARG_1_FOR_SUPER: Final = ErrorMessage('Unsupported argument 1 for "super"') +UNSUPPORTED_ARG_2_FOR_SUPER: Final = ErrorMessage('Unsupported argument 2 for "super"') +SUPER_VARARGS_NOT_SUPPORTED: Final = ErrorMessage('Varargs not supported with "super"') +SUPER_POSITIONAL_ARGS_REQUIRED: Final = ErrorMessage('"super" only accepts positional arguments') +SUPER_ARG_2_NOT_INSTANCE_OF_ARG_1: Final = ErrorMessage( + 'Argument 2 for "super" not an instance of argument 1' +) +TARGET_CLASS_HAS_NO_BASE_CLASS: Final = ErrorMessage("Target class has no base class") +SUPER_OUTSIDE_OF_METHOD_NOT_SUPPORTED: Final = ErrorMessage( + "super() outside of a method is not supported" +) +SUPER_ENCLOSING_POSITIONAL_ARGS_REQUIRED: Final = ErrorMessage( "super() requires one or more positional arguments in enclosing function" ) # Self-type -MISSING_OR_INVALID_SELF_TYPE: Final = ( +MISSING_OR_INVALID_SELF_TYPE: Final = ErrorMessage( "Self argument missing for a non-static method (or an invalid type for self)" ) -ERASED_SELF_TYPE_NOT_SUPERTYPE: Final = ( +ERASED_SELF_TYPE_NOT_SUPERTYPE: Final = ErrorMessage( 'The erased type of self "{}" is not a supertype of its class "{}"' ) -INVALID_SELF_TYPE_OR_EXTRA_ARG: Final = ( +INVALID_SELF_TYPE_OR_EXTRA_ARG: Final = ErrorMessage( "Invalid type for self, or extra argument type in function annotation" ) # Final -CANNOT_INHERIT_FROM_FINAL: Final = 'Cannot inherit from final class "{}"' -DEPENDENT_FINAL_IN_CLASS_BODY: Final = ( +CANNOT_INHERIT_FROM_FINAL: Final = ErrorMessage('Cannot inherit from final class "{}"') +DEPENDENT_FINAL_IN_CLASS_BODY: Final = ErrorMessage( "Final name declared in class body cannot depend on type variables" ) -CANNOT_ACCESS_FINAL_INSTANCE_ATTR: Final = ( +CANNOT_ACCESS_FINAL_INSTANCE_ATTR: Final = ErrorMessage( 'Cannot access final instance attribute "{}" on class object' ) -CANNOT_MAKE_DELETABLE_FINAL: Final = "Deletable attribute cannot be final" +CANNOT_MAKE_DELETABLE_FINAL: Final = ErrorMessage("Deletable attribute cannot be final") # ClassVar -CANNOT_OVERRIDE_INSTANCE_VAR: Final = ( +CANNOT_OVERRIDE_INSTANCE_VAR: Final = ErrorMessage( 'Cannot override instance variable (previously declared on base class "{}") with class ' "variable" ) -CANNOT_OVERRIDE_CLASS_VAR: Final = ( +CANNOT_OVERRIDE_CLASS_VAR: Final = ErrorMessage( 'Cannot override class variable (previously declared on base class "{}") with instance ' "variable" ) # Protocol -RUNTIME_PROTOCOL_EXPECTED: Final = ( +RUNTIME_PROTOCOL_EXPECTED: Final = ErrorMessage( "Only @runtime_checkable protocols can be used with instance and class checks" ) -CANNOT_INSTANTIATE_PROTOCOL: Final = 'Cannot instantiate protocol class "{}"' +CANNOT_INSTANTIATE_PROTOCOL: Final = ErrorMessage('Cannot instantiate protocol class "{}"') + +CONTIGUOUS_ITERABLE_EXPECTED: Final = ErrorMessage("Contiguous iterable with same type expected") +ITERABLE_TYPE_EXPECTED: Final = ErrorMessage("Invalid type '{}' for *expr (iterable expected)") +TYPE_GUARD_POS_ARG_REQUIRED: Final = ErrorMessage("Type guard requires positional argument") +TOO_MANY_UNION_COMBINATIONS: Final = ErrorMessage( + "Not all union combinations were tried because there are too many unions" +) + +# Type Analysis +TYPEANAL_INTERNAL_ERROR: Final = ErrorMessage("Internal error (node is None, kind={})") +NOT_SUBSCRIPTABLE: Final = ErrorMessage('"{}" is not subscriptable') +NOT_SUBSCRIPTABLE_REPLACEMENT: Final = ErrorMessage('"{}" is not subscriptable, use "{}" instead') +PARAMSPEC_UNBOUND: Final = ErrorMessage('ParamSpec "{}" is unbound') +PARAMSPEC_INVALID_LOCATION: Final = ErrorMessage('Invalid location for ParamSpec "{}"') +NO_BOUND_TYPEVAR_GENERIC_ALIAS: Final = ErrorMessage( + 'Can\'t use bound type variable "{}" to define generic alias' +) +TYPEVAR_USED_WITH_ARGS: Final = ErrorMessage('Type variable "{}" used with arguments') +ONLY_OUTERMOST_FINAL: Final = ErrorMessage( + "Final can be only used as an outermost qualifier in a variable annotation" +) +BUILTIN_TUPLE_NOT_DEFINED: Final = ErrorMessage('Name "tuple" is not defined') +SINGLE_TYPE_ARG: Final = ErrorMessage("{} must have exactly one type argument") +INVALID_NESTED_CLASSVAR: Final = ErrorMessage("Invalid type: ClassVar nested inside other type") +CLASSVAR_ATMOST_ONE_TYPE_ARG: Final = ErrorMessage( + "ClassVar[...] must have at most one type argument" +) +ANNOTATED_SINGLE_TYPE_ARG: Final = ErrorMessage( + "Annotated[...] must have exactly one type argument and at least one annotation" +) +GENERIC_TUPLE_UNSUPPORTED: Final = ErrorMessage("Generic tuple types not supported") +GENERIC_TYPED_DICT_UNSUPPORTED: Final = ErrorMessage("Generic TypedDict types not supported") +VARIABLE_NOT_VALID_TYPE: Final = ErrorMessage( + 'Variable "{}" is not valid as a type', codes.VALID_TYPE +) +FUNCTION_NOT_VALID_TYPE: Final = ErrorMessage( + 'Function "{}" is not valid as a type', codes.VALID_TYPE +) +MODULE_NOT_VALID_TYPE: Final = ErrorMessage('Module "{}" is not valid as a type', codes.VALID_TYPE) +UNBOUND_TYPEVAR: Final = ErrorMessage('Type variable "{}" is unbound', codes.VALID_TYPE) +CANNOT_INTERPRET_AS_TYPE: Final = ErrorMessage( + 'Cannot interpret reference "{}" as a type', codes.VALID_TYPE +) +INVALID_TYPE: Final = ErrorMessage("Invalid type") +BRACKETED_EXPR_INVALID_TYPE: Final = ErrorMessage( + 'Bracketed expression "[...]" is not valid as a type' +) +ANNOTATION_SYNTAX_ERROR: Final = ErrorMessage("Syntax error in type annotation", codes.SYNTAX) +TUPLE_SINGLE_STAR_TYPE: Final = ErrorMessage("At most one star type allowed in a tuple") +INVALID_TYPE_USE_LITERAL: Final = ErrorMessage( + "Invalid type: try using Literal[{}] instead?", codes.VALID_TYPE +) +INVALID_LITERAL_TYPE: Final = ErrorMessage( + "Invalid type: {} literals cannot be used as a type", codes.VALID_TYPE +) +INVALID_ANNOTATION: Final = ErrorMessage("Invalid type comment or annotation", codes.VALID_TYPE) +PIPE_UNION_REQUIRES_PY310: Final = ErrorMessage("X | Y syntax for unions requires Python 3.10") +UNEXPECTED_ELLIPSIS: Final = ErrorMessage('Unexpected "..."') +CALLABLE_INVALID_FIRST_ARG: Final = ErrorMessage( + 'The first argument to Callable must be a list of types or "..."' +) +CALLABLE_INVALID_ARGS: Final = ErrorMessage( + 'Please use "Callable[[], ]" or "Callable"' +) +INVALID_ARG_CONSTRUCTOR: Final = ErrorMessage('Invalid argument constructor "{}"') +ARGS_SHOULD_NOT_HAVE_NAMES: Final = ErrorMessage("{} arguments should not have names") +LITERAL_AT_LEAST_ONE_ARG: Final = ErrorMessage("Literal[...] must have at least one parameter") +LITERAL_INDEX_CANNOT_BE_ANY: Final = ErrorMessage( + 'Parameter {} of Literal[...] cannot be of type "Any"' +) +LITERAL_INDEX_INVALID_TYPE: Final = ErrorMessage( + 'Parameter {} of Literal[...] cannot be of type "{}"' +) +LITERAL_INVALID_EXPRESSION: Final = ErrorMessage( + "Invalid type: Literal[...] cannot contain arbitrary expressions" +) +LITERAL_INVALID_PARAMETER: Final = ErrorMessage("Parameter {} of Literal[...] is invalid") +TYPEVAR_BOUND_BY_OUTER_CLASS: Final = ErrorMessage('Type variable "{}" is bound by an outer class') +TYPE_ARG_COUNT_MISMATCH: Final = ErrorMessage('"{}" expects {}, but {} given', codes.TYPE_ARG) +TYPE_ALIAS_ARG_COUNT_MISMATCH: Final = ErrorMessage( + "Bad number of arguments for type alias, expected: {}, given: {}" +) +INVALID_TYPE_ALIAS: Final = ErrorMessage("Invalid type alias: expression is not a valid type") + +# function definitions, from nodes.py +DUPLICATE_ARGUMENT_IN_X: Final = ErrorMessage('Duplicate argument "{}" in {}') +POS_ARGS_BEFORE_DEFAULT_NAMED_OR_VARARGS: Final = ErrorMessage( + "Required positional args may not appear after default, named or var args" +) +DEFAULT_ARGS_BEFORE_NAMED_OR_VARARGS: Final = ErrorMessage( + "Positional default args may not appear after named or var args" +) +VAR_ARGS_BEFORE_NAMED_OR_VARARGS: Final = ErrorMessage( + "Var args may not appear after named or var args" +) +KWARGS_MUST_BE_LAST: Final = ErrorMessage("A **kwargs argument must be the last argument") +MULTIPLE_KWARGS: Final = ErrorMessage("You may only have one **kwargs argument") + +# from type_anal_hook.py +INVALID_SIGNAL_TYPE: Final = ErrorMessage('Invalid "Signal" type (expected "Signal[[t, ...]]")') + +# NewType +NEWTYPE_USED_WITH_PROTOCOL: Final = ErrorMessage("NewType cannot be used with protocol classes") +NEWTYPE_ARG_MUST_BE_SUBCLASSABLE: Final = ErrorMessage( + "Argument 2 to NewType(...) must be subclassable (got {})", codes.VALID_NEWTYPE +) +CANNOT_DECLARE_TYPE_OF_NEWTYPE: Final = ErrorMessage( + "Cannot declare the type of a NewType declaration" +) +CANNOT_REDEFINE_AS_NEWTYPE: Final = ErrorMessage('Cannot redefine "{}" as a NewType') +NEWTYPE_EXPECTS_TWO_ARGS: Final = ErrorMessage( + "NewType(...) expects exactly two positional arguments" +) +NEWTYPE_ARG_STRING_LITERAL: Final = ErrorMessage( + "Argument 1 to NewType(...) must be a string literal" +) +NEWTYPE_ARG_VARNAME_MISMATCH: Final = ErrorMessage( + 'String argument 1 "{}" to NewType(...) does not match variable name "{}"' +) +NEWTYPE_ARG_INVALID_TYPE: Final = ErrorMessage("Argument 2 to NewType(...) must be a valid type") + +# TypedDict +TYPEDDICT_BASES_MUST_BE_TYPEDDICTS: Final = ErrorMessage( + "All bases of a new TypedDict must be TypedDict types" +) +TYPEDDICT_OVERWRITE_FIELD_IN_MERGE: Final = ErrorMessage( + 'Overwriting TypedDict field "{}" while merging' +) +TYPEDDICT_OVERWRITE_FIELD_IN_EXTEND: Final = ErrorMessage( + 'Overwriting TypedDict field "{}" while extending' +) +TYPEDDICT_CLASS_ERROR: Final = ErrorMessage( + "Invalid statement in TypedDict definition; " 'expected "field_name: field_type"' +) +TYPEDDICT_ARG_NAME_MISMATCH: Final = ErrorMessage( + 'First argument "{}" to TypedDict() does not match variable name "{}"', codes.NAME_MATCH +) +TYPEDDICT_TOO_FEW_ARGS: Final = ErrorMessage("Too few arguments for TypedDict()") +TYPEDDICT_TOO_MANY_ARGS: Final = ErrorMessage("Too many arguments for TypedDict()") +TYPEDDICT_UNEXPECTED_ARGS: Final = ErrorMessage("Unexpected arguments to TypedDict()") +TYPEDDICT_CALL_UNEXPECTED_KWARG: Final = ErrorMessage( + 'Unexpected keyword argument "{}" for "TypedDict"' +) +TYPEDDICT_CALL_EXPECTED_STRING_LITERAL: Final = ErrorMessage( + "TypedDict() expects a string literal as the first argument" +) +TYPEDDICT_CALL_EXPECTED_DICT: Final = ErrorMessage( + "TypedDict() expects a dictionary literal as the second argument" +) +TYPEDDICT_RHS_VALUE_UNSUPPORTED: Final = ErrorMessage( + "Right hand side values are not supported in TypedDict" +) +TYPEDDICT_TOTAL_MUST_BE_BOOL: Final = ErrorMessage( + 'TypedDict() "total" argument must be True or False' +) +TYPEDDICT_TOTAL_MUST_BE_BOOL_2: Final = ErrorMessage('Value of "total" must be True or False') +TYPEDDICT_DUPLICATE_KEY: Final = ErrorMessage('Duplicate TypedDict key "{}"') +TYPEDDICT_INVALID_FIELD_NAME: Final = ErrorMessage("Invalid TypedDict() field name") +TYPEDDICT_INVALID_FIELD_TYPE: Final = ErrorMessage("Invalid field type") + +# Enum +ENUM_ATTRIBUTE_UNSUPPORTED: Final = ErrorMessage("Enum type as attribute is not supported") +ENUM_CALL_UNEXPECTED_ARGS: Final = ErrorMessage("Unexpected arguments to {}()") +ENUM_CALL_UNEXPECTED_KWARG: Final = ErrorMessage('Unexpected keyword argument "{}"') +ENUM_CALL_TOO_MANY_ARGS: Final = ErrorMessage("Too many arguments for {}()") +ENUM_CALL_TOO_FEW_ARGS: Final = ErrorMessage("Too few arguments for {}()") +ENUM_CALL_EXPECTED_STRING_LITERAL: Final = ErrorMessage( + "{}() expects a string literal as the first argument" +) +ENUM_CALL_EXPECTED_STRINGS_OR_PAIRS: Final = ErrorMessage( + "{}() with tuple or list expects strings or (name, value) pairs" +) +ENUM_CALL_DICT_EXPECTED_STRING_KEYS: Final = ErrorMessage( + "{}() with dict literal requires string literals" +) +ENUM_CALL_EXPECTED_LITERAL: Final = ErrorMessage( + "{}() expects a string, tuple, list or dict literal as the second argument" +) +ENUM_CALL_ATLEAST_ONE_ITEM: Final = ErrorMessage("{}() needs at least one item") + +# NamedTuple +NAMEDTUPLE_SUPPORTED_ABOVE_PY36: Final = ErrorMessage( + "NamedTuple class syntax is only supported in Python 3.6" +) +NAMEDTUPLE_SINGLE_BASE: Final = ErrorMessage("NamedTuple should be a single base") +NAMEDTUPLE_CLASS_ERROR: Final = ErrorMessage( + "Invalid statement in NamedTuple definition; " 'expected "field_name: field_type [= default]"' +) +NAMEDTUPLE_FIELD_NO_UNDERSCORE: Final = ErrorMessage( + "NamedTuple field name cannot start with an underscore: {}" +) +NAMEDTUPLE_FIELD_DEFAULT_AFTER_NONDEFAULT: Final = ErrorMessage( + "Non-default NamedTuple fields cannot follow default fields" +) +NAMEDTUPLE_TOO_FEW_ARGS: Final = ErrorMessage("Too few arguments for namedtuple()") +NAMEDTUPLE_TOO_MANY_ARGS: Final = ErrorMessage("Too many arguments for namedtuple()") +NAMEDTUPLE_EXPECTED_LIST_TUPLE_DEFAULTS: Final = ErrorMessage( + "List or tuple literal expected as the defaults argument to namedtuple()" +) +NAMEDTUPLE_UNEXPECTED_ARGS: Final = ErrorMessage("Unexpected arguments to namedtuple()") +NAMEDTUPLE_ARG_EXPECTED_STRING_LITERAL: Final = ErrorMessage( + "namedtuple() expects a string literal as the first argument" +) +NAMEDTUPLE_ARG_EXPECTED_LIST_TUPLE: Final = ErrorMessage( + "List or tuple literal expected as the second argument to namedtuple()" +) +NAMEDTUPLE_EXPECTED_STRING_LITERAL: Final = ErrorMessage( + "String literal expected as namedtuple() item" +) +NAMEDTUPLE_FIELDS_NO_UNDERSCORE: Final = ErrorMessage( + "namedtuple() field names cannot start with an underscore: {}" +) +NAMEDTUPLE_TOO_MANY_DEFAULTS: Final = ErrorMessage( + "Too many defaults given in call to namedtuple()" +) +NAMEDTUPLE_INVALID_FIELD_DEFINITION: Final = ErrorMessage("Invalid NamedTuple field definition") +NAMEDTUPLE_INVALID_FIELD_NAME: Final = ErrorMessage("Invalid NamedTuple() field name") +NAMEDTUPLE_INVALID_FIELD_TYPE: Final = ErrorMessage("Invalid field type") +NAMEDTUPLE_TUPLE_EXPECTED: Final = ErrorMessage("Tuple expected as NamedTuple() field") +NAMEDTUPLE_CANNOT_OVERWRITE_ATTRIBUTE: Final = ErrorMessage( + 'Cannot overwrite NamedTuple attribute "{}"' +) + +# TypeArgs +TYPEVAR_INVALID_TYPE_ARG: Final = ErrorMessage( + 'Type variable "{}" not valid as type argument value for "{}"', codes.TYPE_VAR +) +TYPE_ARG_INVALID_SUBTYPE: Final = ErrorMessage( + 'Type argument "{}" of "{}" must be a subtype of "{}"', codes.TYPE_VAR +) +TYPE_ARG_INVALID_VALUE: Final = ErrorMessage( + 'Invalid type argument value for "{}"', codes.TYPE_VAR +) + +# FastParse +TYPE_COMMENT_SYNTAX_ERROR: Final = ErrorMessage("syntax error in type comment", codes.SYNTAX) +TYPE_COMMENT_SYNTAX_ERROR_VALUE: Final = ErrorMessage( + 'syntax error in type comment "{}"', codes.SYNTAX +) +INVALID_TYPE_IGNORE: Final = ErrorMessage('Invalid "type: ignore" comment', codes.SYNTAX) +ELLIPSIS_WITH_OTHER_TYPEARGS: Final = ErrorMessage( + "Ellipses cannot accompany other argument types in function type signature", codes.SYNTAX +) +TYPE_SIGNATURE_TOO_MANY_ARGS: Final = ErrorMessage( + "Type signature has too many arguments", codes.SYNTAX +) +TYPE_SIGNATURE_TOO_FEW_ARGS: Final = ErrorMessage( + "Type signature has too few arguments", codes.SYNTAX +) +ARG_CONSTRUCTOR_NAME_EXPECTED: Final = ErrorMessage("Expected arg constructor name", codes.SYNTAX) +ARG_CONSTRUCTOR_TOO_MANY_ARGS: Final = ErrorMessage( + "Too many arguments for argument constructor", codes.SYNTAX +) +MULTIPLE_VALUES_FOR_NAME_KWARG: Final = ErrorMessage( + '"{}" gets multiple values for keyword argument "name"', codes.SYNTAX +) +MULTIPLE_VALUES_FOR_TYPE_KWARG: Final = ErrorMessage( + '"{}" gets multiple values for keyword argument "type"', codes.SYNTAX +) +ARG_CONSTRUCTOR_UNEXPECTED_ARG: Final = ErrorMessage( + 'Unexpected argument "{}" for argument constructor', codes.SYNTAX +) +ARG_NAME_EXPECTED_STRING_LITERAL: Final = ErrorMessage( + "Expected string literal for argument name, got {}", codes.SYNTAX +) +EXCEPT_EXPR_NOTNAME_UNSIPPORTED: Final = ErrorMessage( + 'Sorry, "except , " is not supported', codes.SYNTAX +) + +# strings from messages.py +MEMBER_NOT_ASSIGNABLE: Final = ErrorMessage('Member "{}" is not assignable') +UNSUPPORTED_OPERAND_FOR_IN: Final = ErrorMessage( + "Unsupported right operand type for in ({})", codes.OPERATOR +) +UNSUPPORTED_OPERAND_FOR_UNARY_MINUS: Final = ErrorMessage( + "Unsupported operand type for unary - ({})", codes.OPERATOR +) +UNSUPPORTED_OPERAND_FOR_UNARY_PLUS: Final = ErrorMessage( + "Unsupported operand type for unary + ({})", codes.OPERATOR +) +UNSUPPORTED_OPERAND_FOR_INVERT: Final = ErrorMessage( + "Unsupported operand type for ~ ({})", codes.OPERATOR +) +TYPE_NOT_GENERIC_OR_INDEXABLE: Final = ErrorMessage("The type {} is not generic and not indexable") +TYPE_NOT_INDEXABLE: Final = ErrorMessage("Value of type {} is not indexable", codes.INDEX) +UNSUPPORTED_TARGET_INDEXED_ASSIGNMENT: Final = ErrorMessage( + "Unsupported target for indexed assignment ({})", codes.INDEX +) +CALLING_FUNCTION_OF_UNKNOWN_TYPE: Final = ErrorMessage( + "Cannot call function of unknown type", codes.OPERATOR +) +TYPE_NOT_CALLABLE: Final = ErrorMessage("{} not callable", codes.OPERATOR) +TYPE_NOT_CALLABLE_2: Final = ErrorMessage("{} not callable") +TYPE_HAS_NO_ATTRIBUTE_X_MAYBE_Y: Final = ErrorMessage( + '{} has no attribute "{}"; maybe {}?{}', codes.ATTR_DEFINED +) +TYPE_HAS_NO_ATTRIBUTE_X: Final = ErrorMessage('{} has no attribute "{}"{}', codes.ATTR_DEFINED) +ITEM_HAS_NO_ATTRIBUTE_X: Final = ErrorMessage( + 'Item {} of {} has no attribute "{}"{}', codes.UNION_ATTR +) +UNSUPPORTED_OPERANDS_LIKELY_UNION: Final = ErrorMessage( + "Unsupported operand types for {} (likely involving Union)", codes.OPERATOR +) +UNSUPPORTED_OPERANDS: Final = ErrorMessage( + "Unsupported operand types for {} ({} and {})", codes.OPERATOR +) +UNSUPPORTED_LEFT_OPERAND_TYPE_UNION: Final = ErrorMessage( + "Unsupported left operand type for {} (some union)", codes.OPERATOR +) +UNSUPPORTED_LEFT_OPERAND_TYPE: Final = ErrorMessage( + "Unsupported left operand type for {} ({})", codes.OPERATOR +) +UNTYPED_FUNCTION_CALL: Final = ErrorMessage( + "Call to untyped function {} in typed context", codes.NO_UNTYPED_CALL +) +INVALID_INDEX_TYPE: Final = ErrorMessage( + "Invalid index type {} for {}; expected type {}", codes.INDEX +) +TARGET_INCOMPATIBLE_TYPE: Final = ErrorMessage( + "{} (expression has type {}, target has type {})", codes.ASSIGNMENT +) +LIST_ITEM_INCOMPATIBLE_TYPE: Final = ErrorMessage( + "{} item {} has incompatible type {}; expected {}", codes.LIST_ITEM +) +DICT_ENTRY_INCOMPATIBLE_TYPE: Final = ErrorMessage( + "{} entry {} has incompatible type {}: {}; expected {}: {}", codes.DICT_ITEM +) +LIST_COMP_INCOMPATIBLE_TYPE: Final = ErrorMessage( + "List comprehension has incompatible type List[{}]; expected List[{}]" +) +SET_COMP_INCOMPATIBLE_TYPE: Final = ErrorMessage( + "Set comprehension has incompatible type Set[{}]; expected Set[{}]" +) +DICT_COMP_INCOMPATIBLE_TYPE: Final = ErrorMessage( + "{} expression in dictionary comprehension has incompatible type {}; expected type {}" +) +GENERATOR_INCOMPATIBLE_TYPE: Final = ErrorMessage( + "Generator has incompatible item type {}; expected {}" +) +MULTIPLE_VALUES_FOR_KWARG: Final = ErrorMessage( + '{} gets multiple values for keyword argument "{}"' +) +NO_RETURN_VALUE: Final = ErrorMessage("{} does not return a value", codes.FUNC_RETURNS_VALUE) +FUNCTION_NO_RETURN_VALUE: Final = ErrorMessage( + "Function does not return a value", codes.FUNC_RETURNS_VALUE +) +UNDERSCORE_FUNCTION_CALL: Final = ErrorMessage('Calling function named "_" is not allowed') +READING_DELETED_VALUE: Final = ErrorMessage("Trying to read deleted variable{}") +ASSIGNMENT_OUTSIDE_EXCEPT: Final = ErrorMessage("Assignment to variable{} outside except: block") +OVERLOADS_REQUIRE_ATLEAST_ONE_ARG: Final = ErrorMessage( + "All overload variants{} require at least one argument" +) +UNPACK_MORE_THAN_ONE_VALUE_NEEDED: Final = ErrorMessage( + "Need more than 1 value to unpack ({} expected)" +) +UNPACK_TOO_FEW_VALUES: Final = ErrorMessage("Need more than {} values to unpack ({} expected)") +UNPACK_TOO_MANY_VALUES: Final = ErrorMessage( + "Too many values to unpack ({} expected, {} provided)" +) +UNPACKING_STRINGS_DISALLOWED: Final = ErrorMessage("Unpacking a string is disallowed") +TYPE_NOT_ITERABLE: Final = ErrorMessage('"{}" object is not iterable') +INCOMPATIBLE_OPERATOR_ASSIGNMENT: Final = ErrorMessage( + "Result type of {} incompatible in assignment" +) +OVERLOAD_SIGNATURE_INCOMPATIBLE: Final = ErrorMessage( + 'Signature of "{}" incompatible with {}', codes.OVERRIDE +) +SIGNATURE_INCOMPATIBLE_WITH_SUPERTYPE: Final = ErrorMessage( + 'Signature of "{}" incompatible with {}', codes.OVERRIDE +) +ARG_INCOMPATIBLE_WITH_SUPERTYPE: Final = ErrorMessage( + 'Argument {} of "{}" is incompatible with {}; supertype defines the argument type as "{}"', + codes.OVERRIDE, +) +RETURNTYPE_INCOMPATIBLE_WITH_SUPERTYPE: Final = ErrorMessage( + 'Return type {} of "{}" incompatible with return type {} in {}', codes.OVERRIDE +) +TYPE_APPLICATION_ON_NON_GENERIC_TYPE: Final = ErrorMessage( + "Type application targets a non-generic function or class" +) +TYPE_APPLICATION_TOO_MANY_TYPES: Final = ErrorMessage( + "Type application has too many types ({} expected)" +) +TYPE_APPLICATION_TOO_FEW_TYPES: Final = ErrorMessage( + "Type application has too few types ({} expected)" +) +CANNOT_INFER_TYPE_ARG_NAMED_FUNC: Final = ErrorMessage("Cannot infer type argument {} of {}") +CANNOT_INFER_TYPE_ARG_FUNC: Final = ErrorMessage("Cannot infer function type argument") +INVALID_VAR_ARGS: Final = ErrorMessage("List or tuple expected as variable arguments") +KEYWORDS_MUST_BE_STRINGS: Final = ErrorMessage("Keywords must be strings") +ARG_MUST_BE_MAPPING: Final = ErrorMessage("Argument after ** must be a mapping{}", codes.ARG_TYPE) +MEMBER_UNDEFINED_IN_SUPERCLASS: Final = ErrorMessage('"{}" undefined in superclass') +SUPER_ARG_EXPECTED_TYPE: Final = ErrorMessage( + 'Argument 1 for "super" must be a type object; got {}', codes.ARG_TYPE +) +FORMAT_STR_TOO_FEW_ARGS: Final = ErrorMessage( + "Not enough arguments for format string", codes.STRING_FORMATTING +) +FORMAT_STR_TOO_MANY_ARGS: Final = ErrorMessage( + "Not all arguments converted during string formatting", codes.STRING_FORMATTING +) +FORMAT_STR_UNSUPPORTED_CHAR: Final = ErrorMessage( + 'Unsupported format character "{}"', codes.STRING_FORMATTING +) +STRING_INTERPOLATION_WITH_STAR_AND_KEY: Final = ErrorMessage( + "String interpolation contains both stars and mapping keys", codes.STRING_FORMATTING +) +FORMAT_STR_INVALID_CHR_CONVERSION_RANGE: Final = ErrorMessage( + '"{}c" requires an integer in range(256) or a single byte', codes.STRING_FORMATTING +) +FORMAT_STR_INVALID_CHR_CONVERSION: Final = ErrorMessage( + '"{}c" requires int or char', codes.STRING_FORMATTING +) +KEY_NOT_IN_MAPPING: Final = ErrorMessage('Key "{}" not found in mapping', codes.STRING_FORMATTING) +FORMAT_STR_MIXED_KEYS_AND_NON_KEYS: Final = ErrorMessage( + "String interpolation mixes specifier with and without mapping keys", codes.STRING_FORMATTING +) +CANNOT_DETERMINE_TYPE: Final = ErrorMessage('Cannot determine type of "{}"', codes.HAS_TYPE) +CANNOT_DETERMINE_TYPE_IN_BASE: Final = ErrorMessage( + 'Cannot determine type of "{}" in base class "{}"' +) +DOES_NOT_ACCEPT_SELF: Final = ErrorMessage( + 'Attribute function "{}" with type {} does not accept self argument' +) +INCOMPATIBLE_SELF_ARG: Final = ErrorMessage('Invalid self argument {} to {} "{}" with type {}') +INCOMPATIBLE_CONDITIONAL_FUNCS: Final = ErrorMessage( + "All conditional function variants must have identical signatures" +) +CANNOT_INSTANTIATE_ABSTRACT_CLASS: Final = ErrorMessage( + 'Cannot instantiate abstract class "{}" with abstract attribute{} {}', codes.ABSTRACT +) +INCOMPATIBLE_BASE_CLASS_DEFNS: Final = ErrorMessage( + 'Definition of "{}" in base class "{}" is incompatible with definition in base class "{}"' +) +CANNOT_ASSIGN_TO_CLASSVAR: Final = ErrorMessage( + 'Cannot assign to class variable "{}" via instance' +) +CANNOT_OVERRIDE_TO_FINAL: Final = ErrorMessage( + 'Cannot override writable attribute "{}" with a final one' +) +CANNOT_OVERRIDE_FINAL: Final = ErrorMessage( + 'Cannot override final attribute "{}" (previously declared in base class "{}")' +) +CANNOT_ASSIGN_TO_FINAL: Final = ErrorMessage('Cannot assign to final {} "{}"') +PROTOCOL_MEMBER_CANNOT_BE_FINAL: Final = ErrorMessage("Protocol member cannot be final") +FINAL_WITHOUT_VALUE: Final = ErrorMessage("Final name must be initialized with a value") +PROPERTY_IS_READ_ONLY: Final = ErrorMessage('Property "{}" defined in "{}" is read-only') +NON_OVERLAPPING_COMPARISON: Final = ErrorMessage( + "Non-overlapping {} check ({} type: {}, {} type: {})", codes.COMPARISON_OVERLAP +) +OVERLOAD_INCONSISTENT_DECORATOR_USE: Final = ErrorMessage( + 'Overload does not consistently use the "@{}" decorator on all function signatures.' +) +OVERLOAD_INCOMPATIBLE_RETURN_TYPES: Final = ErrorMessage( + "Overloaded function signatures {} and {} overlap with incompatible return types" +) +OVERLOAD_SIGNATURE_WILL_NEVER_MATCH: Final = ErrorMessage( + "Overloaded function signature {index2} will never be matched: signature {index1}'s parameter type(s) are the same or broader" +) +OVERLOAD_INCONSISTENT_TYPEVARS: Final = ErrorMessage( + "Overloaded function implementation cannot satisfy signature {} due to inconsistencies in how they use type variables" +) +OVERLOAD_INCONSISTENT_ARGS: Final = ErrorMessage( + "Overloaded function implementation does not accept all possible arguments of signature {}" +) +OVERLOAD_INCONSISTENT_RETURN_TYPE: Final = ErrorMessage( + "Overloaded function implementation cannot produce return type of signature {}" +) +OPERATOR_METHOD_SIGNATURE_OVERLAP: Final = ErrorMessage( + 'Signatures of "{}" of "{}" and "{}" of {} are unsafely overlapping' +) +FORWARD_OPERATOR_NOT_CALLABLE: Final = ErrorMessage('Forward operator "{}" is not callable') +INCOMPATIBLE_SIGNATURES: Final = ErrorMessage('Signatures of "{}" and "{}" are incompatible') +INVALID_YIELD_FROM: Final = ErrorMessage('"yield from" can\'t be applied to {}') +INVALID_SIGNATURE: Final = ErrorMessage('Invalid signature "{}"') +INVALID_SIGNATURE_SPECIAL: Final = ErrorMessage('Invalid signature "{}" for "{}"') +UNSUPPORTED_TYPE_TYPE: Final = ErrorMessage('Cannot instantiate type "Type[{}]"') +REDUNDANT_CAST: Final = ErrorMessage("Redundant cast to {}", codes.REDUNDANT_CAST) +UNFOLLOWED_IMPORT: Final = ErrorMessage( + "{} becomes {} due to an unfollowed import", codes.NO_ANY_UNIMPORTED +) +ANNOTATION_NEEDED: Final = ErrorMessage('Need type {} for "{}"{}', codes.VAR_ANNOTATED) +NO_EXPLICIT_ANY: Final = ErrorMessage('Explicit "Any" is not allowed') +TYPEDDICT_MISSING_KEYS: Final = ErrorMessage("Missing {} for TypedDict {}", codes.TYPEDDICT_ITEM) +TYPEDDICT_EXTRA_KEYS: Final = ErrorMessage("Extra {} for TypedDict {}", codes.TYPEDDICT_ITEM) +TYPEDDICT_UNEXPECTED_KEYS: Final = ErrorMessage("Unexpected TypedDict {}") +TYPEDDICT_KEYS_MISMATCH: Final = ErrorMessage("Expected {} but found {}", codes.TYPEDDICT_ITEM) +TYPEDDICT_KEY_STRING_LITERAL_EXPECTED: Final = ErrorMessage( + "TypedDict key must be a string literal; expected one of {}" +) +TYPEDDICT_KEY_INVALID: Final = ErrorMessage( + '"{}" is not a valid TypedDict key; expected one of {}' +) +TYPEDDICT_UNKNOWN_KEY: Final = ErrorMessage('TypedDict {} has no key "{}"', codes.TYPEDDICT_ITEM) +TYPEDDICT_AMBIGUOUS_TYPE: Final = ErrorMessage( + "Type of TypedDict is ambiguous, could be any of ({})" +) +TYPEDDICT_CANNOT_DELETE_KEY: Final = ErrorMessage('TypedDict key "{}" cannot be deleted') +TYPEDDICT_NAMED_CANNOT_DELETE_KEY: Final = ErrorMessage( + 'Key "{}" of TypedDict {} cannot be deleted' +) +TYPEDDICT_INCONSISTENT_SETDEFAULT_ARGS: Final = ErrorMessage( + 'Argument 2 to "setdefault" of "TypedDict" has incompatible type {}; expected {}', + codes.TYPEDDICT_ITEM, +) +PARAMETERIZED_GENERICS_DISALLOWED: Final = ErrorMessage( + "Parameterized generics cannot be used with class or instance checks" +) +EXPR_HAS_ANY_TYPE: Final = ErrorMessage('Expression has type "Any"') +EXPR_CONTAINS_ANY_TYPE: Final = ErrorMessage('Expression type contains "Any" (has type {})') +INCORRECTLY_RETURNING_ANY: Final = ErrorMessage( + "Returning Any from function declared to return {}", codes.NO_ANY_RETURN +) +INVALID_EXIT_RETURN_TYPE: Final = ErrorMessage( + '"bool" is invalid as return type for "__exit__" that always returns False', codes.EXIT_RETURN +) +UNTYPED_DECORATOR_FUNCTION: Final = ErrorMessage( + "Function is untyped after decorator transformation" +) +DECORATED_TYPE_CONTAINS_ANY: Final = ErrorMessage( + 'Type of decorated function contains type "Any" ({})' +) +DECORATOR_MAKES_FUNCTION_UNTYPED: Final = ErrorMessage( + 'Untyped decorator makes function "{}" untyped' +) +CONCRETE_ONLY_ASSIGN: Final = ErrorMessage( + "Can only assign concrete classes to a variable of type {}" +) +EXPECTED_CONCRETE_CLASS: Final = ErrorMessage( + "Only concrete class can be given where {} is expected" +) +CANNOT_USE_FUNCTION_WITH_TYPE: Final = ErrorMessage("Cannot use {}() with {} type") +ISSUBCLASS_ONLY_NON_METHOD_PROTOCOL: Final = ErrorMessage( + "Only protocols that don't have non-method members can be used with issubclass()" +) +UNREACHABLE_STATEMENT: Final = ErrorMessage("Statement is unreachable", codes.UNREACHABLE) +UNREACHABLE_RIGHT_OPERAND: Final = ErrorMessage( + 'Right operand of "{}" is never evaluated', codes.UNREACHABLE +) +EXPR_IS_ALWAYS_BOOL: Final = ErrorMessage("{} is always {}", codes.REDUNDANT_EXPR) +IMPOSSIBLE_SUBCLASS: Final = ErrorMessage("Subclass of {} cannot exist: would have {}") + +# String formatting checks +FORMAT_STR_INVALID_SPECIFIER: Final = ErrorMessage( + "Invalid conversion specifier in format string", codes.STRING_FORMATTING +) +FORMAT_STR_BRACES_IN_SPECIFIER: Final = ErrorMessage( + "Conversion value must not contain { or }", codes.STRING_FORMATTING +) +FORMAT_STR_NESTING_ATMOST_TWO_LEVELS: Final = ErrorMessage( + "Formatting nesting must be at most two levels deep", codes.STRING_FORMATTING +) +FORMAT_STR_UNEXPECTED_RBRACE: Final = ErrorMessage( + "Invalid conversion specifier in format string: unexpected }", codes.STRING_FORMATTING +) +FORMAT_STR_UNMATCHED_LBRACE: Final = ErrorMessage( + "Invalid conversion specifier in format string: unmatched {", codes.STRING_FORMATTING +) +UNRECOGNIZED_FORMAT_SPEC: Final = ErrorMessage( + 'Unrecognized format specification "{}"', codes.STRING_FORMATTING +) +FORMAT_STR_INVALID_CONVERSION_TYPE: Final = ErrorMessage( + 'Invalid conversion type "{}", must be one of "r", "s" or "a"', codes.STRING_FORMATTING +) +FORMAT_STR_BYTES_USE_REPR: Final = ErrorMessage( + "On Python 3 '{}'.format(b'abc') produces \"b'abc'\", not 'abc'; use '{!r}'.format(b'abc') if this is desired behavior", + codes.STR_BYTES_PY3, +) +FORMAT_STR_BYTES_USE_REPR_OLD: Final = ErrorMessage( + "On Python 3 '%s' % b'abc' produces \"b'abc'\", not 'abc'; use '%r' % b'abc' if this is desired behavior", + codes.STR_BYTES_PY3, +) +FORMAT_STR_INVALID_NUMERIC_FLAG: Final = ErrorMessage( + "Numeric flags are only allowed for numeric types", codes.STRING_FORMATTING +) +FORMAT_STR_REPLACEMENT_NOT_FOUND: Final = ErrorMessage( + "Cannot find replacement for positional format specifier {}", codes.STRING_FORMATTING +) +FORMAT_STR_NAMED_REPLACEMENT_NOT_FOUND: Final = ErrorMessage( + 'Cannot find replacement for named format specifier "{}"', codes.STRING_FORMATTING +) +FORMAT_STR_PARTIAL_FIELD_NUMBERING: Final = ErrorMessage( + "Cannot combine automatic field numbering and manual field specification", + codes.STRING_FORMATTING, +) +FORMAT_STR_SYNTAX_ERROR: Final = ErrorMessage( + 'Syntax error in format specifier "{}"', codes.STRING_FORMATTING +) +FORMAT_STR_INVALID_ACCESSOR_EXPR: Final = ErrorMessage( + 'Only index and member expressions are allowed in format field accessors; got "{}"', + codes.STRING_FORMATTING, +) +FORMAT_STR_INVALID_INDEX_ACCESSOR: Final = ErrorMessage( + 'Invalid index expression in format field accessor "{}"', codes.STRING_FORMATTING +) +FORMAT_STR_BYTES_ABOVE_PY35: Final = ErrorMessage( + "Bytes formatting is only supported in Python 3.5 and later", codes.STRING_FORMATTING +) +FORMAT_STR_BYTES_DICT_KEYS_MUST_BE_BYTES: Final = ErrorMessage( + "Dictionary keys in bytes formatting must be bytes, not strings", codes.STRING_FORMATTING +) +FORMAT_STR_BYTES_REQUIRED_PY3: Final = ErrorMessage( + "On Python 3 b'%s' requires bytes, not string", codes.STRING_FORMATTING +) +FORMAT_STR_INVALID_BYTES_SPECIFIER_PY35: Final = ErrorMessage( + 'Format character "b" is only supported in Python 3.5 and later', codes.STRING_FORMATTING +) +FORMAT_STR_INVALID_BYTES_SPECIFIER: Final = ErrorMessage( + 'Format character "b" is only supported on bytes patterns', codes.STRING_FORMATTING +) +FORMAT_STR_ASCII_SPECIFIER_PY3: Final = ErrorMessage( + 'Format character "a" is only supported in Python 3', codes.STRING_FORMATTING +) + +# semanal.py +METHOD_ATLEAST_ONE_ARG: Final = ErrorMessage("Method must have at least one argument") +OVERLOAD_IMPLEMENTATION_IN_STUB: Final = ErrorMessage( + "An implementation for an overloaded function is not allowed in a stub file" +) +OVERLOAD_IMPLEMENTATION_LAST: Final = ErrorMessage( + "The implementation for an overloaded function must come last" +) +OVERLOAD_IMPLEMENTATION_REQUIRED: Final = ErrorMessage( + "An overloaded function outside a stub file must have an implementation" +) +FINAL_DEC_ON_OVERLOAD_ONLY: Final = ErrorMessage( + "@final should be applied only to overload implementation" +) +FINAL_DEC_STUB_FIRST_OVERLOAD: Final = ErrorMessage( + "In a stub file @final must be applied only to the first overload" +) +DECORATED_PROPERTY_UNSUPPORTED: Final = ErrorMessage("Decorated property not supported") +UNEXPECTED_PROPERTY_DEFN: Final = ErrorMessage('Unexpected definition for property "{}"') +TOO_MANY_ARGS: Final = ErrorMessage("Too many arguments") +FINAL_DEC_WITH_METHODS_ONLY: Final = ErrorMessage( + "@final cannot be used with non-method functions" +) +DECORATOR_USED_WITH_NON_METHOD: Final = ErrorMessage('"{}" used with a non-method') +CANNOT_USE_FINAL_DEC_WITH_TYPEDDICT: Final = ErrorMessage("@final cannot be used with TypedDict") +RUNTIME_CHECKABLE_WITH_NON_PROPERTY: Final = ErrorMessage( + "@runtime_checkable can only be used with protocol classes" +) +BASES_MUST_HAVE_SINGLE_GENERIC_OR_PROTOCOL: Final = ErrorMessage( + "Only single Generic[...] or Protocol[...] can be in bases" +) +DUPLICATE_TYPEVARS_IN_GENERIC_OR_PROTOCOL: Final = ErrorMessage( + "Duplicate type variables in Generic[...] or Protocol[...]" +) +GENERIC_PROTOCOL_NOT_ALL_TYPEVARS: Final = ErrorMessage( + "If Generic[...] or Protocol[...] is present it should list all type variables" +) +FREE_TYPEVAR_EXPECTED: Final = ErrorMessage("Free type variable expected in {}[...]") +UNSUPPORTED_DYNAMIC_BASE_CLASS: Final = ErrorMessage("Unsupported dynamic base class{}") +INVALID_BASE_CLASS: Final = ErrorMessage("Invalid base class{}") +CANNOT_SUBCLASS_NEWTYPE: Final = ErrorMessage('Cannot subclass "NewType"') +CANNOT_SUBCLASS_ANY_NAMED: Final = ErrorMessage('Class cannot subclass "{}" (has type "Any")') +CANNOT_SUBCLASS_ANY: Final = ErrorMessage('Class cannot subclass value of type "Any"') +INCOMPATIBLE_BASES: Final = ErrorMessage("Class has two incompatible bases derived from tuple") +CANNOT_DETERMINE_MRO: Final = ErrorMessage( + 'Cannot determine consistent method resolution order (MRO) for "{}"' +) +INNER_METACLASS_UNSUPPORTED: Final = ErrorMessage( + "Metaclasses defined as inner classes are not supported" +) +MULTIPLE_METACLASSES: Final = ErrorMessage("Multiple metaclass definitions") +INHERITANCE_CYCLE: Final = ErrorMessage("Cycle in inheritance hierarchy") +NAMED_INVALID_BASE_CLASS: Final = ErrorMessage('"{}" is not a valid base class') +DUPLICATE_BASE_CLASS: Final = ErrorMessage('Duplicate base class "{}"') +UNSUPPORTED_NAMED_DYNAMIC_BASE_CLASS: Final = ErrorMessage( + 'Dynamic metaclass not supported for "{}"' +) +INVALID_METACLASS: Final = ErrorMessage('Invalid metaclass "{}"') +METACLASS_MUST_INHERIT_TYPE: Final = ErrorMessage( + 'Metaclasses not inheriting from "type" are not supported' +) +INCONSISTENT_METACLAS_STRUCTURE: Final = ErrorMessage('Inconsistent metaclass structure for "{}"') +NO_GENERIC_ENUM: Final = ErrorMessage("Enum class cannot be generic") +MODULE_MISSING_ATTIRBUTE: Final = ErrorMessage( + 'Module "{}" has no attribute "{}"{}', codes.ATTR_DEFINED +) +NO_IMPLICIT_REEXPORT: Final = ErrorMessage( + 'Module "{}" does not explicitly export attribute "{}"; implicit reexport disabled' +) +INCORRECT_RELATIVE_IMPORT: Final = ErrorMessage("Relative import climbs too many namespaces") +INVALID_TYPE_ALIAS_TARGET: Final = ErrorMessage( + 'Type variable "{}" is invalid as target for type alias' +) +NAMEDTUPLE_ATTRIBUTE_UNSUPPORTED: Final = ErrorMessage( + "NamedTuple type as an attribute is not supported" +) +NAMEDTUPLE_INCORRECT_FIRST_ARG: Final = ErrorMessage( + 'First argument to namedtuple() should be "{}", not "{}"', codes.NAME_MATCH +) +TYPEDDICT_ATTRIBUTE_UNSUPPORTED: Final = ErrorMessage( + "TypedDict type as attribute is not supported" +) +FINAL_ATMOST_ONE_ARG: Final = ErrorMessage("Final[...] takes at most one type argument") +FINAL_INITIALIZER_REQUIRED: Final = ErrorMessage( + "Type in Final[...] can only be omitted if there is an initializer" +) +FINAL_CLASSVAR_DISALLOWED: Final = ErrorMessage( + "Variable should not be annotated with both ClassVar and Final" +) +INVALID_FINAL: Final = ErrorMessage("Invalid final declaration") +FINAL_IN_LOOP_DISALLOWED: Final = ErrorMessage("Cannot use Final inside a loop") +FINAL_ONLY_ON_SELF_MEMBER: Final = ErrorMessage( + "Final can be only applied to a name or an attribute on self" +) +FINAL_ONLY_IN_CLASS_BODY_OR_INIT: Final = ErrorMessage( + "Can only declare a final attribute in class body or __init__" +) +PROTOCOL_MEMBERS_MUST_BE_TYPED: Final = ErrorMessage( + "All protocol members must have explicitly declared types" +) +MULTIPLE_TYPES_WITHOUT_EXPLICIT_TYPE: Final = ErrorMessage( + 'Cannot assign multiple types to name "{}" without an explicit "Type[...]" annotation' +) +TYPE_DECLARATION_IN_ASSIGNMENT: Final = ErrorMessage( + "Type cannot be declared in assignment to non-self attribute" +) +UNEXPECTED_TYPE_DECLARATION: Final = ErrorMessage("Unexpected type declaration") +STAR_ASSIGNMENT_TARGET_LIST_OR_TUPLE: Final = ErrorMessage( + "Starred assignment target must be in a list or tuple" +) +INVALID_ASSIGNMENT_TARGET: Final = ErrorMessage("Invalid assignment target") +REDEFINE_AS_FINAL: Final = ErrorMessage("Cannot redefine an existing name as final") +TWO_STAR_EXPRESSIONS_IN_ASSIGNMENT: Final = ErrorMessage("Two starred expressions in assignment") +PROTOCOL_ASSIGNMENT_TO_SELF: Final = ErrorMessage( + "Protocol members cannot be defined via assignment to self" +) +STARTYPE_ONLY_FOR_STAR_EXPRESSIONS: Final = ErrorMessage( + "Star type only allowed for starred expressions" +) +INCOMPATIBLE_TUPLE_ITEM_COUNT: Final = ErrorMessage("Incompatible number of tuple items") +TUPLE_TYPE_EXPECTED: Final = ErrorMessage("Tuple type expected for multiple variables") +CANNOT_DECLARE_TYPE_OF_TYPEVAR: Final = ErrorMessage("Cannot declare the type of a type variable") +REDEFINE_AS_TYPEVAR: Final = ErrorMessage('Cannot redefine "{}" as a type variable') +TYPEVAR_CALL_TOO_FEW_ARGS: Final = ErrorMessage("Too few arguments for {}()") +TYPEVAR_CALL_EXPECTED_STRING_LITERAL: Final = ErrorMessage( + "{}() expects a string literal as first argument" +) +TYPEVAR_NAME_ARG_MISMATCH: Final = ErrorMessage( + 'String argument 1 "{}" to {}(...) does not match variable name "{}"' +) +TYPEVAR_UNEXPECTED_ARG: Final = ErrorMessage("Unexpected argument to TypeVar()") +TYPEVAR_UNEXPECTED_ARG_NAMED: Final = ErrorMessage('Unexpected argument to TypeVar(): "{}"') +TYPEVAR_COVARIANT_MUST_BE_TRUE: Final = ErrorMessage("TypeVar 'covariant' may only be 'True'") +TYPEVAR_CONTRAVARIANT_MUST_BE_TRUE: Final = ErrorMessage( + "TypeVar 'contravariant' may only be 'True'" +) +TYPEVAR_VALUE_WITH_BOUND_DISALLOWED: Final = ErrorMessage( + "TypeVar cannot have both values and an upper bound" +) +TYPEVAR_BOUND_MUST_BE_TYPE: Final = ErrorMessage('TypeVar "bound" must be a type') +TYPEVAR_VALUES_ARG_UNSUPPORTED: Final = ErrorMessage('TypeVar "values" argument not supported') +USE_NEW_TYPEVAR_SYNTAX: Final = ErrorMessage( + "Use TypeVar('T', t, ...) instead of TypeVar('T', values=(t, ...))" +) +TYPEVAR_COVARIANT_AND_CONTRAVARIANT: Final = ErrorMessage( + "TypeVar cannot be both covariant and contravariant" +) +TYPEVAR_SINGLE_CONSTRAINT: Final = ErrorMessage("TypeVar cannot have only a single constraint") +CANNOT_DECLARE_TYPE_OF_PARAMSPEC: Final = ErrorMessage( + "Cannot declare the type of a parameter specification" +) +TYPE_EXPECTED: Final = ErrorMessage("Type expected") +CLASSVAR_OUTSIDE_CLASS_BODY: Final = ErrorMessage( + "ClassVar can only be used for assignments in class body" +) +MULTIPLE_MODULE_ASSIGNMENT: Final = ErrorMessage( + 'Cannot assign multiple modules to name "{}" without explicit "types.ModuleType" annotation' +) +DELETABLE_MUST_BE_WITH_LIST_OR_TUPLE: Final = ErrorMessage( + '"__deletable__" must be initialized with a list or tuple expression' +) +DELETABLE_EXPECTED_STRING_LITERAL: Final = ErrorMessage( + 'Invalid "__deletable__" item; string literal expected' +) +RETURN_OUTSIDE_FUNCTION: Final = ErrorMessage('"return" outside function') +BREAK_OUTSIDE_LOOP: Final = ErrorMessage('"break" outside loop') +CONTINUE_OUTSIDE_LOOP: Final = ErrorMessage('"continue" outside loop') +WITH_HAS_NO_TARGETS: Final = ErrorMessage('Invalid type comment: "with" statement has no targets') +WITH_INCOMPATIBLE_TARGET_COUNT: Final = ErrorMessage( + 'Incompatible number of types for "with" targets' +) +WITH_MULTIPLE_TYPES_EXPECTED: Final = ErrorMessage( + 'Multiple types expected for multiple "with" targets' +) +INVALID_DELETE_TARGET: Final = ErrorMessage("Invalid delete target") +NAME_IS_NONLOCAL_AND_GLOBAL: Final = ErrorMessage('Name "{}" is nonlocal and global') +NONLOCAL_AT_MODULE_LEVEL: Final = ErrorMessage("nonlocal declaration not allowed at module level") +NONLOCAL_NO_BINDING_FOUND: Final = ErrorMessage('No binding for nonlocal "{}" found') +LOCAL_DEFINITION_BEFORE_NONLOCAL: Final = ErrorMessage( + 'Name "{}" is already defined in local scope before nonlocal declaration' +) +NAME_ONLY_VALID_IN_TYPE_CONTEXT: Final = ErrorMessage( + '"{}" is a type variable and only valid in type context' +) +SUPER_OUTSIDE_CLASS: Final = ErrorMessage('"super" used outside class') +INVALID_STAR_EXPRESSION: Final = ErrorMessage( + "Can use starred expression only as assignment target" +) +YIELD_OUTSIDE_FUNC: Final = ErrorMessage('"yield" outside function') +YIELD_FROM_OUTSIDE_FUNC: Final = ErrorMessage('"yield from" outside function') +YIELD_IN_ASYNC_FUNC: Final = ErrorMessage('"yield" in async function') +YIELD_FROM_IN_ASYNC_FUNC: Final = ErrorMessage('"yield from" in async function') +CAST_TARGET_IS_NOT_TYPE: Final = ErrorMessage("Cast target is not a type") +ANY_CALL_UNSUPPORTED: Final = ErrorMessage( + "Any(...) is no longer supported. Use cast(Any, ...) instead" +) +PROMOTE_ARG_EXPECTED_TYPE: Final = ErrorMessage("Argument 1 to _promote is not a type") +ARG_COUNT_MISMATCH: Final = ErrorMessage('"{}" expects {} argument{}') +POS_ARG_COUNT_MISMATCH: Final = ErrorMessage('"{}" must be called with {} positional argument{}') +TYPE_EXPECTED_IN_BRACKETS: Final = ErrorMessage("Type expected within [...]") +AWAIT_OUTSIDE_FUNC: Final = ErrorMessage('"await" outside function') +AWAIT_OUTSIDE_COROUTINE: Final = ErrorMessage('"await" outside coroutine ("async def")') +CANNOT_RESOLVE_NAME: Final = ErrorMessage('Cannot resolve {} "{}" (possible cyclic definition)') +NONCONSECUTIVE_OVERLOAD_FOUND: Final = ErrorMessage("Nonconsecutive overload {} found") +DEFINITION_MISSING_OVERLOAD: Final = ErrorMessage("Definition of '{}' missing 'overload'") +NAME_NOT_DEFINED: Final = ErrorMessage('Name "{}" is not defined', codes.NAME_DEFINED) +NAME_ALREADY_DEFINED: Final = ErrorMessage('{} "{}" already defined{}', codes.NO_REDEF) + +# proper_plugin +ISINSTANCE_ON_UNEXPANDED_TYPE: Final = ErrorMessage( + "Never apply isinstance() to unexpanded types; use mypy.types.get_proper_type() first" +) +REDUNDANT_GET_PROPER_TYPE: Final = ErrorMessage("Redundant call to get_proper_type()") + +# attrs +CANNOT_DETERMINE_INIT_TYPE: Final = ErrorMessage("Cannot determine __init__ type from converter") +CMP_WITH_EQ_AND_ORDER: Final = ErrorMessage('Don\'t mix "cmp" with "eq" and "order"') +EQ_TRUE_IF_ORDER_TRUE: Final = ErrorMessage("eq must be True if order is True") +ARG_MUST_BE_TRUE_OR_FALSE: Final = ErrorMessage('"{}" argument must be True or False.') +AUTO_ATTRIBS_UNSUPPORTED_PY2: Final = ErrorMessage("auto_attribs is not supported in Python 2") +ATTRS_NEWSTYLE_CLASS_ONLY: Final = ErrorMessage("attrs only works with new-style classes") +KW_ONLY_UNSUPPORTED_PY2: Final = ErrorMessage("kw_only is not supported in Python 2") +DEFAULT_WITH_FACTORY: Final = ErrorMessage('Can\'t pass both "default" and "factory".') +TYPE_INVALID_ARG: Final = ErrorMessage("Invalid argument to type") +NON_DEFAULT_ATTRS_AFTER_DEFAULT: Final = ErrorMessage( + "Non-default attributes not allowed after default attributes." +) +ATTR_TOO_MANY_NAMES: Final = ErrorMessage("Too many names for one attribute") +CONVERT_WITH_CONVERTER: Final = ErrorMessage('Can\'t pass both "convert" and "converter".') +CONVERT_DEPRECATED: Final = ErrorMessage("convert is deprecated, use converter") +UNSUPPORTED_CONVERTER: Final = ErrorMessage( + "Unsupported converter, only named functions and types are currently supported" +) + +# plugins +TOTAL_ORDERING_NO_OPERATOR_DEFINED: Final = ErrorMessage( + 'No ordering operation defined when using "functools.total_ordering": < > <= >=' +) +DATACLASS_ORDER_METHODS_DISALLOWED: Final = ErrorMessage( + "You may not have a custom {} method when order=True" +) +DATACLASS_DEFAULT_ATTRS_BEFORE_NON_DEFAULT: Final = ErrorMessage( + "Attributes without a default cannot follow attributes with one" +) +DATACLASS_SINGLE_KW_ONLY_TYPE: Final = ErrorMessage( + "There may not be more than one field with the KW_ONLY type" +) +CTYPES_VALUE_WITH_CHAR_OR_WCHAR_ONLY: Final = ErrorMessage( + 'Array attribute "value" is only available with element type "c_char" or "c_wchar", not {}' +) +CTYPES_RAW_WITH_CHAR_ONLY: Final = ErrorMessage( + 'Array attribute "raw" is only available with element type "c_char", not {}' +) +CTYPES_INCOMPATIBLE_CONSTRUCTOR_ARG: Final = ErrorMessage( + "Array constructor argument {} of type {} is not convertible to the array element type {}" +) +SINGLEDISPATCH_ATLEAST_ONE_ARG: Final = ErrorMessage( + "Singledispatch function requires at least one argument" +) +SINGLEDISPATCH_FIRST_ARG_POSITIONAL: Final = ErrorMessage( + "First argument to singledispatch function must be a positional argument" +) +DISPATCH_TYPE_FALLBACK_SUBTYPE: Final = ErrorMessage( + "Dispatch type {} must be subtype of fallback function first argument {}" +) diff --git a/mypy/messages.py b/mypy/messages.py index 8c316ed56b6b..d0d1d3d0df7f 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -41,6 +41,7 @@ from mypy.sametypes import is_same_type from mypy.util import unmangle from mypy.errorcodes import ErrorCode +from mypy.message_registry import ErrorMessage from mypy import message_registry, errorcodes as codes TYPES_FOR_UNIMPORTED_HINTS: Final = { @@ -181,7 +182,7 @@ def report(self, end_line=end_line, code=code, allow_dups=allow_dups) def fail(self, - msg: str, + msg: ErrorMessage, context: Optional[Context], *, code: Optional[ErrorCode] = None, @@ -189,7 +190,12 @@ def fail(self, origin: Optional[Context] = None, allow_dups: bool = False) -> None: """Report an error message (unless disabled).""" - self.report(msg, context, 'error', code=code, file=file, + if isinstance(msg, str): # TODO: Remove + self.report(msg, context, 'error', code=code, file=file, + origin=origin, allow_dups=allow_dups) + return + + self.report(msg.value, context, 'error', code=msg.code, file=file, origin=origin, allow_dups=allow_dups) def note(self, @@ -249,10 +255,10 @@ def has_no_attr(self, if (isinstance(original_type, Instance) and original_type.type.has_readable_member(member)): - self.fail('Member "{}" is not assignable'.format(member), context) + self.fail(message_registry.MEMBER_NOT_ASSIGNABLE.format(member), context) elif member == '__contains__': - self.fail('Unsupported right operand type for in ({})'.format( - format_type(original_type)), context, code=codes.OPERATOR) + self.fail(message_registry.UNSUPPORTED_OPERAND_FOR_IN.format( + format_type(original_type)), context) elif member in op_methods.values(): # Access to a binary operator member (e.g. _add). This case does # not handle indexing operations. @@ -261,36 +267,36 @@ def has_no_attr(self, self.unsupported_left_operand(op, original_type, context) break elif member == '__neg__': - self.fail('Unsupported operand type for unary - ({})'.format( - format_type(original_type)), context, code=codes.OPERATOR) + self.fail(message_registry.UNSUPPORTED_OPERAND_FOR_UNARY_MINUS.format( + format_type(original_type)), context) elif member == '__pos__': - self.fail('Unsupported operand type for unary + ({})'.format( - format_type(original_type)), context, code=codes.OPERATOR) + self.fail(message_registry.UNSUPPORTED_OPERAND_FOR_UNARY_PLUS.format( + format_type(original_type)), context) elif member == '__invert__': - self.fail('Unsupported operand type for ~ ({})'.format( - format_type(original_type)), context, code=codes.OPERATOR) + self.fail(message_registry.UNSUPPORTED_OPERAND_FOR_INVERT.format( + format_type(original_type)), context) elif member == '__getitem__': # Indexed get. # TODO: Fix this consistently in format_type if isinstance(original_type, CallableType) and original_type.is_type_obj(): - self.fail('The type {} is not generic and not indexable'.format( + self.fail(message_registry.TYPE_NOT_GENERIC_OR_INDEXABLE.format( format_type(original_type)), context) else: - self.fail('Value of type {} is not indexable'.format( - format_type(original_type)), context, code=codes.INDEX) + self.fail(message_registry.TYPE_NOT_INDEXABLE.format( + format_type(original_type)), context) elif member == '__setitem__': # Indexed set. - self.fail('Unsupported target for indexed assignment ({})'.format( - format_type(original_type)), context, code=codes.INDEX) + self.fail(message_registry.UNSUPPORTED_TARGET_INDEXED_ASSIGNMENT.format( + format_type(original_type)), context) elif member == '__call__': if isinstance(original_type, Instance) and \ (original_type.type.fullname == 'builtins.function'): # "'function' not callable" is a confusing error message. # Explain that the problem is that the type of the function is not known. - self.fail('Cannot call function of unknown type', context, code=codes.OPERATOR) + self.fail(message_registry.CALLING_FUNCTION_OF_UNKNOWN_TYPE, context) else: - self.fail('{} not callable'.format(format_type(original_type)), context, - code=codes.OPERATOR) + self.fail(message_registry.TYPE_NOT_CALLABLE.format(format_type(original_type)), + context) else: # The non-special case: a missing ordinary attribute. extra = '' @@ -320,21 +326,19 @@ def has_no_attr(self, matches = [] if matches: self.fail( - '{} has no attribute "{}"; maybe {}?{}'.format( + message_registry.TYPE_HAS_NO_ATTRIBUTE_X_MAYBE_Y.format( format_type(original_type), member, pretty_seq(matches, "or"), extra, ), - context, - code=codes.ATTR_DEFINED) + context) failed = True if not failed: self.fail( - '{} has no attribute "{}"{}'.format( + message_registry.TYPE_HAS_NO_ATTRIBUTE_X.format( format_type(original_type), member, extra), - context, - code=codes.ATTR_DEFINED) + context) elif isinstance(original_type, UnionType): # The checker passes "object" in lieu of "None" for attribute # checks, so we manually convert it back. @@ -342,18 +346,15 @@ def has_no_attr(self, if typ_format == '"object"' and \ any(type(item) == NoneType for item in original_type.items): typ_format = '"None"' - self.fail('Item {} of {} has no attribute "{}"{}'.format( - typ_format, orig_type_format, member, extra), context, - code=codes.UNION_ATTR) + self.fail(message_registry.ITEM_HAS_NO_ATTRIBUTE_X.format( + typ_format, orig_type_format, member, extra), context) return AnyType(TypeOfAny.from_error) def unsupported_operand_types(self, op: str, left_type: Any, right_type: Any, - context: Context, - *, - code: ErrorCode = codes.OPERATOR) -> None: + context: Context) -> None: """Report unsupported operand types for a binary operation. Types can be Type objects or strings. @@ -371,29 +372,26 @@ def unsupported_operand_types(self, right_str = format_type(right_type) if self.disable_type_names: - msg = 'Unsupported operand types for {} (likely involving Union)'.format(op) + msg = message_registry.UNSUPPORTED_OPERANDS_LIKELY_UNION.format(op) else: - msg = 'Unsupported operand types for {} ({} and {})'.format( - op, left_str, right_str) - self.fail(msg, context, code=code) + msg = message_registry.UNSUPPORTED_OPERANDS.format(op, left_str, right_str) + self.fail(msg, context) def unsupported_left_operand(self, op: str, typ: Type, context: Context) -> None: if self.disable_type_names: - msg = 'Unsupported left operand type for {} (some union)'.format(op) + msg = message_registry.UNSUPPORTED_LEFT_OPERAND_TYPE_UNION.format(op) else: - msg = 'Unsupported left operand type for {} ({})'.format( - op, format_type(typ)) - self.fail(msg, context, code=codes.OPERATOR) + msg = message_registry.UNSUPPORTED_LEFT_OPERAND_TYPE.format(op, format_type(typ)) + self.fail(msg, context) def not_callable(self, typ: Type, context: Context) -> Type: - self.fail('{} not callable'.format(format_type(typ)), context) + self.fail(message_registry.TYPE_NOT_CALLABLE_2.format(format_type(typ)), context) return AnyType(TypeOfAny.from_error) def untyped_function_call(self, callee: CallableType, context: Context) -> Type: name = callable_name(callee) or '(unknown)' - self.fail('Call to untyped function {} in typed context'.format(name), context, - code=codes.NO_UNTYPED_CALL) + self.fail(message_registry.UNTYPED_FUNCTION_CALL.format(name), context) return AnyType(TypeOfAny.from_error) def incompatible_argument(self, @@ -421,6 +419,8 @@ def incompatible_argument(self, callee_name = callable_name(callee) if callee_name is not None: name = callee_name + target = 'to {} '.format(name) + if callee.bound_args and callee.bound_args[0] is not None: base = format_type(callee.bound_args[0]) else: @@ -432,50 +432,41 @@ def incompatible_argument(self, if name.startswith('"{}" of'.format(variant)): if op == 'in' or variant != method: # Reversed order of base/argument. - self.unsupported_operand_types(op, arg_type, base, - context, code=codes.OPERATOR) + self.unsupported_operand_types(op, arg_type, base, context) else: - self.unsupported_operand_types(op, base, arg_type, - context, code=codes.OPERATOR) + self.unsupported_operand_types(op, base, arg_type, context) return codes.OPERATOR if name.startswith('"__cmp__" of'): - self.unsupported_operand_types("comparison", arg_type, base, - context, code=codes.OPERATOR) - return codes.INDEX + self.unsupported_operand_types("comparison", arg_type, base, context) + return codes.OPERATOR if name.startswith('"__getitem__" of'): - self.invalid_index_type(arg_type, callee.arg_types[n - 1], base, context, - code=codes.INDEX) + self.invalid_index_type(arg_type, callee.arg_types[n - 1], base, context) return codes.INDEX if name.startswith('"__setitem__" of'): if n == 1: - self.invalid_index_type(arg_type, callee.arg_types[n - 1], base, context, - code=codes.INDEX) + self.invalid_index_type(arg_type, callee.arg_types[n - 1], base, context) return codes.INDEX else: - msg = '{} (expression has type {}, target has type {})' + msg = message_registry.TARGET_INCOMPATIBLE_TYPE arg_type_str, callee_type_str = format_type_distinctly(arg_type, callee.arg_types[n - 1]) self.fail(msg.format(message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT, arg_type_str, callee_type_str), - context, code=codes.ASSIGNMENT) + context) return codes.ASSIGNMENT - target = 'to {} '.format(name) - - msg = '' - code = codes.MISC - notes: List[str] = [] if callee_name == '': name = callee_name[1:-1] n -= 1 actual_type_str, expected_type_str = format_type_distinctly(arg_type, callee.arg_types[0]) - msg = '{} item {} has incompatible type {}; expected {}'.format( + msg = message_registry.LIST_ITEM_INCOMPATIBLE_TYPE.format( name.title(), n, actual_type_str, expected_type_str) - code = codes.LIST_ITEM + self.fail(msg, context) + return msg.code elif callee_name == '': name = callee_name[1:-1] n -= 1 @@ -496,35 +487,43 @@ def incompatible_argument(self, value_type_str, expected_value_type_str = format_type_distinctly( value_type, expected_value_type) - msg = '{} entry {} has incompatible type {}: {}; expected {}: {}'.format( + msg = message_registry.DICT_ENTRY_INCOMPATIBLE_TYPE.format( name.title(), n, key_type_str, value_type_str, expected_key_type_str, expected_value_type_str) - code = codes.DICT_ITEM + self.fail(msg, context) + return msg.code elif callee_name == '': actual_type_str, expected_type_str = map(strip_quotes, format_type_distinctly(arg_type, callee.arg_types[0])) - msg = 'List comprehension has incompatible type List[{}]; expected List[{}]'.format( + msg = message_registry.LIST_COMP_INCOMPATIBLE_TYPE.format( actual_type_str, expected_type_str) + self.fail(msg, context) + return msg.code elif callee_name == '': actual_type_str, expected_type_str = map(strip_quotes, format_type_distinctly(arg_type, callee.arg_types[0])) - msg = 'Set comprehension has incompatible type Set[{}]; expected Set[{}]'.format( + msg = message_registry.SET_COMP_INCOMPATIBLE_TYPE.format( actual_type_str, expected_type_str) + self.fail(msg, context) + return msg.code elif callee_name == '': actual_type_str, expected_type_str = format_type_distinctly(arg_type, callee.arg_types[n - 1]) - msg = ('{} expression in dictionary comprehension has incompatible type {}; ' - 'expected type {}').format( + msg = message_registry.DICT_COMP_INCOMPATIBLE_TYPE.format( 'Key' if n == 1 else 'Value', actual_type_str, expected_type_str) + self.fail(msg, context) elif callee_name == '': actual_type_str, expected_type_str = format_type_distinctly(arg_type, callee.arg_types[0]) - msg = 'Generator has incompatible item type {}; expected {}'.format( + msg = message_registry.GENERATOR_INCOMPATIBLE_TYPE.format( actual_type_str, expected_type_str) + self.fail(msg, context) + return msg.code + else: try: expected_type = callee.arg_types[m - 1] @@ -557,11 +556,11 @@ def incompatible_argument(self, bare=True) arg_label = '"{}"'.format(arg_name) if isinstance(outer_context, IndexExpr) and isinstance(outer_context.index, StrExpr): - msg = 'Value of "{}" has incompatible type {}; expected {}' .format( + msg_str = 'Value of "{}" has incompatible type {}; expected {}' .format( outer_context.index.value, quote_type_string(arg_type_str), quote_type_string(expected_type_str)) else: - msg = 'Argument {} {}has incompatible type {}; expected {}'.format( + msg_str = 'Argument {} {}has incompatible type {}; expected {}'.format( arg_label, target, quote_type_string(arg_type_str), quote_type_string(expected_type_str)) object_type = get_proper_type(object_type) @@ -574,14 +573,20 @@ def incompatible_argument(self, expected_types = list(expected_type.items) else: expected_types = [expected_type] + + notes: List[str] = [] for type in get_proper_types(expected_types): if isinstance(arg_type, Instance) and isinstance(type, Instance): notes = append_invariance_notes(notes, arg_type, type) - self.fail(msg, context, code=code) - if notes: - for note_msg in notes: - self.note(note_msg, context, code=code) - return code + + msg = ErrorMessage(msg_str, code) + self.fail(msg, context) + if notes: + for note_msg in notes: + self.note(note_msg, context, code=code) + return msg.code + + return None def incompatible_argument_note(self, original_caller_type: ProperType, @@ -606,10 +611,10 @@ def incompatible_argument_note(self, self.note_call(original_caller_type, call, context, code=code) def invalid_index_type(self, index_type: Type, expected_type: Type, base_str: str, - context: Context, *, code: ErrorCode) -> None: + context: Context) -> None: index_str, expected_str = format_type_distinctly(index_type, expected_type) - self.fail('Invalid index type {} for {}; expected type {}'.format( - index_str, base_str, expected_str), context, code=code) + self.fail(message_registry.INVALID_INDEX_TYPE.format( + index_str, base_str, expected_str), context) def too_few_arguments(self, callee: CallableType, context: Context, argument_names: Optional[Sequence[Optional[str]]]) -> None: @@ -630,15 +635,16 @@ def too_few_arguments(self, callee: CallableType, context: Context, else: msg = 'Too few arguments' + for_function(callee) - self.fail(msg, context, code=codes.CALL_ARG) + + self.fail(ErrorMessage(msg, codes.CALL_ARG), context) def missing_named_argument(self, callee: CallableType, context: Context, name: str) -> None: msg = 'Missing named argument "{}"'.format(name) + for_function(callee) - self.fail(msg, context, code=codes.CALL_ARG) + self.fail(ErrorMessage(msg, codes.CALL_ARG), context) def too_many_arguments(self, callee: CallableType, context: Context) -> None: msg = 'Too many arguments' + for_function(callee) - self.fail(msg, context, code=codes.CALL_ARG) + self.fail(ErrorMessage(msg, codes.CALL_ARG), context) def too_many_arguments_from_typed_dict(self, callee: CallableType, @@ -648,16 +654,16 @@ def too_many_arguments_from_typed_dict(self, for key in arg_type.items: if key not in callee.arg_names: msg = 'Extra argument "{}" from **args'.format(key) + for_function(callee) + self.fail(ErrorMessage(msg), context) break else: self.too_many_arguments(callee, context) - return - self.fail(msg, context) + def too_many_positional_arguments(self, callee: CallableType, context: Context) -> None: msg = 'Too many positional arguments' + for_function(callee) - self.fail(msg, context) + self.fail(ErrorMessage(msg), context) def unexpected_keyword_argument(self, callee: CallableType, name: str, arg_type: Type, context: Context) -> None: @@ -677,7 +683,7 @@ def unexpected_keyword_argument(self, callee: CallableType, name: str, arg_type: matches = best_matches(name, not_matching_type_args) if matches: msg += "; did you mean {}?".format(pretty_seq(matches[:3], "or")) - self.fail(msg, context, code=codes.CALL_ARG) + self.fail(ErrorMessage(msg, codes.CALL_ARG), context) module = find_defining_module(self.modules, callee) if module: assert callee.definition is not None @@ -689,7 +695,7 @@ def unexpected_keyword_argument(self, callee: CallableType, name: str, arg_type: def duplicate_argument_value(self, callee: CallableType, index: int, context: Context) -> None: - self.fail('{} gets multiple values for keyword argument "{}"'. + self.fail(message_registry.MULTIPLE_VALUES_FOR_KWARG. format(callable_name(callee) or 'Function', callee.arg_names[index]), context) @@ -700,13 +706,12 @@ def does_not_return_value(self, callee_type: Optional[Type], context: Context) - if isinstance(callee_type, FunctionLike): name = callable_name(callee_type) if name is not None: - self.fail('{} does not return a value'.format(capitalize(name)), context, - code=codes.FUNC_RETURNS_VALUE) + self.fail(message_registry.NO_RETURN_VALUE.format(capitalize(name)), context) else: - self.fail('Function does not return a value', context, code=codes.FUNC_RETURNS_VALUE) + self.fail(message_registry.FUNCTION_NO_RETURN_VALUE, context) def underscore_function_call(self, context: Context) -> None: - self.fail('Calling function named "_" is not allowed', context) + self.fail(message_registry.UNDERSCORE_FUNCTION_CALL, context) def deleted_as_rvalue(self, typ: DeletedType, context: Context) -> None: """Report an error about using an deleted type as an rvalue.""" @@ -714,7 +719,7 @@ def deleted_as_rvalue(self, typ: DeletedType, context: Context) -> None: s = "" else: s = ' "{}"'.format(typ.source) - self.fail('Trying to read deleted variable{}'.format(s), context) + self.fail(message_registry.READING_DELETED_VALUE.format(s), context) def deleted_as_lvalue(self, typ: DeletedType, context: Context) -> None: """Report an error about using an deleted type as an lvalue. @@ -726,7 +731,7 @@ def deleted_as_lvalue(self, typ: DeletedType, context: Context) -> None: s = "" else: s = ' "{}"'.format(typ.source) - self.fail('Assignment to variable{} outside except: block'.format(s), context) + self.fail(message_registry.ASSIGNMENT_OUTSIDE_EXCEPT.format(s), context) def no_variant_matches_arguments(self, plausible_targets: List[CallableType], @@ -744,14 +749,14 @@ def no_variant_matches_arguments(self, arg_types_str = ', '.join(format_type(arg) for arg in arg_types) num_args = len(arg_types) if num_args == 0: - self.fail('All overload variants{} require at least one argument'.format(name_str), - context, code=code) + msg = 'All overload variants{} require at least one argument'.format(name_str) + self.fail(ErrorMessage(msg, code), context) elif num_args == 1: - self.fail('No overload variant{} matches argument type {}' - .format(name_str, arg_types_str), context, code=code) + msg = 'No overload variant{} matches argument type {}'.format(name_str, arg_types_str) + self.fail(ErrorMessage(msg, code), context) else: - self.fail('No overload variant{} matches argument types {}' - .format(name_str, arg_types_str), context, code=code) + msg = 'No overload variant{} matches argument types {}'.format(name_str, arg_types_str) + self.fail(ErrorMessage(msg, code), context) self.pretty_overload_matches(plausible_targets, overload, context, offset=2, max_items=2, code=code) @@ -760,32 +765,31 @@ def wrong_number_values_to_unpack(self, provided: int, expected: int, context: Context) -> None: if provided < expected: if provided == 1: - self.fail('Need more than 1 value to unpack ({} expected)'.format(expected), + self.fail(message_registry.UNPACK_MORE_THAN_ONE_VALUE_NEEDED.format(expected), context) else: - self.fail('Need more than {} values to unpack ({} expected)'.format( + self.fail(message_registry.UNPACK_TOO_FEW_VALUES.format( provided, expected), context) elif provided > expected: - self.fail('Too many values to unpack ({} expected, {} provided)'.format( + self.fail(message_registry.UNPACK_TOO_MANY_VALUES.format( expected, provided), context) def unpacking_strings_disallowed(self, context: Context) -> None: - self.fail("Unpacking a string is disallowed", context) + self.fail(message_registry.UNPACKING_STRINGS_DISALLOWED, context) def type_not_iterable(self, type: Type, context: Context) -> None: - self.fail('"{}" object is not iterable'.format(type), context) + self.fail(message_registry.TYPE_NOT_ITERABLE.format(type), context) def incompatible_operator_assignment(self, op: str, context: Context) -> None: - self.fail('Result type of {} incompatible in assignment'.format(op), - context) + self.fail(message_registry.INCOMPATIBLE_OPERATOR_ASSIGNMENT.format(op), context) def overload_signature_incompatible_with_supertype( self, name: str, name_in_super: str, supertype: str, overload: Overloaded, context: Context) -> None: target = self.override_target(name, name_in_super, supertype) - self.fail('Signature of "{}" incompatible with {}'.format( - name, target), context, code=codes.OVERRIDE) + self.fail(message_registry.OVERLOAD_SIGNATURE_INCOMPATIBLE.format( + name, target), context) note_template = 'Overload variants must be defined in the same order as they are in "{}"' self.note(note_template.format(supertype), context, code=codes.OVERRIDE) @@ -794,11 +798,11 @@ def signature_incompatible_with_supertype( self, name: str, name_in_super: str, supertype: str, context: Context, original: Optional[FunctionLike] = None, override: Optional[FunctionLike] = None) -> None: - code = codes.OVERRIDE target = self.override_target(name, name_in_super, supertype) - self.fail('Signature of "{}" incompatible with {}'.format( - name, target), context, code=code) + self.fail(message_registry.SIGNATURE_INCOMPATIBLE_WITH_SUPERTYPE.format( + name, target), context) + code = codes.OVERRIDE INCLUDE_DECORATOR = True # Include @classmethod and @staticmethod decorators, if any ALLOW_DUPS = True # Allow duplicate notes, needed when signatures are duplicates MAX_ITEMS = 3 # Display a max of three items for Overloaded types @@ -850,11 +854,9 @@ def argument_incompatible_with_supertype( context: Context) -> None: target = self.override_target(name, name_in_supertype, supertype) arg_type_in_supertype_f = format_type_bare(arg_type_in_supertype) - self.fail('Argument {} of "{}" is incompatible with {}; ' - 'supertype defines the argument type as "{}"' + self.fail(message_registry.ARG_INCOMPATIBLE_WITH_SUPERTYPE .format(arg_num, name, target, arg_type_in_supertype_f), - context, - code=codes.OVERRIDE) + context) self.note( 'This violates the Liskov substitution principle', context, @@ -883,10 +885,9 @@ def return_type_incompatible_with_supertype( context: Context) -> None: target = self.override_target(name, name_in_supertype, supertype) override_str, original_str = format_type_distinctly(override, original) - self.fail('Return type {} of "{}" incompatible with return type {} in {}' + self.fail(message_registry.RETURNTYPE_INCOMPATIBLE_WITH_SUPERTYPE .format(override_str, name, original_str, target), - context, - code=codes.OVERRIDE) + context) def override_target(self, name: str, name_in_super: str, supertype: str) -> str: @@ -899,40 +900,39 @@ def incompatible_type_application(self, expected_arg_count: int, actual_arg_count: int, context: Context) -> None: if expected_arg_count == 0: - self.fail('Type application targets a non-generic function or class', + self.fail(message_registry.TYPE_APPLICATION_ON_NON_GENERIC_TYPE, context) elif actual_arg_count > expected_arg_count: - self.fail('Type application has too many types ({} expected)' + self.fail(message_registry.TYPE_APPLICATION_TOO_MANY_TYPES .format(expected_arg_count), context) else: - self.fail('Type application has too few types ({} expected)' + self.fail(message_registry.TYPE_APPLICATION_TOO_FEW_TYPES .format(expected_arg_count), context) def could_not_infer_type_arguments(self, callee_type: CallableType, n: int, context: Context) -> None: callee_name = callable_name(callee_type) if callee_name is not None and n > 0: - self.fail('Cannot infer type argument {} of {}'.format(n, callee_name), context) + self.fail(message_registry.CANNOT_INFER_TYPE_ARG_NAMED_FUNC.format(n, callee_name), + context) else: - self.fail('Cannot infer function type argument', context) + self.fail(message_registry.CANNOT_INFER_TYPE_ARG_FUNC, context) def invalid_var_arg(self, typ: Type, context: Context) -> None: - self.fail('List or tuple expected as variable arguments', context) + self.fail(message_registry.INVALID_VAR_ARGS, context) def invalid_keyword_var_arg(self, typ: Type, is_mapping: bool, context: Context) -> None: typ = get_proper_type(typ) if isinstance(typ, Instance) and is_mapping: - self.fail('Keywords must be strings', context) + self.fail(message_registry.KEYWORDS_MUST_BE_STRINGS, context) else: suffix = '' if isinstance(typ, Instance): suffix = ', not {}'.format(format_type(typ)) - self.fail( - 'Argument after ** must be a mapping{}'.format(suffix), - context, code=codes.ARG_TYPE) + self.fail(message_registry.ARG_MUST_BE_MAPPING.format(suffix), context) def undefined_in_superclass(self, member: str, context: Context) -> None: - self.fail('"{}" undefined in superclass'.format(member), context) + self.fail(message_registry.MEMBER_UNDEFINED_IN_SUPERCLASS.format(member), context) def first_argument_for_super_must_be_type(self, actual: Type, context: Context) -> None: actual = get_proper_type(actual) @@ -942,93 +942,81 @@ def first_argument_for_super_must_be_type(self, actual: Type, context: Context) type_str = 'a non-type instance' else: type_str = format_type(actual) - self.fail('Argument 1 for "super" must be a type object; got {}'.format(type_str), context, - code=codes.ARG_TYPE) + self.fail(message_registry.SUPER_ARG_EXPECTED_TYPE.format(type_str), context) def too_few_string_formatting_arguments(self, context: Context) -> None: - self.fail('Not enough arguments for format string', context, - code=codes.STRING_FORMATTING) + self.fail(message_registry.FORMAT_STR_TOO_FEW_ARGS, context) def too_many_string_formatting_arguments(self, context: Context) -> None: - self.fail('Not all arguments converted during string formatting', context, - code=codes.STRING_FORMATTING) + self.fail(message_registry.FORMAT_STR_TOO_MANY_ARGS, context) def unsupported_placeholder(self, placeholder: str, context: Context) -> None: - self.fail('Unsupported format character "%s"' % placeholder, context, - code=codes.STRING_FORMATTING) + self.fail(message_registry.FORMAT_STR_UNSUPPORTED_CHAR.format(placeholder), context) def string_interpolation_with_star_and_key(self, context: Context) -> None: - self.fail('String interpolation contains both stars and mapping keys', context, - code=codes.STRING_FORMATTING) + self.fail(message_registry.STRING_INTERPOLATION_WITH_STAR_AND_KEY, context) def requires_int_or_single_byte(self, context: Context, format_call: bool = False) -> None: - self.fail('"{}c" requires an integer in range(256) or a single byte' + self.fail(message_registry.FORMAT_STR_INVALID_CHR_CONVERSION_RANGE .format(':' if format_call else '%'), - context, code=codes.STRING_FORMATTING) + context) def requires_int_or_char(self, context: Context, format_call: bool = False) -> None: - self.fail('"{}c" requires int or char'.format(':' if format_call else '%'), - context, code=codes.STRING_FORMATTING) + self.fail(message_registry.FORMAT_STR_INVALID_CHR_CONVERSION + .format(':' if format_call else '%'), + context) def key_not_in_mapping(self, key: str, context: Context) -> None: - self.fail('Key "%s" not found in mapping' % key, context, - code=codes.STRING_FORMATTING) + self.fail(message_registry.KEY_NOT_IN_MAPPING.format(key), context) def string_interpolation_mixing_key_and_non_keys(self, context: Context) -> None: - self.fail('String interpolation mixes specifier with and without mapping keys', context, - code=codes.STRING_FORMATTING) + self.fail(message_registry.FORMAT_STR_MIXED_KEYS_AND_NON_KEYS, context) def cannot_determine_type(self, name: str, context: Context) -> None: - self.fail('Cannot determine type of "%s"' % name, context, code=codes.HAS_TYPE) + self.fail(message_registry.CANNOT_DETERMINE_TYPE.format(name), context) def cannot_determine_type_in_base(self, name: str, base: str, context: Context) -> None: - self.fail('Cannot determine type of "%s" in base class "%s"' % (name, base), context) + self.fail(message_registry.CANNOT_DETERMINE_TYPE_IN_BASE.format(name, base), context) def no_formal_self(self, name: str, item: CallableType, context: Context) -> None: - self.fail('Attribute function "%s" with type %s does not accept self argument' - % (name, format_type(item)), context) + self.fail(message_registry.DOES_NOT_ACCEPT_SELF.format(name, format_type(item)), context) def incompatible_self_argument(self, name: str, arg: Type, sig: CallableType, is_classmethod: bool, context: Context) -> None: kind = 'class attribute function' if is_classmethod else 'attribute function' - self.fail('Invalid self argument %s to %s "%s" with type %s' - % (format_type(arg), kind, name, format_type(sig)), context) + self.fail(message_registry.INCOMPATIBLE_SELF_ARG + .format(format_type(arg), kind, name, format_type(sig)), context) def incompatible_conditional_function_def(self, defn: FuncDef) -> None: - self.fail('All conditional function variants must have identical ' - 'signatures', defn) + self.fail(message_registry.INCOMPATIBLE_CONDITIONAL_FUNCS, defn) def cannot_instantiate_abstract_class(self, class_name: str, abstract_attributes: List[str], context: Context) -> None: attrs = format_string_list(['"%s"' % a for a in abstract_attributes]) - self.fail('Cannot instantiate abstract class "%s" with abstract ' - 'attribute%s %s' % (class_name, plural_s(abstract_attributes), - attrs), - context, code=codes.ABSTRACT) + self.fail(message_registry.CANNOT_INSTANTIATE_ABSTRACT_CLASS + .format(class_name, plural_s(abstract_attributes), attrs), + context) def base_class_definitions_incompatible(self, name: str, base1: TypeInfo, base2: TypeInfo, context: Context) -> None: - self.fail('Definition of "{}" in base class "{}" is incompatible ' - 'with definition in base class "{}"'.format( + self.fail(message_registry.INCOMPATIBLE_BASE_CLASS_DEFNS.format( name, base1.name, base2.name), context) def cant_assign_to_method(self, context: Context) -> None: - self.fail(message_registry.CANNOT_ASSIGN_TO_METHOD, context, - code=codes.ASSIGNMENT) + self.fail(message_registry.CANNOT_ASSIGN_TO_METHOD, context) def cant_assign_to_classvar(self, name: str, context: Context) -> None: - self.fail('Cannot assign to class variable "%s" via instance' % name, context) + self.fail(message_registry.CANNOT_ASSIGN_TO_CLASSVAR.format(name), context) def final_cant_override_writable(self, name: str, ctx: Context) -> None: - self.fail('Cannot override writable attribute "{}" with a final one'.format(name), ctx) + self.fail(message_registry.CANNOT_OVERRIDE_TO_FINAL.format(name), ctx) def cant_override_final(self, name: str, base_name: str, ctx: Context) -> None: - self.fail('Cannot override final attribute "{}"' - ' (previously declared in base class "{}")'.format(name, base_name), ctx) + self.fail(message_registry.CANNOT_OVERRIDE_FINAL.format(name, base_name), ctx) def cant_assign_to_final(self, name: str, attr_assign: bool, ctx: Context) -> None: """Warn about a prohibited assignment to a final attribute. @@ -1036,17 +1024,17 @@ def cant_assign_to_final(self, name: str, attr_assign: bool, ctx: Context) -> No Pass `attr_assign=True` if the assignment assigns to an attribute. """ kind = "attribute" if attr_assign else "name" - self.fail('Cannot assign to final {} "{}"'.format(kind, unmangle(name)), ctx) + self.fail(message_registry.CANNOT_ASSIGN_TO_FINAL.format(kind, unmangle(name)), ctx) def protocol_members_cant_be_final(self, ctx: Context) -> None: - self.fail("Protocol member cannot be final", ctx) + self.fail(message_registry.PROTOCOL_MEMBER_CANNOT_BE_FINAL, ctx) def final_without_value(self, ctx: Context) -> None: - self.fail("Final name must be initialized with a value", ctx) + self.fail(message_registry.FINAL_WITHOUT_VALUE, ctx) def read_only_property(self, name: str, type: TypeInfo, context: Context) -> None: - self.fail('Property "{}" defined in "{}" is read-only'.format( + self.fail(message_registry.PROPERTY_IS_READ_ONLY.format( name, type.name), context) def incompatible_typevar_value(self, @@ -1056,47 +1044,36 @@ def incompatible_typevar_value(self, context: Context) -> None: self.fail(message_registry.INCOMPATIBLE_TYPEVAR_VALUE .format(typevar_name, callable_name(callee) or 'function', format_type(typ)), - context, - code=codes.TYPE_VAR) + context) def dangerous_comparison(self, left: Type, right: Type, kind: str, ctx: Context) -> None: left_str = 'element' if kind == 'container' else 'left operand' right_str = 'container item' if kind == 'container' else 'right operand' - message = 'Non-overlapping {} check ({} type: {}, {} type: {})' + message = message_registry.NON_OVERLAPPING_COMPARISON left_typ, right_typ = format_type_distinctly(left, right) - self.fail(message.format(kind, left_str, left_typ, right_str, right_typ), ctx, - code=codes.COMPARISON_OVERLAP) + self.fail(message.format(kind, left_str, left_typ, right_str, right_typ), ctx) def overload_inconsistently_applies_decorator(self, decorator: str, context: Context) -> None: - self.fail( - 'Overload does not consistently use the "@{}" '.format(decorator) - + 'decorator on all function signatures.', - context) + self.fail(message_registry.OVERLOAD_INCONSISTENT_DECORATOR_USE.format(decorator), context) def overloaded_signatures_overlap(self, index1: int, index2: int, context: Context) -> None: - self.fail('Overloaded function signatures {} and {} overlap with ' - 'incompatible return types'.format(index1, index2), context) + self.fail(message_registry.OVERLOAD_INCOMPATIBLE_RETURN_TYPES.format(index1, index2), + context) def overloaded_signature_will_never_match(self, index1: int, index2: int, context: Context) -> None: - self.fail( - 'Overloaded function signature {index2} will never be matched: ' - 'signature {index1}\'s parameter type(s) are the same or broader'.format( - index1=index1, - index2=index2), - context) + self.fail(message_registry.OVERLOAD_SIGNATURE_WILL_NEVER_MATCH + .format(index1=index1, index2=index2), + context) def overloaded_signatures_typevar_specific(self, index: int, context: Context) -> None: - self.fail('Overloaded function implementation cannot satisfy signature {} '.format(index) + - 'due to inconsistencies in how they use type variables', context) + self.fail(message_registry.OVERLOAD_INCONSISTENT_TYPEVARS.format(index), context) def overloaded_signatures_arg_specific(self, index: int, context: Context) -> None: - self.fail('Overloaded function implementation does not accept all possible arguments ' - 'of signature {}'.format(index), context) + self.fail(message_registry.OVERLOAD_INCONSISTENT_ARGS.format(index), context) def overloaded_signatures_ret_specific(self, index: int, context: Context) -> None: - self.fail('Overloaded function implementation cannot produce return type ' - 'of signature {}'.format(index), context) + self.fail(message_registry.OVERLOAD_INCONSISTENT_RETURN_TYPE.format(index), context) def warn_both_operands_are_from_unions(self, context: Context) -> None: self.note('Both left and right operands are unions', context, code=codes.OPERATOR) @@ -1108,33 +1085,33 @@ def warn_operand_was_from_union(self, side: str, original: Type, context: Contex def operator_method_signatures_overlap( self, reverse_class: TypeInfo, reverse_method: str, forward_class: Type, forward_method: str, context: Context) -> None: - self.fail('Signatures of "{}" of "{}" and "{}" of {} ' - 'are unsafely overlapping'.format( + self.fail(message_registry.OPERATOR_METHOD_SIGNATURE_OVERLAP.format( reverse_method, reverse_class.name, forward_method, format_type(forward_class)), context) def forward_operator_not_callable( self, forward_method: str, context: Context) -> None: - self.fail('Forward operator "{}" is not callable'.format( + self.fail(message_registry.FORWARD_OPERATOR_NOT_CALLABLE.format( forward_method), context) def signatures_incompatible(self, method: str, other_method: str, context: Context) -> None: - self.fail('Signatures of "{}" and "{}" are incompatible'.format( + self.fail(message_registry.INCOMPATIBLE_SIGNATURES.format( method, other_method), context) def yield_from_invalid_operand_type(self, expr: Type, context: Context) -> Type: text = format_type(expr) if format_type(expr) != 'object' else expr - self.fail('"yield from" can\'t be applied to {}'.format(text), context) + self.fail(message_registry.INVALID_YIELD_FROM.format(text), context) return AnyType(TypeOfAny.from_error) def invalid_signature(self, func_type: Type, context: Context) -> None: - self.fail('Invalid signature "{}"'.format(func_type), context) + self.fail(message_registry.INVALID_SIGNATURE.format(func_type), context) def invalid_signature_for_special_method( self, func_type: Type, context: Context, method_name: str) -> None: - self.fail('Invalid signature "{}" for "{}"'.format(func_type, method_name), context) + self.fail(message_registry.INVALID_SIGNATURE_SPECIAL.format(func_type, method_name), + context) def reveal_type(self, typ: Type, context: Context) -> None: self.note('Revealed type is "{}"'.format(typ), context) @@ -1148,15 +1125,13 @@ def reveal_locals(self, type_map: Dict[str, Optional[Type]], context: Context) - self.note(line, context) def unsupported_type_type(self, item: Type, context: Context) -> None: - self.fail('Cannot instantiate type "Type[{}]"'.format(format_type_bare(item)), context) + self.fail(message_registry.UNSUPPORTED_TYPE_TYPE.format(format_type_bare(item)), context) def redundant_cast(self, typ: Type, context: Context) -> None: - self.fail('Redundant cast to {}'.format(format_type(typ)), context, - code=codes.REDUNDANT_CAST) + self.fail(message_registry.REDUNDANT_CAST.format(format_type(typ)), context) def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> None: - self.fail("{} becomes {} due to an unfollowed import".format(prefix, format_type(typ)), - ctx, code=codes.NO_ANY_UNIMPORTED) + self.fail(message_registry.UNFOLLOWED_IMPORT.format(prefix, format_type(typ)), ctx) def need_annotation_for_var(self, node: SymbolNode, context: Context, python_version: Optional[Tuple[int, int]] = None) -> None: @@ -1180,11 +1155,11 @@ def need_annotation_for_var(self, node: SymbolNode, context: Context, else: needed = 'comment' - self.fail('Need type {} for "{}"{}'.format(needed, unmangle(node.name), hint), context, - code=codes.VAR_ANNOTATED) + self.fail(message_registry.ANNOTATION_NEEDED.format(needed, unmangle(node.name), hint), + context) def explicit_any(self, ctx: Context) -> None: - self.fail('Explicit "Any" is not allowed', ctx) + self.fail(message_registry.NO_EXPLICIT_ANY, ctx) def unexpected_typeddict_keys( self, @@ -1199,35 +1174,33 @@ def unexpected_typeddict_keys( if actual_set < expected_set: # Use list comprehension instead of set operations to preserve order. missing = [key for key in expected_keys if key not in actual_set] - self.fail('Missing {} for TypedDict {}'.format( + self.fail(message_registry.TYPEDDICT_MISSING_KEYS.format( format_key_list(missing, short=True), format_type(typ)), - context, code=codes.TYPEDDICT_ITEM) + context) return else: extra = [key for key in actual_keys if key not in expected_set] if extra: # If there are both extra and missing keys, only report extra ones for # simplicity. - self.fail('Extra {} for TypedDict {}'.format( + self.fail(message_registry.TYPEDDICT_EXTRA_KEYS.format( format_key_list(extra, short=True), format_type(typ)), - context, code=codes.TYPEDDICT_ITEM) + context) return found = format_key_list(actual_keys, short=True) if not expected_keys: - self.fail('Unexpected TypedDict {}'.format(found), context) + self.fail(message_registry.TYPEDDICT_UNEXPECTED_KEYS.format(found), context) return expected = format_key_list(expected_keys) if actual_keys and actual_set < expected_set: found = 'only {}'.format(found) - self.fail('Expected {} but found {}'.format(expected, found), context, - code=codes.TYPEDDICT_ITEM) + self.fail(message_registry.TYPEDDICT_KEYS_MISMATCH.format(expected, found), context) def typeddict_key_must_be_string_literal( self, typ: TypedDictType, context: Context) -> None: - self.fail( - 'TypedDict key must be a string literal; expected one of {}'.format( + self.fail(message_registry.TYPEDDICT_KEY_STRING_LITERAL_EXPECTED.format( format_item_name_list(typ.items.keys())), context) def typeddict_key_not_found( @@ -1236,11 +1209,11 @@ def typeddict_key_not_found( item_name: str, context: Context) -> None: if typ.is_anonymous(): - self.fail('"{}" is not a valid TypedDict key; expected one of {}'.format( + self.fail(message_registry.TYPEDDICT_KEY_INVALID.format( item_name, format_item_name_list(typ.items.keys())), context) else: - self.fail('TypedDict {} has no key "{}"'.format( - format_type(typ), item_name), context, code=codes.TYPEDDICT_ITEM) + self.fail(message_registry.TYPEDDICT_UNKNOWN_KEY.format( + format_type(typ), item_name), context) matches = best_matches(item_name, typ.items.keys()) if matches: self.note("Did you mean {}?".format( @@ -1251,7 +1224,7 @@ def typeddict_context_ambiguous( types: List[TypedDictType], context: Context) -> None: formatted_types = ', '.join(list(format_type_distinctly(*types))) - self.fail('Type of TypedDict is ambiguous, could be any of ({})'.format( + self.fail(message_registry.TYPEDDICT_AMBIGUOUS_TYPE.format( formatted_types), context) def typeddict_key_cannot_be_deleted( @@ -1260,10 +1233,10 @@ def typeddict_key_cannot_be_deleted( item_name: str, context: Context) -> None: if typ.is_anonymous(): - self.fail('TypedDict key "{}" cannot be deleted'.format(item_name), + self.fail(message_registry.TYPEDDICT_CANNOT_DELETE_KEY.format(item_name), context) else: - self.fail('Key "{}" of TypedDict {} cannot be deleted'.format( + self.fail(message_registry.TYPEDDICT_NAMED_CANNOT_DELETE_KEY.format( item_name, format_type(typ)), context) def typeddict_setdefault_arguments_inconsistent( @@ -1271,30 +1244,26 @@ def typeddict_setdefault_arguments_inconsistent( default: Type, expected: Type, context: Context) -> None: - msg = 'Argument 2 to "setdefault" of "TypedDict" has incompatible type {}; expected {}' - self.fail(msg.format(format_type(default), format_type(expected)), context, - code=codes.TYPEDDICT_ITEM) + msg = message_registry.TYPEDDICT_INCONSISTENT_SETDEFAULT_ARGS + self.fail(msg.format(format_type(default), format_type(expected)), context) def type_arguments_not_allowed(self, context: Context) -> None: - self.fail('Parameterized generics cannot be used with class or instance checks', context) + self.fail(message_registry.PARAMETERIZED_GENERICS_DISALLOWED, context) def disallowed_any_type(self, typ: Type, context: Context) -> None: typ = get_proper_type(typ) if isinstance(typ, AnyType): - message = 'Expression has type "Any"' + message = message_registry.EXPR_HAS_ANY_TYPE else: - message = 'Expression type contains "Any" (has type {})'.format(format_type(typ)) + message = message_registry.EXPR_CONTAINS_ANY_TYPE.format(format_type(typ)) self.fail(message, context) def incorrectly_returning_any(self, typ: Type, context: Context) -> None: - message = 'Returning Any from function declared to return {}'.format( - format_type(typ)) - self.fail(message, context, code=codes.NO_ANY_RETURN) + message = message_registry.INCORRECTLY_RETURNING_ANY.format(format_type(typ)) + self.fail(message, context) def incorrect__exit__return(self, context: Context) -> None: - self.fail( - '"bool" is invalid as return type for "__exit__" that always returns False', context, - code=codes.EXIT_RETURN) + self.fail(message_registry.INVALID_EXIT_RETURN_TYPE, context) self.note( 'Use "typing_extensions.Literal[False]" as the return type or change it to "None"', context, code=codes.EXIT_RETURN) @@ -1306,13 +1275,13 @@ def incorrect__exit__return(self, context: Context) -> None: def untyped_decorated_function(self, typ: Type, context: Context) -> None: typ = get_proper_type(typ) if isinstance(typ, AnyType): - self.fail("Function is untyped after decorator transformation", context) + self.fail(message_registry.UNTYPED_DECORATOR_FUNCTION, context) else: - self.fail('Type of decorated function contains type "Any" ({})'.format( + self.fail(message_registry.DECORATED_TYPE_CONTAINS_ANY.format( format_type(typ)), context) def typed_function_untyped_decorator(self, func_name: str, context: Context) -> None: - self.fail('Untyped decorator makes function "{}" untyped'.format(func_name), context) + self.fail(message_registry.DECORATOR_MAKES_FUNCTION_UNTYPED.format(func_name), context) def bad_proto_variance(self, actual: int, tvar_name: str, expected: int, context: Context) -> None: @@ -1320,24 +1289,23 @@ def bad_proto_variance(self, actual: int, tvar_name: str, expected: int, ' {} one is expected'.format(variance_string(actual), tvar_name, variance_string(expected))) - self.fail(msg, context) + self.fail(ErrorMessage(msg), context) def concrete_only_assign(self, typ: Type, context: Context) -> None: - self.fail("Can only assign concrete classes to a variable of type {}" - .format(format_type(typ)), context) + self.fail(message_registry.CONCRETE_ONLY_ASSIGN.format(format_type(typ)), context) def concrete_only_call(self, typ: Type, context: Context) -> None: - self.fail("Only concrete class can be given where {} is expected" + self.fail(message_registry.EXPECTED_CONCRETE_CLASS .format(format_type(typ)), context) def cannot_use_function_with_type( self, method_name: str, type_name: str, context: Context) -> None: - self.fail("Cannot use {}() with {} type".format(method_name, type_name), context) + self.fail(message_registry.CANNOT_USE_FUNCTION_WITH_TYPE.format(method_name, type_name), + context) def report_non_method_protocol(self, tp: TypeInfo, members: List[str], context: Context) -> None: - self.fail("Only protocols that don't have non-method members can be" - " used with issubclass()", context) + self.fail(message_registry.ISSUBCLASS_ONLY_NON_METHOD_PROTOCOL, context) if len(members) < 3: attrs = ', '.join(members) self.note('Protocol "{}" has non-method member(s): {}' @@ -1354,7 +1322,7 @@ def note_call(self, context, code=code) def unreachable_statement(self, context: Context) -> None: - self.fail("Statement is unreachable", context, code=codes.UNREACHABLE) + self.fail(message_registry.UNREACHABLE_STATEMENT, context) def redundant_left_operand(self, op_name: str, context: Context) -> None: """Indicates that the left operand of a boolean expression is redundant: @@ -1368,8 +1336,7 @@ def unreachable_right_operand(self, op_name: str, context: Context) -> None: it does not change the truth value of the entire condition as a whole. 'op_name' should either be the string "and" or the string "or". """ - self.fail('Right operand of "{}" is never evaluated'.format(op_name), - context, code=codes.UNREACHABLE) + self.fail(message_registry.UNREACHABLE_RIGHT_OPERAND.format(op_name), context) def redundant_condition_in_comprehension(self, truthiness: bool, context: Context) -> None: self.redundant_expr("If condition in comprehension", truthiness, context) @@ -1381,17 +1348,18 @@ def redundant_condition_in_assert(self, truthiness: bool, context: Context) -> N self.redundant_expr("Condition in assert", truthiness, context) def redundant_expr(self, description: str, truthiness: bool, context: Context) -> None: - self.fail("{} is always {}".format(description, str(truthiness).lower()), - context, code=codes.REDUNDANT_EXPR) + self.fail(message_registry.EXPR_IS_ALWAYS_BOOL + .format(description, str(truthiness).lower()), + context) def impossible_intersection(self, formatted_base_class_list: str, reason: str, context: Context, ) -> None: - template = "Subclass of {} cannot exist: would have {}" - self.fail(template.format(formatted_base_class_list, reason), context, - code=codes.UNREACHABLE) + self.fail(message_registry.IMPOSSIBLE_SUBCLASS + .format(formatted_base_class_list, reason), + context) def report_protocol_problems(self, subtype: Union[Instance, TupleType, TypedDictType], @@ -1604,7 +1572,7 @@ def try_report_long_tuple_assignment_error(self, error_msg = "{} ({} {}, {} {})".format(msg, subtype_label, self.format_long_tuple_type(subtype), supertype_label, self.format_long_tuple_type(supertype)) - self.fail(error_msg, context, code=code) + self.fail(ErrorMessage(error_msg, code), context) return True self.generate_incompatible_tuple_error(supertype.items, subtype.items, context, msg, code) @@ -1642,7 +1610,7 @@ def generate_incompatible_tuple_error(self, error_msg += '; {} items are omitted)'.format(str(error_cnt - 3)) else: error_msg += ')' - self.fail(error_msg, context, code=code) + self.fail(ErrorMessage(error_msg, code), context) for note in notes: self.note(note, context, code=code) diff --git a/mypy/nodes.py b/mypy/nodes.py index ce0286db84f6..25231f484068 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -14,6 +14,8 @@ import mypy.strconv from mypy.util import short_type from mypy.visitor import NodeVisitor, StatementVisitor, ExpressionVisitor +from mypy.message_registry import ErrorMessage +from mypy import message_registry from mypy.bogus_type import Bogus @@ -3146,7 +3148,7 @@ def get_member_expr_fullname(expr: MemberExpr) -> Optional[str]: def check_arg_kinds( - arg_kinds: List[ArgKind], nodes: List[T], fail: Callable[[str, T], None]) -> None: + arg_kinds: List[ArgKind], nodes: List[T], fail: Callable[[ErrorMessage, T], None]) -> None: is_var_arg = False is_kw_arg = False seen_named = False @@ -3154,38 +3156,37 @@ def check_arg_kinds( for kind, node in zip(arg_kinds, nodes): if kind == ARG_POS: if is_var_arg or is_kw_arg or seen_named or seen_opt: - fail("Required positional args may not appear " - "after default, named or var args", + fail(message_registry.POS_ARGS_BEFORE_DEFAULT_NAMED_OR_VARARGS, node) break elif kind == ARG_OPT: if is_var_arg or is_kw_arg or seen_named: - fail("Positional default args may not appear after named or var args", node) + fail(message_registry.DEFAULT_ARGS_BEFORE_NAMED_OR_VARARGS, node) break seen_opt = True elif kind == ARG_STAR: if is_var_arg or is_kw_arg or seen_named: - fail("Var args may not appear after named or var args", node) + fail(message_registry.VAR_ARGS_BEFORE_NAMED_OR_VARARGS, node) break is_var_arg = True elif kind == ARG_NAMED or kind == ARG_NAMED_OPT: seen_named = True if is_kw_arg: - fail("A **kwargs argument must be the last argument", node) + fail(message_registry.KWARGS_MUST_BE_LAST, node) break elif kind == ARG_STAR2: if is_kw_arg: - fail("You may only have one **kwargs argument", node) + fail(message_registry.MULTIPLE_KWARGS, node) break is_kw_arg = True -def check_arg_names(names: Sequence[Optional[str]], nodes: List[T], fail: Callable[[str, T], None], +def check_arg_names(names: Sequence[Optional[str]], nodes: List[T], fail: Callable[[ErrorMessage, T], None], description: str = 'function definition') -> None: seen_names: Set[Optional[str]] = set() for name, node in zip(names, nodes): if name is not None and name in seen_names: - fail('Duplicate argument "{}" in {}'.format(name, description), node) + fail(message_registry.DUPLICATE_ARGUMENT_IN_X.format(name, description), node) break seen_names.add(name) diff --git a/mypy/plugin.py b/mypy/plugin.py index 2c39a4548a32..7d5863fee661 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -134,6 +134,7 @@ class C: pass from mypy.options import Options from mypy.lookup import lookup_fully_qualified from mypy.errorcodes import ErrorCode +from mypy.message_registry import ErrorMessage @trait @@ -151,7 +152,7 @@ class TypeAnalyzerPluginInterface: options: Options @abstractmethod - def fail(self, msg: str, ctx: Context, *, code: Optional[ErrorCode] = None) -> None: + def fail(self, msg: ErrorMessage, ctx: Context) -> None: """Emit an error message at given location.""" raise NotImplementedError @@ -223,7 +224,7 @@ def type_context(self) -> List[Optional[Type]]: raise NotImplementedError @abstractmethod - def fail(self, msg: str, ctx: Context, *, code: Optional[ErrorCode] = None) -> None: + def fail(self, msg: ErrorMessage, ctx: Context) -> None: """Emit an error message at given location.""" raise NotImplementedError @@ -260,8 +261,7 @@ def parse_bool(self, expr: Expression) -> Optional[bool]: raise NotImplementedError @abstractmethod - def fail(self, msg: str, ctx: Context, serious: bool = False, *, - blocker: bool = False, code: Optional[ErrorCode] = None) -> None: + def fail(self, msg: ErrorMessage, ctx: Context, *, blocker: bool = False) -> None: """Emit an error message at given location.""" raise NotImplementedError diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 1494ca913545..68aae05c9271 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1,5 +1,6 @@ """Plugin for supporting the attrs library (http://www.attrs.org)""" +from mypy import message_registry from mypy.backports import OrderedDict from typing import Optional, Dict, List, cast, Tuple, Iterable @@ -29,8 +30,6 @@ from mypy.util import unmangle from mypy.server.trigger import make_wildcard_trigger -KW_ONLY_PYTHON_2_UNSUPPORTED = "kw_only is not supported in Python 2" - # The names of the different functions that create classes or arguments. attr_class_makers: Final = { 'attr.s', @@ -127,7 +126,7 @@ def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument: init_type = UnionType.make_union([init_type, NoneType()]) if not init_type: - ctx.api.fail("Cannot determine __init__ type from converter", self.context) + ctx.api.fail(message_registry.CANNOT_DETERMINE_INIT_TYPE, self.context) init_type = AnyType(TypeOfAny.from_error) elif self.converter.name == '': # This means we had a converter but it's not of a type we can infer. @@ -207,7 +206,7 @@ def _determine_eq_order(ctx: 'mypy.plugin.ClassDefContext') -> bool: order = _get_decorator_optional_bool_argument(ctx, 'order') if cmp is not None and any((eq is not None, order is not None)): - ctx.api.fail('Don\'t mix "cmp" with "eq" and "order"', ctx.reason) + ctx.api.fail(message_registry.CMP_WITH_EQ_AND_ORDER, ctx.reason) # cmp takes precedence due to bw-compatibility. if cmp is not None: @@ -221,7 +220,7 @@ def _determine_eq_order(ctx: 'mypy.plugin.ClassDefContext') -> bool: order = eq if eq is False and order is True: - ctx.api.fail('eq must be True if order is True', ctx.reason) + ctx.api.fail(message_registry.EQ_TRUE_IF_ORDER_TRUE, ctx.reason) return order @@ -245,7 +244,7 @@ def _get_decorator_optional_bool_argument( return False if attr_value.fullname == 'builtins.None': return None - ctx.api.fail('"{}" argument must be True or False.'.format(name), ctx.reason) + ctx.api.fail(message_registry.ARG_MUST_BE_TRUE_OR_FALSE.format(name), ctx.reason) return default return default else: @@ -277,14 +276,14 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext', if ctx.api.options.python_version[0] < 3: if auto_attribs: - ctx.api.fail("auto_attribs is not supported in Python 2", ctx.reason) + ctx.api.fail(message_registry.AUTO_ATTRIBS_UNSUPPORTED_PY2, ctx.reason) return if not info.defn.base_type_exprs: # Note: This will not catch subclassing old-style classes. - ctx.api.fail("attrs only works with new-style classes", info.defn) + ctx.api.fail(message_registry.ATTRS_NEWSTYLE_CLASS_ONLY, info.defn) return if kw_only: - ctx.api.fail(KW_ONLY_PYTHON_2_UNSUPPORTED, ctx.reason) + ctx.api.fail(message_registry.KW_ONLY_UNSUPPORTED_PY2, ctx.reason) return attributes = _analyze_class(ctx, auto_attribs, kw_only) @@ -399,9 +398,7 @@ def _analyze_class(ctx: 'mypy.plugin.ClassDefContext', context = attribute.context if i >= len(super_attrs) else ctx.cls if not attribute.has_default and last_default: - ctx.api.fail( - "Non-default attributes not allowed after default attributes.", - context) + ctx.api.fail(message_registry.NON_DEFAULT_ATTRS_AFTER_DEFAULT, context) last_default |= attribute.has_default return attributes @@ -524,7 +521,7 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext', return None if len(stmt.lvalues) > 1: - ctx.api.fail("Too many names for one attribute", stmt) + ctx.api.fail(message_registry.ATTR_TOO_MANY_NAMES, stmt) return None # This is the type that belongs in the __init__ method for this attrib. @@ -536,7 +533,7 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext', # See https://github.com/python-attrs/attrs/issues/481 for explanation. kw_only |= _get_bool_argument(ctx, rvalue, 'kw_only', False) if kw_only and ctx.api.options.python_version[0] < 3: - ctx.api.fail(KW_ONLY_PYTHON_2_UNSUPPORTED, stmt) + ctx.api.fail(message_registry.KW_ONLY_UNSUPPORTED_PY2, stmt) return None # TODO: Check for attr.NOTHING @@ -544,7 +541,7 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext', attr_has_factory = bool(_get_argument(rvalue, 'factory')) if attr_has_default and attr_has_factory: - ctx.api.fail('Can\'t pass both "default" and "factory".', rvalue) + ctx.api.fail(message_registry.DEFAULT_WITH_FACTORY, rvalue) elif attr_has_factory: attr_has_default = True @@ -554,7 +551,7 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext', try: un_type = expr_to_unanalyzed_type(type_arg, ctx.api.options, ctx.api.is_stub_file) except TypeTranslationError: - ctx.api.fail('Invalid argument to type', type_arg) + ctx.api.fail(message_registry.TYPE_INVALID_ARG, type_arg) else: init_type = ctx.api.anal_type(un_type) if init_type and isinstance(lhs.node, Var) and not lhs.node.type: @@ -566,9 +563,9 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext', converter = _get_argument(rvalue, 'converter') convert = _get_argument(rvalue, 'convert') if convert and converter: - ctx.api.fail('Can\'t pass both "convert" and "converter".', rvalue) + ctx.api.fail(message_registry.CONVERT_WITH_CONVERTER, rvalue) elif convert: - ctx.api.fail("convert is deprecated, use converter", rvalue) + ctx.api.fail(message_registry.CONVERT_DEPRECATED, rvalue) converter = convert converter_info = _parse_converter(ctx, converter) @@ -605,10 +602,7 @@ def _parse_converter(ctx: 'mypy.plugin.ClassDefContext', return argument # Signal that we have an unsupported converter. - ctx.api.fail( - "Unsupported converter, only named functions and types are currently supported", - converter - ) + ctx.api.fail(message_registry.UNSUPPORTED_CONVERTER, converter) return Converter('') return Converter(None) diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index d1d74f92aaec..83d58f576efe 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -1,3 +1,4 @@ +from mypy import message_registry from typing import List, Optional, Union from mypy.nodes import ( @@ -39,7 +40,7 @@ def _get_bool_argument(ctx: ClassDefContext, expr: CallExpr, if attr_value: ret = ctx.api.parse_bool(attr_value) if ret is None: - ctx.api.fail('"{}" argument must be True or False.'.format(name), expr) + ctx.api.fail(message_registry.ARG_MUST_BE_TRUE_OR_FALSE.format(name), expr) return default return ret return default diff --git a/mypy/plugins/ctypes.py b/mypy/plugins/ctypes.py index 87ffcdfe3339..e171340c792f 100644 --- a/mypy/plugins/ctypes.py +++ b/mypy/plugins/ctypes.py @@ -4,7 +4,7 @@ # Fully qualified instead of "from mypy.plugin import ..." to avoid circular import problems. import mypy.plugin -from mypy import nodes +from mypy import message_registry, nodes from mypy.maptype import map_instance_to_supertype from mypy.messages import format_type from mypy.subtypes import is_subtype @@ -125,17 +125,15 @@ def array_constructor_callback(ctx: 'mypy.plugin.FunctionContext') -> Type: "The stub of the ctypes.Array constructor should have a single vararg parameter" for arg_num, (arg_kind, arg_type) in enumerate(zip(ctx.arg_kinds[0], ctx.arg_types[0]), 1): if arg_kind == nodes.ARG_POS and not is_subtype(arg_type, allowed): - ctx.api.msg.fail( - 'Array constructor argument {} of type {}' - ' is not convertible to the array element type {}' + ctx.api.fail( + message_registry.CTYPES_INCOMPATIBLE_CONSTRUCTOR_ARG .format(arg_num, format_type(arg_type), format_type(et)), ctx.context) elif arg_kind == nodes.ARG_STAR: ty = ctx.api.named_generic_type("typing.Iterable", [allowed]) if not is_subtype(arg_type, ty): it = ctx.api.named_generic_type("typing.Iterable", [et]) - ctx.api.msg.fail( - 'Array constructor argument {} of type {}' - ' is not convertible to the array element type {}' + ctx.api.fail( + message_registry.CTYPES_INCOMPATIBLE_CONSTRUCTOR_ARG .format(arg_num, format_type(arg_type), format_type(it)), ctx.context) return ctx.default_return_type @@ -203,9 +201,8 @@ def array_value_callback(ctx: 'mypy.plugin.AttributeContext') -> Type: elif isinstance(tp, Instance) and tp.type.fullname == 'ctypes.c_wchar': types.append(_get_text_type(ctx.api)) else: - ctx.api.msg.fail( - 'Array attribute "value" is only available' - ' with element type "c_char" or "c_wchar", not {}' + ctx.api.fail( + message_registry.CTYPES_VALUE_WITH_CHAR_OR_WCHAR_ONLY .format(format_type(et)), ctx.context) return make_simplified_union(types) return ctx.default_attr_type @@ -221,9 +218,8 @@ def array_raw_callback(ctx: 'mypy.plugin.AttributeContext') -> Type: or isinstance(tp, Instance) and tp.type.fullname == 'ctypes.c_char'): types.append(_get_bytes_type(ctx.api)) else: - ctx.api.msg.fail( - 'Array attribute "raw" is only available' - ' with element type "c_char", not {}' + ctx.api.fail( + message_registry.CTYPES_RAW_WITH_CHAR_ONLY .format(format_type(et)), ctx.context) return make_simplified_union(types) return ctx.default_attr_type diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index af0153b2f56c..c82cfde41b50 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -1,5 +1,6 @@ """Plugin that provides support for dataclasses.""" +from mypy import message_registry from typing import Dict, List, Set, Tuple, Optional from typing_extensions import Final @@ -150,7 +151,7 @@ def transform(self) -> None: # Add <, >, <=, >=, but only if the class has an eq method. if decorator_arguments['order']: if not decorator_arguments['eq']: - ctx.api.fail('eq must be True if order is True', ctx.cls) + ctx.api.fail(message_registry.EQ_TRUE_IF_ORDER_TRUE, ctx.cls) for method_name in ['__lt__', '__gt__', '__le__', '__ge__']: # Like for __eq__ and __ne__, we want "other" to match @@ -167,7 +168,7 @@ def transform(self) -> None: if existing_method is not None and not existing_method.plugin_generated: assert existing_method.node ctx.api.fail( - 'You may not have a custom %s method when order=True' % method_name, + message_registry.DATACLASS_ORDER_METHODS_DISALLOWED.format(method_name), existing_method.node, ) @@ -356,7 +357,7 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]: context = (Context(line=attr.line, column=attr.column) if attr in attrs else ctx.cls) ctx.api.fail( - 'Attributes without a default cannot follow attributes with one', + message_registry.DATACLASS_DEFAULT_ATTRS_BEFORE_NON_DEFAULT, context, ) @@ -365,7 +366,7 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]: context = (Context(line=attr.line, column=attr.column) if attr in attrs else ctx.cls) ctx.api.fail( - 'There may not be more than one field with the KW_ONLY type', + message_registry.DATACLASS_SINGLE_KW_ONLY_TYPE, context, ) found_kw_sentinel = found_kw_sentinel or self._is_kw_only_type(attr.type) diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 0984abe80cee..9d5160f92312 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -1,4 +1,5 @@ """Plugin for supporting the functools standard library module.""" +from mypy import message_registry from typing import Dict, NamedTuple, Optional import mypy.plugin @@ -32,7 +33,7 @@ def functools_total_ordering_maker_callback(ctx: mypy.plugin.ClassDefContext, comparison_methods = _analyze_class(ctx) if not comparison_methods: ctx.api.fail( - 'No ordering operation defined when using "functools.total_ordering": < > <= >=', + message_registry.TOTAL_ORDERING_NO_OPERATOR_DEFINED, ctx.reason) return diff --git a/mypy/plugins/singledispatch.py b/mypy/plugins/singledispatch.py index f3575b8966a4..9f15fdb056a9 100644 --- a/mypy/plugins/singledispatch.py +++ b/mypy/plugins/singledispatch.py @@ -1,3 +1,4 @@ +from mypy import message_registry from mypy.messages import format_type from mypy.plugins.common import add_method_to_class from mypy.nodes import ( @@ -9,6 +10,7 @@ FunctionLike ) from mypy.plugin import CheckerPluginInterface, FunctionContext, MethodContext, MethodSigContext +from mypy.message_registry import ErrorMessage from typing import List, NamedTuple, Optional, Sequence, TypeVar, Union from typing_extensions import Final @@ -71,7 +73,7 @@ def make_fake_register_class_instance(api: CheckerPluginInterface, type_args: Se PluginContext = Union[FunctionContext, MethodContext] -def fail(ctx: PluginContext, msg: str, context: Optional[Context]) -> None: +def fail(ctx: PluginContext, msg: ErrorMessage, context: Optional[Context]) -> None: """Emit an error message. This tries to emit an error message at the location specified by `context`, falling back to the @@ -95,7 +97,7 @@ def create_singledispatch_function_callback(ctx: FunctionContext) -> Type: if len(func_type.arg_kinds) < 1: fail( ctx, - 'Singledispatch function requires at least one argument', + message_registry.SINGLEDISPATCH_ATLEAST_ONE_ARG, func_type.definition, ) return ctx.default_return_type @@ -103,7 +105,7 @@ def create_singledispatch_function_callback(ctx: FunctionContext) -> Type: elif not func_type.arg_kinds[0].is_positional(star=True): fail( ctx, - 'First argument to singledispatch function must be a positional argument', + message_registry.SINGLEDISPATCH_FIRST_ARG_POSITIONAL, func_type.definition, ) return ctx.default_return_type @@ -173,7 +175,7 @@ def register_function(ctx: PluginContext, singledispatch_obj: Instance, func: Ty fallback_dispatch_type = fallback.arg_types[0] if not is_subtype(dispatch_type, fallback_dispatch_type): - fail(ctx, 'Dispatch type {} must be subtype of fallback function first argument {}'.format( + fail(ctx, message_registry.DISPATCH_TYPE_FALLBACK_SUBTYPE.format( format_type(dispatch_type), format_type(fallback_dispatch_type) ), func.definition) return diff --git a/mypy/semanal.py b/mypy/semanal.py index 49ec5c88f30f..ade796d64b8f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -86,6 +86,7 @@ best_matches, MessageBuilder, pretty_seq, SUGGESTED_TEST_FIXTURES, TYPES_FOR_UNIMPORTED_HINTS ) from mypy.errorcodes import ErrorCode +from mypy.message_registry import ErrorMessage from mypy import message_registry, errorcodes as codes from mypy.types import ( FunctionLike, UnboundType, TypeVarType, TupleType, UnionType, StarType, @@ -659,7 +660,7 @@ def prepare_method_signature(self, func: FuncDef, info: TypeInfo) -> None: if func.name in ['__init_subclass__', '__class_getitem__']: func.is_class = True if not func.arguments: - self.fail('Method must have at least one argument', func) + self.fail(message_registry.METHOD_ATLEAST_ONE_ARG, func) elif isinstance(functype, CallableType): self_type = get_proper_type(functype.arg_types[0]) if isinstance(self_type, AnyType): @@ -824,11 +825,9 @@ def handle_missing_overload_decorators(self, # Some of them were overloads, but not all. for idx in non_overload_indexes: if self.is_stub_file: - self.fail("An implementation for an overloaded function " - "is not allowed in a stub file", defn.items[idx]) + self.fail(message_registry.OVERLOAD_IMPLEMENTATION_IN_STUB, defn.items[idx]) else: - self.fail("The implementation for an overloaded function " - "must come last", defn.items[idx]) + self.fail(message_registry.OVERLOAD_IMPLEMENTATION_LAST, defn.items[idx]) else: for idx in non_overload_indexes[1:]: self.name_already_defined(defn.name, defn.items[idx], defn.items[0]) @@ -849,9 +848,7 @@ def handle_missing_overload_implementation(self, defn: OverloadedFuncDef) -> Non else: item.is_abstract = True else: - self.fail( - "An overloaded function outside a stub file must have an implementation", - defn) + self.fail(message_registry.OVERLOAD_IMPLEMENTATION_REQUIRED, defn) def process_final_in_overload(self, defn: OverloadedFuncDef) -> None: """Detect the @final status of an overloaded function (and perform checks).""" @@ -863,12 +860,10 @@ def process_final_in_overload(self, defn: OverloadedFuncDef) -> None: # Only show the error once per overload bad_final = next(ov for ov in defn.items if ov.is_final) if not self.is_stub_file: - self.fail("@final should be applied only to overload implementation", - bad_final) + self.fail(message_registry.FINAL_DEC_ON_OVERLOAD_ONLY, bad_final) elif any(item.is_final for item in defn.items[1:]): bad_final = next(ov for ov in defn.items[1:] if ov.is_final) - self.fail("In a stub file @final must be applied only to the first overload", - bad_final) + self.fail(message_registry.FINAL_DEC_STUB_FIRST_OVERLOAD, bad_final) if defn.impl is not None and defn.impl.is_final: defn.is_final = True @@ -923,10 +918,10 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - # Get abstractness from the original definition. item.func.is_abstract = first_item.func.is_abstract else: - self.fail("Decorated property not supported", item) + self.fail(message_registry.DECORATED_PROPERTY_UNSUPPORTED, item) item.func.accept(self) else: - self.fail('Unexpected definition for property "{}"'.format(first_item.func.name), + self.fail(message_registry.UNEXPECTED_PROPERTY_DEFN.format(first_item.func.name), item) deleted_items.append(i + 1) for i in reversed(deleted_items): @@ -985,13 +980,13 @@ def check_function_signature(self, fdef: FuncItem) -> None: sig = fdef.type assert isinstance(sig, CallableType) if len(sig.arg_types) < len(fdef.arguments): - self.fail('Type signature has too few arguments', fdef) + self.fail(message_registry.TYPE_SIGNATURE_TOO_FEW_ARGS, fdef) # Add dummy Any arguments to prevent crashes later. num_extra_anys = len(fdef.arguments) - len(sig.arg_types) extra_anys = [AnyType(TypeOfAny.from_error)] * num_extra_anys sig.arg_types.extend(extra_anys) elif len(sig.arg_types) > len(fdef.arguments): - self.fail('Type signature has too many arguments', fdef, blocker=True) + self.fail(message_registry.TYPE_SIGNATURE_TOO_MANY_ARGS, fdef, blocker=True) def visit_decorator(self, dec: Decorator) -> None: self.statement = dec @@ -1037,7 +1032,7 @@ def visit_decorator(self, dec: Decorator) -> None: dec.var.is_settable_property = True self.check_decorated_function_is_method('property', dec) if len(dec.func.arguments) > 1: - self.fail('Too many arguments', dec.func) + self.fail(message_registry.TOO_MANY_ARGS, dec.func) elif refers_to_fullname(d, 'typing.no_type_check'): dec.var.type = AnyType(TypeOfAny.special_form) no_type_check = True @@ -1052,7 +1047,7 @@ def visit_decorator(self, dec: Decorator) -> None: dec.var.is_final = True removed.append(i) else: - self.fail("@final cannot be used with non-method functions", d) + self.fail(message_registry.FINAL_DEC_WITH_METHODS_ONLY, d) for i in reversed(removed): del dec.decorators[i] if (not dec.is_overload or dec.var.is_property) and self.type: @@ -1061,12 +1056,12 @@ def visit_decorator(self, dec: Decorator) -> None: if not no_type_check and self.recurse_into_functions: dec.func.accept(self) if dec.decorators and dec.var.is_property: - self.fail('Decorated property not supported', dec) + self.fail(message_registry.DECORATED_PROPERTY_UNSUPPORTED, dec) def check_decorated_function_is_method(self, decorator: str, context: Context) -> None: if not self.type or self.is_func_scope(): - self.fail('"%s" used with a non-method' % decorator, context) + self.fail(message_registry.DECORATOR_USED_WITH_NON_METHOD.format(decorator), context) # # Classes @@ -1129,7 +1124,7 @@ def analyze_class(self, defn: ClassDef) -> None: if isinstance(decorator, RefExpr): if decorator.fullname in ('typing.final', 'typing_extensions.final'): - self.fail("@final cannot be used with TypedDict", decorator) + self.fail(message_registry.CANNOT_USE_FINAL_DEC_WITH_TYPEDDICT, decorator) if info is None: self.mark_incomplete(defn.name, defn) else: @@ -1253,7 +1248,7 @@ def analyze_class_decorator(self, defn: ClassDef, decorator: Expression) -> None if defn.info.is_protocol: defn.info.runtime_protocol = True else: - self.fail('@runtime_checkable can only be used with protocol classes', + self.fail(message_registry.RUNTIME_CHECKABLE_WITH_NON_PROPERTY, defn) elif decorator.fullname in ('typing.final', 'typing_extensions.final'): @@ -1293,7 +1288,7 @@ class Foo(Bar, Generic[T]): ... result = self.analyze_class_typevar_declaration(base) if result is not None: if declared_tvars: - self.fail('Only single Generic[...] or Protocol[...] can be in bases', context) + self.fail(message_registry.BASES_MUST_HAVE_SINGLE_GENERIC_OR_PROTOCOL, context) removed.append(i) tvars = result[0] is_protocol |= result[1] @@ -1310,11 +1305,10 @@ class Foo(Bar, Generic[T]): ... all_tvars = self.get_all_bases_tvars(base_type_exprs, removed) if declared_tvars: if len(remove_dups(declared_tvars)) < len(declared_tvars): - self.fail("Duplicate type variables in Generic[...] or Protocol[...]", context) + self.fail(message_registry.DUPLICATE_TYPEVARS_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) + self.fail(message_registry.GENERIC_PROTOCOL_NOT_ALL_TYPEVARS, context) # In case of error, Generic tvars will go first declared_tvars = remove_dups(declared_tvars + all_tvars) else: @@ -1363,8 +1357,7 @@ def analyze_class_typevar_declaration( if tvar: tvars.append(tvar) elif not self.found_incomplete_ref(tag): - self.fail('Free type variable expected in %s[...]' % - sym.node.name, base) + self.fail(message_registry.FREE_TYPEVAR_EXPECTED.format(sym.node.name), base) return tvars, is_proto return None @@ -1489,12 +1482,14 @@ def analyze_base_classes( except TypeTranslationError: name = self.get_name_repr_of_expr(base_expr) if isinstance(base_expr, CallExpr): - msg = 'Unsupported dynamic base class' + msg = message_registry.UNSUPPORTED_DYNAMIC_BASE_CLASS else: - msg = 'Invalid base class' + msg = message_registry.INVALID_BASE_CLASS + extra = '' if name: - msg += ' "{}"'.format(name) - self.fail(msg, base_expr) + extra += ' "{}"'.format(name) + + self.fail(msg.format(extra), base_expr) is_error = True continue if base is None: @@ -1522,22 +1517,24 @@ def configure_base_classes(self, base_types.append(actual_base) elif isinstance(base, Instance): if base.type.is_newtype: - self.fail('Cannot subclass "NewType"', defn) + self.fail(message_registry.CANNOT_SUBCLASS_NEWTYPE, defn) base_types.append(base) elif isinstance(base, AnyType): if self.options.disallow_subclassing_any: if isinstance(base_expr, (NameExpr, MemberExpr)): - msg = 'Class cannot subclass "{}" (has type "Any")'.format(base_expr.name) + msg = message_registry.CANNOT_SUBCLASS_ANY_NAMED.format(base_expr.name) else: - msg = 'Class cannot subclass value of type "Any"' + msg = message_registry.CANNOT_SUBCLASS_ANY self.fail(msg, base_expr) info.fallback_to_any = True else: - msg = 'Invalid base class' + msg = message_registry.INVALID_BASE_CLASS name = self.get_name_repr_of_expr(base_expr) + extra = '' if name: - msg += ' "{}"'.format(name) - self.fail(msg, base_expr) + extra += ' "{}"'.format(name) + + self.fail(msg.format(extra), base_expr) info.fallback_to_any = True if self.options.disallow_any_unimported and has_any_from_unimported_type(base): if isinstance(base_expr, (NameExpr, MemberExpr)): @@ -1569,7 +1566,7 @@ def configure_tuple_base_class(self, # There may be an existing valid tuple type from previous semanal iterations. # Use equality to check if it is the case. if info.tuple_type and info.tuple_type != base: - self.fail("Class has two incompatible bases derived from tuple", defn) + self.fail(message_registry.INCOMPATIBLE_BASES, defn) defn.has_incompatible_baseclass = True info.tuple_type = base if isinstance(base_expr, CallExpr): @@ -1600,8 +1597,7 @@ def calculate_class_mro(self, defn: ClassDef, try: calculate_mro(defn.info, obj_type) except MroError: - self.fail('Cannot determine consistent method resolution ' - 'order (MRO) for "%s"' % defn.name, defn) + self.fail(message_registry.CANNOT_DETERMINE_MRO.format(defn.name), defn) self.set_dummy_mro(defn.info) # Allow plugins to alter the MRO to handle the fact that `def mro()` # on metaclasses permits MRO rewriting. @@ -1625,7 +1621,7 @@ def update_metaclass(self, defn: ClassDef) -> None: if self.options.python_version[0] == 2: for body_node in defn.defs.body: if isinstance(body_node, ClassDef) and body_node.name == "__metaclass__": - self.fail("Metaclasses defined as inner classes are not supported", body_node) + self.fail(message_registry.INNER_METACLASS_UNSUPPORTED, body_node) break elif isinstance(body_node, AssignmentStmt) and len(body_node.lvalues) == 1: lvalue = body_node.lvalues[0] @@ -1661,7 +1657,7 @@ def update_metaclass(self, defn: ClassDef) -> None: if len(metas) == 0: return if len(metas) > 1: - self.fail("Multiple metaclass definitions", defn) + self.fail(message_registry.MULTIPLE_METACLASSES, defn) return defn.metaclass = metas.pop() @@ -1671,15 +1667,16 @@ def verify_base_classes(self, defn: ClassDef) -> bool: for base in info.bases: baseinfo = base.type if self.is_base_class(info, baseinfo): - self.fail('Cycle in inheritance hierarchy', defn) + self.fail(message_registry.INHERITANCE_CYCLE, defn) cycle = True if baseinfo.fullname == 'builtins.bool': - self.fail('"%s" is not a valid base class' % - baseinfo.name, defn, blocker=True) + self.fail(message_registry.NAMED_INVALID_BASE_CLASS.format(baseinfo.name), + defn, + blocker=True) return False dup = find_duplicate(info.direct_base_classes()) if dup: - self.fail('Duplicate base class "%s"' % dup.name, defn, blocker=True) + self.fail(message_registry.DUPLICATE_BASE_CLASS.format(dup.name), defn, blocker=True) return False return not cycle @@ -1706,7 +1703,8 @@ def analyze_metaclass(self, defn: ClassDef) -> None: elif isinstance(defn.metaclass, MemberExpr): metaclass_name = get_member_expr_fullname(defn.metaclass) if metaclass_name is None: - self.fail('Dynamic metaclass not supported for "%s"' % defn.name, defn.metaclass) + self.fail(message_registry.UNSUPPORTED_NAMED_DYNAMIC_BASE_CLASS.format(defn.name), + defn.metaclass) return sym = self.lookup_qualified(metaclass_name, defn.metaclass) if sym is None: @@ -1723,10 +1721,10 @@ def analyze_metaclass(self, defn: ClassDef) -> None: self.defer(defn) return if not isinstance(sym.node, TypeInfo) or sym.node.tuple_type is not None: - self.fail('Invalid metaclass "%s"' % metaclass_name, defn.metaclass) + self.fail(message_registry.INVALID_METACLASS.format(metaclass_name), defn.metaclass) return if not sym.node.is_metaclass(): - self.fail('Metaclasses not inheriting from "type" are not supported', + self.fail(message_registry.METACLASS_MUST_INHERIT_TYPE, defn.metaclass) return inst = fill_typevars(sym.node) @@ -1745,12 +1743,12 @@ def analyze_metaclass(self, defn: ClassDef) -> None: # Inconsistency may happen due to multiple baseclasses even in classes that # do not declare explicit metaclass, but it's harder to catch at this stage if defn.metaclass is not None: - self.fail('Inconsistent metaclass structure for "%s"' % defn.name, defn) + self.fail(message_registry.INCONSISTENT_METACLAS_STRUCTURE.format(defn.name), defn) else: if defn.info.metaclass_type.type.has_base('enum.EnumMeta'): defn.info.is_enum = True if defn.type_vars: - self.fail("Enum class cannot be generic", defn) + self.fail(message_registry.NO_GENERIC_ENUM, defn) # # Imports @@ -1890,20 +1888,20 @@ def report_missing_module_attribute( # is incomplete. Defer the current target. self.mark_incomplete(imported_id, context) return - message = 'Module "{}" has no attribute "{}"'.format(import_id, source_id) + message = message_registry.MODULE_MISSING_ATTIRBUTE + suggestion = '' # Suggest alternatives, if any match is found. module = self.modules.get(import_id) if module: if not self.options.implicit_reexport and source_id in module.names.keys(): - message = ('Module "{}" does not explicitly export attribute "{}"' - '; implicit reexport disabled'.format(import_id, source_id)) + message = (message_registry.NO_IMPLICIT_REEXPORT.format(import_id, source_id)) else: alternatives = set(module.names.keys()).difference({source_id}) matches = best_matches(source_id, alternatives)[:3] if matches: suggestion = "; maybe {}?".format(pretty_seq(matches, "or")) - message += "{}".format(suggestion) - self.fail(message, context, code=codes.ATTR_DEFINED) + message = message.format(import_id, source_id, suggestion) + self.fail(message, context) self.add_unknown_imported_symbol( imported_id, context, target_name=None, module_public=module_public, module_hidden=not module_public @@ -1949,7 +1947,7 @@ def correct_relative_import(self, node: Union[ImportFrom, ImportAll]) -> str: import_id, ok = correct_relative_import(self.cur_mod_id, node.relative, node.id, self.cur_mod_node.is_package_init_file()) if not ok: - self.fail("Relative import climbs too many namespaces", node) + self.fail(message_registry.INCORRECT_RELATIVE_IMPORT, node) return import_id def visit_import_all(self, i: ImportAll) -> None: @@ -2162,7 +2160,7 @@ def is_type_ref(self, rv: Expression, bare: bool = False) -> bool: if not isinstance(rv, RefExpr): return False if isinstance(rv.node, TypeVarExpr): - self.fail('Type variable "{}" is invalid as target for type alias'.format( + self.fail(message_registry.INVALID_TYPE_ALIAS_TARGET.format( rv.fullname), rv) return False @@ -2243,11 +2241,11 @@ def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool: if internal_name is None: return False if isinstance(lvalue, MemberExpr): - self.fail("NamedTuple type as an attribute is not supported", lvalue) + self.fail(message_registry.NAMEDTUPLE_ATTRIBUTE_UNSUPPORTED, lvalue) return False if internal_name != name: - self.fail('First argument to namedtuple() should be "{}", not "{}"'.format( - name, internal_name), s.rvalue, code=codes.NAME_MATCH) + self.fail(message_registry.NAMEDTUPLE_INCORRECT_FIRST_ARG.format( + name, internal_name), s.rvalue) return True # Yes, it's a valid namedtuple, but defer if it is not ready. if not info: @@ -2267,7 +2265,7 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool: if not is_typed_dict: return False if isinstance(lvalue, MemberExpr): - self.fail("TypedDict type as attribute is not supported", lvalue) + self.fail(message_registry.TYPEDDICT_ATTRIBUTE_UNSUPPORTED, lvalue) return False # Yes, it's a valid typed dict, but defer if it is not ready. if not info: @@ -2323,22 +2321,22 @@ def unwrap_final(self, s: AssignmentStmt) -> bool: return False assert isinstance(s.unanalyzed_type, UnboundType) if len(s.unanalyzed_type.args) > 1: - self.fail("Final[...] takes at most one type argument", s.unanalyzed_type) + self.fail(message_registry.FINAL_ATMOST_ONE_ARG, s.unanalyzed_type) invalid_bare_final = False if not s.unanalyzed_type.args: s.type = None if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: invalid_bare_final = True - self.fail("Type in Final[...] can only be omitted if there is an initializer", s) + self.fail(message_registry.FINAL_INITIALIZER_REQUIRED, s) else: s.type = s.unanalyzed_type.args[0] if s.type is not None and self.is_classvar(s.type): - self.fail("Variable should not be annotated with both ClassVar and Final", s) + self.fail(message_registry.FINAL_CLASSVAR_DISALLOWED, s) return False if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], RefExpr): - self.fail("Invalid final declaration", s) + self.fail(message_registry.INVALID_FINAL, s) return False lval = s.lvalues[0] assert isinstance(lval, RefExpr) @@ -2350,7 +2348,7 @@ def unwrap_final(self, s: AssignmentStmt) -> bool: lval.is_inferred_def = s.type is None if self.loop_depth > 0: - self.fail("Cannot use Final inside a loop", s) + self.fail(message_registry.FINAL_IN_LOOP_DISALLOWED, s) if self.type and self.type.is_protocol: self.msg.protocol_members_cant_be_final(s) if (isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs and @@ -2370,13 +2368,13 @@ def check_final_implicit_def(self, s: AssignmentStmt) -> None: assert isinstance(lval, RefExpr) if isinstance(lval, MemberExpr): if not self.is_self_member_ref(lval): - self.fail("Final can be only applied to a name or an attribute on self", s) + self.fail(message_registry.FINAL_ONLY_ON_SELF_MEMBER, s) s.is_final_def = False return else: assert self.function_stack if self.function_stack[-1].name != '__init__': - self.fail("Can only declare a final attribute in class body or __init__", s) + self.fail(message_registry.FINAL_ONLY_IN_CLASS_BODY_OR_INIT, s) s.is_final_def = False return @@ -2442,7 +2440,7 @@ def process_type_annotation(self, s: AssignmentStmt) -> None: else: if (self.type and self.type.is_protocol and self.is_annotated_protocol_member(s) and not self.is_func_scope()): - self.fail('All protocol members must have explicitly declared types', s) + self.fail(message_registry.PROTOCOL_MEMBERS_MUST_BE_TYPED, s) # Set the type if the rvalue is a simple literal (even if the above error occurred). if len(s.lvalues) == 1 and isinstance(s.lvalues[0], RefExpr): if s.lvalues[0].is_inferred_def: @@ -2575,8 +2573,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: # TODO: find a more robust way to track the order of definitions. # Note: if is_alias_def=True, this is just a node from previous iteration. if isinstance(existing.node, TypeAlias) and not s.is_alias_def: - self.fail('Cannot assign multiple types to name "{}"' - ' without an explicit "Type[...]" annotation' + self.fail(message_registry.MULTIPLE_TYPES_WITHOUT_EXPLICIT_TYPE .format(lvalue.name), lvalue) return False @@ -2708,11 +2705,10 @@ def analyze_lvalue(self, elif isinstance(lval, MemberExpr): self.analyze_member_lvalue(lval, explicit_type, is_final) if explicit_type and not self.is_self_member_ref(lval): - self.fail('Type cannot be declared in assignment to non-self ' - 'attribute', lval) + self.fail(message_registry.TYPE_DECLARATION_IN_ASSIGNMENT, lval) elif isinstance(lval, IndexExpr): if explicit_type: - self.fail('Unexpected type declaration', lval) + self.fail(message_registry.UNEXPECTED_TYPE_DECLARATION, lval) lval.accept(self) elif isinstance(lval, TupleExpr): self.analyze_tuple_or_list_lvalue(lval, explicit_type) @@ -2720,9 +2716,9 @@ def analyze_lvalue(self, if nested: self.analyze_lvalue(lval.expr, nested, explicit_type) else: - self.fail('Starred assignment target must be in a list or tuple', lval) + self.fail(message_registry.STAR_ASSIGNMENT_TARGET_LIST_OR_TUPLE, lval) else: - self.fail('Invalid assignment target', lval) + self.fail(message_registry.INVALID_ASSIGNMENT_TARGET, lval) def analyze_name_lvalue(self, lvalue: NameExpr, @@ -2740,7 +2736,7 @@ def analyze_name_lvalue(self, name = lvalue.name if self.is_alias_for_final_name(name): if is_final: - self.fail("Cannot redefine an existing name as final", lvalue) + self.fail(message_registry.REDEFINE_AS_FINAL, lvalue) else: self.msg.cant_assign_to_final(name, self.type is not None, lvalue) @@ -2769,7 +2765,7 @@ def analyze_name_lvalue(self, typ = AnyType(TypeOfAny.special_form) self.store_declared_types(lvalue, typ) if is_final and self.is_final_redefinition(kind, name): - self.fail("Cannot redefine an existing name as final", lvalue) + self.fail(message_registry.REDEFINE_AS_FINAL, lvalue) else: self.make_name_lvalue_point_to_existing_def(lvalue, explicit_type, is_final) @@ -2832,7 +2828,7 @@ def make_name_lvalue_point_to_existing_def( """ if is_final: # Redefining an existing name with final is always an error. - self.fail("Cannot redefine an existing name as final", lval) + self.fail(message_registry.REDEFINE_AS_FINAL, lval) original_def = self.lookup(lval.name, lval, suppress_errors=True) if original_def is None and self.type and not self.is_func_scope(): # Workaround to allow "x, x = ..." in class body. @@ -2855,7 +2851,7 @@ def analyze_tuple_or_list_lvalue(self, lval: TupleExpr, star_exprs = [item for item in items if isinstance(item, StarExpr)] if len(star_exprs) > 1: - self.fail('Two starred expressions in assignment', lval) + self.fail(message_registry.TWO_STAR_EXPRESSIONS_IN_ASSIGNMENT, lval) else: if len(star_exprs) == 1: star_exprs[0].valid = True @@ -2880,7 +2876,7 @@ def analyze_member_lvalue(self, lval: MemberExpr, explicit_type: bool, is_final: node = self.type.get(lval.name) if cur_node and is_final: # Overrides will be checked in type checker. - self.fail("Cannot redefine an existing name as final", lval) + self.fail(message_registry.REDEFINE_AS_FINAL, lval) # On first encounter with this definition, if this attribute was defined before # with an inferred type and it's marked with an explicit type now, give an error. if (not lval.node and cur_node and isinstance(cur_node.node, Var) and @@ -2894,7 +2890,7 @@ def analyze_member_lvalue(self, lval: MemberExpr, explicit_type: bool, is_final: # so we also check if `is_final` is passed. or (cur_node is None and (explicit_type or is_final))): if self.type.is_protocol and node is None: - self.fail("Protocol members cannot be defined via assignment to self", lval) + self.fail(message_registry.PROTOCOL_ASSIGNMENT_TO_SELF, lval) else: # Implicit attribute definition in __init__. lval.is_new_def = True @@ -2921,13 +2917,13 @@ def is_self_member_ref(self, memberexpr: MemberExpr) -> bool: def check_lvalue_validity(self, node: Union[Expression, SymbolNode, None], ctx: Context) -> None: if isinstance(node, TypeVarExpr): - self.fail('Invalid assignment target', ctx) + self.fail(message_registry.INVALID_ASSIGNMENT_TARGET, ctx) elif isinstance(node, TypeInfo): self.fail(message_registry.CANNOT_ASSIGN_TO_TYPE, ctx) def store_declared_types(self, lvalue: Lvalue, typ: Type) -> None: if isinstance(typ, StarType) and not isinstance(lvalue, StarExpr): - self.fail('Star type only allowed for starred expressions', lvalue) + self.fail(message_registry.STARTYPE_ONLY_FOR_STAR_EXPRESSIONS, lvalue) if isinstance(lvalue, RefExpr): lvalue.is_inferred_def = False if isinstance(lvalue.node, Var): @@ -2939,13 +2935,12 @@ def store_declared_types(self, lvalue: Lvalue, typ: Type) -> None: typ = get_proper_type(typ) if isinstance(typ, TupleType): if len(lvalue.items) != len(typ.items): - self.fail('Incompatible number of tuple items', lvalue) + self.fail(message_registry.INCOMPATIBLE_TUPLE_ITEM_COUNT, lvalue) return for item, itemtype in zip(lvalue.items, typ.items): self.store_declared_types(item, itemtype) else: - self.fail('Tuple type expected for multiple variables', - lvalue) + self.fail(message_registry.TUPLE_TYPE_EXPECTED, lvalue) elif isinstance(lvalue, StarExpr): # Historical behavior for the old parser if isinstance(typ, StarType): @@ -2969,7 +2964,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: lvalue = s.lvalues[0] assert isinstance(lvalue, NameExpr) if s.type: - self.fail("Cannot declare the type of a type variable", s) + self.fail(message_registry.CANNOT_DECLARE_TYPE_OF_TYPEVAR, s) return False name = lvalue.name @@ -2994,7 +2989,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: # Also give error for another type variable with the same name. (isinstance(existing.node, TypeVarExpr) and existing.node is call.analyzed)): - self.fail('Cannot redefine "%s" as a type variable' % name, s) + self.fail(message_registry.REDEFINE_AS_TYPEVAR.format(name), s) return False if self.options.disallow_any_unimported: @@ -3041,15 +3036,15 @@ def check_typevarlike_name(self, call: CallExpr, name: str, context: Context) -> call.callee.name if isinstance(call.callee, NameExpr) else call.callee.fullname ) if len(call.args) < 1: - self.fail("Too few arguments for {}()".format(typevarlike_type), context) + self.fail(message_registry.TYPEVAR_CALL_TOO_FEW_ARGS.format(typevarlike_type), context) return False if (not isinstance(call.args[0], (StrExpr, BytesExpr, UnicodeExpr)) or not call.arg_kinds[0] == ARG_POS): - self.fail("{}() expects a string literal as first argument".format(typevarlike_type), + self.fail(message_registry.TYPEVAR_CALL_EXPECTED_STRING_LITERAL.format(typevarlike_type), context) return False elif call.args[0].value != name: - msg = 'String argument 1 "{}" to {}(...) does not match variable name "{}"' + msg = message_registry.TYPEVAR_NAME_ARG_MISMATCH self.fail(msg.format(call.args[0].value, typevarlike_type, name), context) return False return True @@ -3082,31 +3077,31 @@ def process_typevar_parameters(self, args: List[Expression], upper_bound: Type = self.object_type() for param_value, param_name, param_kind in zip(args, names, kinds): if not param_kind == ARG_NAMED: - self.fail("Unexpected argument to TypeVar()", context) + self.fail(message_registry.TYPEVAR_UNEXPECTED_ARG, context) return None if param_name == 'covariant': if isinstance(param_value, NameExpr): if param_value.name == 'True': covariant = True else: - self.fail("TypeVar 'covariant' may only be 'True'", context) + self.fail(message_registry.TYPEVAR_COVARIANT_MUST_BE_TRUE, context) return None else: - self.fail("TypeVar 'covariant' may only be 'True'", context) + self.fail(message_registry.TYPEVAR_COVARIANT_MUST_BE_TRUE, context) return None elif param_name == 'contravariant': if isinstance(param_value, NameExpr): if param_value.name == 'True': contravariant = True else: - self.fail("TypeVar 'contravariant' may only be 'True'", context) + self.fail(message_registry.TYPEVAR_CONTRAVARIANT_MUST_BE_TRUE, context) return None else: - self.fail("TypeVar 'contravariant' may only be 'True'", context) + self.fail(message_registry.TYPEVAR_CONTRAVARIANT_MUST_BE_TRUE, context) return None elif param_name == 'bound': if has_values: - self.fail("TypeVar cannot have both values and an upper bound", context) + self.fail(message_registry.TYPEVAR_VALUE_WITH_BOUND_DISALLOWED, context) return None try: # We want to use our custom error message below, so we suppress @@ -3124,27 +3119,26 @@ def process_typevar_parameters(self, args: List[Expression], analyzed = PlaceholderType(None, [], context.line) upper_bound = get_proper_type(analyzed) if isinstance(upper_bound, AnyType) and upper_bound.is_from_error: - self.fail('TypeVar "bound" must be a type', param_value) + self.fail(message_registry.TYPEVAR_BOUND_MUST_BE_TYPE, param_value) # Note: we do not return 'None' here -- we want to continue # using the AnyType as the upper bound. except TypeTranslationError: - self.fail('TypeVar "bound" must be a type', param_value) + self.fail(message_registry.TYPEVAR_BOUND_MUST_BE_TYPE, param_value) return None elif param_name == 'values': # Probably using obsolete syntax with values=(...). Explain the current syntax. - self.fail('TypeVar "values" argument not supported', context) - self.fail("Use TypeVar('T', t, ...) instead of TypeVar('T', values=(t, ...))", - context) + self.fail(message_registry.TYPEVAR_VALUES_ARG_UNSUPPORTED, context) + self.fail(message_registry.USE_NEW_TYPEVAR_SYNTAX, context) return None else: - self.fail('Unexpected argument to TypeVar(): "{}"'.format(param_name), context) + self.fail(message_registry.TYPEVAR_UNEXPECTED_ARG_NAMED.format(param_name), context) return None if covariant and contravariant: - self.fail("TypeVar cannot be both covariant and contravariant", context) + self.fail(message_registry.TYPEVAR_COVARIANT_AND_CONTRAVARIANT, context) return None elif num_values == 1: - self.fail("TypeVar cannot have only a single constraint", context) + self.fail(message_registry.TYPEVAR_SINGLE_CONSTRAINT, context) return None elif covariant: variance = COVARIANT @@ -3171,7 +3165,7 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: lvalue = s.lvalues[0] assert isinstance(lvalue, NameExpr) if s.type: - self.fail("Cannot declare the type of a parameter specification", s) + self.fail(message_registry.CANNOT_DECLARE_TYPE_OF_PARAMSPEC, s) return False name = lvalue.name @@ -3232,7 +3226,7 @@ def analyze_value_types(self, items: List[Expression]) -> List[Type]: analyzed = PlaceholderType(None, [], node.line) result.append(analyzed) except TypeTranslationError: - self.fail('Type expected', node) + self.fail(message_registry.TYPE_EXPECTED, node) result.append(AnyType(TypeOfAny.from_error)) return result @@ -3269,7 +3263,7 @@ def is_final_type(self, typ: Optional[Type]) -> bool: return sym.node.fullname in ('typing.Final', 'typing_extensions.Final') def fail_invalid_classvar(self, context: Context) -> None: - self.fail('ClassVar can only be used for assignments in class body', context) + self.fail(message_registry.CLASSVAR_OUTSIDE_CLASS_BODY, context) def process_module_assignment(self, lvals: List[Lvalue], rval: Expression, ctx: AssignmentStmt) -> None: @@ -3331,10 +3325,9 @@ def process_module_assignment(self, lvals: List[Lvalue], rval: Expression, if lnode: if isinstance(lnode.node, MypyFile) and lnode.node is not rnode.node: assert isinstance(lval, (NameExpr, MemberExpr)) - self.fail( - 'Cannot assign multiple modules to name "{}" ' - 'without explicit "types.ModuleType" annotation'.format(lval.name), - ctx) + self.fail(message_registry.MULTIPLE_MODULE_ASSIGNMENT + .format(lval.name), + ctx) # never create module alias except on initial var definition elif lval.is_inferred_def: assert rnode.node is not None @@ -3354,13 +3347,13 @@ def process__deletable__(self, s: AssignmentStmt) -> None: s.lvalues[0].name == '__deletable__' and s.lvalues[0].kind == MDEF): rvalue = s.rvalue if not isinstance(rvalue, (ListExpr, TupleExpr)): - self.fail('"__deletable__" must be initialized with a list or tuple expression', s) + self.fail(message_registry.DELETABLE_MUST_BE_WITH_LIST_OR_TUPLE, s) return items = rvalue.items attrs = [] for item in items: if not isinstance(item, StrExpr): - self.fail('Invalid "__deletable__" item; string literal expected', item) + self.fail(message_registry.DELETABLE_EXPECTED_STRING_LITERAL, item) else: attrs.append(item.value) assert self.type @@ -3389,7 +3382,7 @@ def visit_expression_stmt(self, s: ExpressionStmt) -> None: def visit_return_stmt(self, s: ReturnStmt) -> None: self.statement = s if not self.is_func_scope(): - self.fail('"return" outside function', s) + self.fail(message_registry.RETURN_OUTSIDE_FUNCTION, s) if s.expr: s.expr.accept(self) @@ -3448,12 +3441,12 @@ def visit_for_stmt(self, s: ForStmt) -> None: def visit_break_stmt(self, s: BreakStmt) -> None: self.statement = s if self.loop_depth == 0: - self.fail('"break" outside loop', s, serious=True, blocker=True) + self.fail(message_registry.BREAK_OUTSIDE_LOOP, s, serious=True, blocker=True) def visit_continue_stmt(self, s: ContinueStmt) -> None: self.statement = s if self.loop_depth == 0: - self.fail('"continue" outside loop', s, serious=True, blocker=True) + self.fail(message_registry.CONTINUE_OUTSIDE_LOOP, s, serious=True, blocker=True) def visit_if_stmt(self, s: IfStmt) -> None: self.statement = s @@ -3489,7 +3482,7 @@ def visit_with_stmt(self, s: WithStmt) -> None: actual_targets = [t for t in s.target if t is not None] if len(actual_targets) == 0: # We have a type for no targets - self.fail('Invalid type comment: "with" statement has no targets', s) + self.fail(message_registry.WITH_HAS_NO_TARGETS, s) elif len(actual_targets) == 1: # We have one target and one type types = [s.unanalyzed_type] @@ -3499,10 +3492,10 @@ def visit_with_stmt(self, s: WithStmt) -> None: types = s.unanalyzed_type.items.copy() else: # But it's the wrong number of items - self.fail('Incompatible number of types for "with" targets', s) + self.fail(message_registry.WITH_INCOMPATIBLE_TARGET_COUNT, s) else: # We have multiple targets and one type - self.fail('Multiple types expected for multiple "with" targets', s) + self.fail(message_registry.WITH_MULTIPLE_TYPES_EXPECTED, s) new_types: List[Type] = [] for e, n in zip(s.expr, s.target): @@ -3530,7 +3523,7 @@ def visit_del_stmt(self, s: DelStmt) -> None: self.statement = s s.expr.accept(self) if not self.is_valid_del_target(s.expr): - self.fail('Invalid delete target', s) + self.fail(message_registry.INVALID_DELETE_TARGET, s) def is_valid_del_target(self, s: Expression) -> bool: if isinstance(s, (IndexExpr, NameExpr, MemberExpr)): @@ -3544,27 +3537,26 @@ def visit_global_decl(self, g: GlobalDecl) -> None: self.statement = g for name in g.names: if name in self.nonlocal_decls[-1]: - self.fail('Name "{}" is nonlocal and global'.format(name), g) + self.fail(message_registry.NAME_IS_NONLOCAL_AND_GLOBAL.format(name), g) self.global_decls[-1].add(name) def visit_nonlocal_decl(self, d: NonlocalDecl) -> None: self.statement = d if not self.is_func_scope(): - self.fail("nonlocal declaration not allowed at module level", d) + self.fail(message_registry.NONLOCAL_AT_MODULE_LEVEL, d) else: for name in d.names: for table in reversed(self.locals[:-1]): if table is not None and name in table: break else: - self.fail('No binding for nonlocal "{}" found'.format(name), d) + self.fail(message_registry.NONLOCAL_NO_BINDING_FOUND.format(name), d) if self.locals[-1] is not None and name in self.locals[-1]: - self.fail('Name "{}" is already defined in local ' - 'scope before nonlocal declaration'.format(name), d) + self.fail(message_registry.LOCAL_DEFINITION_BEFORE_NONLOCAL.format(name), d) if name in self.global_decls[-1]: - self.fail('Name "{}" is nonlocal and global'.format(name), d) + self.fail(message_registry.NAME_IS_NONLOCAL_AND_GLOBAL.format(name), d) self.nonlocal_decls[-1].add(name) def visit_print_stmt(self, s: PrintStmt) -> None: @@ -3594,8 +3586,7 @@ def visit_name_expr(self, expr: NameExpr) -> None: def bind_name_expr(self, expr: NameExpr, sym: SymbolTableNode) -> None: """Bind name expression to a symbol table node.""" if isinstance(sym.node, TypeVarExpr) and self.tvar_scope.get_binding(sym): - self.fail('"{}" is a type variable and only valid in type ' - 'context'.format(expr.name), expr) + self.fail(message_registry.NAME_ONLY_VALID_IN_TYPE_CONTEXT.format(expr.name), expr) elif isinstance(sym.node, PlaceholderNode): self.process_placeholder(expr.name, 'name', expr) else: @@ -3605,7 +3596,7 @@ def bind_name_expr(self, expr: NameExpr, sym: SymbolTableNode) -> None: def visit_super_expr(self, expr: SuperExpr) -> None: if not self.type and not expr.call.args: - self.fail('"super" used outside class', expr) + self.fail(message_registry.SUPER_OUTSIDE_CLASS, expr) return expr.info = self.type for arg in expr.call.args: @@ -3638,16 +3629,16 @@ def visit_dict_expr(self, expr: DictExpr) -> None: def visit_star_expr(self, expr: StarExpr) -> None: if not expr.valid: # XXX TODO Change this error message - self.fail('Can use starred expression only as assignment target', expr) + self.fail(message_registry.INVALID_STAR_EXPRESSION, expr) else: expr.expr.accept(self) def visit_yield_from_expr(self, e: YieldFromExpr) -> None: if not self.is_func_scope(): # not sure - self.fail('"yield from" outside function', e, serious=True, blocker=True) + self.fail(message_registry.YIELD_FROM_OUTSIDE_FUNC, e, serious=True, blocker=True) else: if self.function_stack[-1].is_coroutine: - self.fail('"yield from" in async function', e, serious=True, blocker=True) + self.fail(message_registry.YIELD_FROM_IN_ASYNC_FUNC, e, serious=True, blocker=True) else: self.function_stack[-1].is_generator = True if e.expr: @@ -3668,7 +3659,7 @@ def visit_call_expr(self, expr: CallExpr) -> None: try: target = self.expr_to_unanalyzed_type(expr.args[0]) except TypeTranslationError: - self.fail('Cast target is not a type', expr) + self.fail(message_registry.CAST_TARGET_IS_NOT_TYPE, expr) return # Piggyback CastExpr object to the CallExpr object; it takes # precedence over the CallExpr semantics. @@ -3717,7 +3708,7 @@ def visit_call_expr(self, expr: CallExpr) -> None: expr.analyzed.accept(self) elif refers_to_fullname(expr.callee, 'typing.Any'): # Special form Any(...) no longer supported. - self.fail('Any(...) is no longer supported. Use cast(Any, ...) instead', expr) + self.fail(message_registry.ANY_CALL_UNSUPPORTED, expr) elif refers_to_fullname(expr.callee, 'typing._promote'): # Special form _promote(...). if not self.check_fixed_args(expr, 1, '_promote'): @@ -3726,7 +3717,7 @@ def visit_call_expr(self, expr: CallExpr) -> None: try: target = self.expr_to_unanalyzed_type(expr.args[0]) except TypeTranslationError: - self.fail('Argument 1 to _promote is not a type', expr) + self.fail(message_registry.PROMOTE_ARG_EXPECTED_TYPE, expr) return expr.analyzed = PromoteExpr(target) expr.analyzed.line = expr.line @@ -3781,12 +3772,10 @@ def check_fixed_args(self, expr: CallExpr, numargs: int, if numargs == 1: s = '' if len(expr.args) != numargs: - self.fail('"%s" expects %d argument%s' % (name, numargs, s), - expr) + self.fail(message_registry.ARG_COUNT_MISMATCH.format(name, numargs, s), expr) return False if expr.arg_kinds != [ARG_POS] * numargs: - self.fail('"%s" must be called with %s positional argument%s' % - (name, numargs, s), expr) + self.fail(message_registry.POS_ARG_COUNT_MISMATCH.format(name, numargs, s), expr) return False return True @@ -3922,7 +3911,7 @@ def analyze_type_application_args(self, expr: IndexExpr) -> Optional[List[Type]] try: typearg = self.expr_to_unanalyzed_type(item) except TypeTranslationError: - self.fail('Type expected within [...]', expr) + self.fail(message_registry.TYPE_EXPECTED_IN_BRACKETS, expr) return None # We always allow unbound type variables in IndexExpr, since we # may be analysing a type alias definition rvalue. The error will be @@ -4030,11 +4019,11 @@ def visit__promote_expr(self, expr: PromoteExpr) -> None: def visit_yield_expr(self, expr: YieldExpr) -> None: if not self.is_func_scope(): - self.fail('"yield" outside function', expr, serious=True, blocker=True) + self.fail(message_registry.YIELD_OUTSIDE_FUNC, expr, serious=True, blocker=True) else: if self.function_stack[-1].is_coroutine: if self.options.python_version < (3, 6): - self.fail('"yield" in async function', expr, serious=True, blocker=True) + self.fail(message_registry.YIELD_IN_ASYNC_FUNC, expr, serious=True, blocker=True) else: self.function_stack[-1].is_generator = True self.function_stack[-1].is_async_generator = True @@ -4045,9 +4034,9 @@ def visit_yield_expr(self, expr: YieldExpr) -> None: def visit_await_expr(self, expr: AwaitExpr) -> None: if not self.is_func_scope(): - self.fail('"await" outside function', expr) + self.fail(message_registry.AWAIT_OUTSIDE_FUNC, expr) elif not self.function_stack[-1].is_coroutine: - self.fail('"await" outside coroutine ("async def")', expr) + self.fail(message_registry.AWAIT_OUTSIDE_COROUTINE, expr) expr.expr.accept(self) # @@ -4706,7 +4695,7 @@ def process_placeholder(self, name: str, kind: str, ctx: Context) -> None: self.defer(ctx) def cannot_resolve_name(self, name: str, kind: str, ctx: Context) -> None: - self.fail('Cannot resolve {} "{}" (possible cyclic definition)'.format(kind, name), ctx) + self.fail(message_registry.CANNOT_RESOLVE_NAME.format(kind, name), ctx) def qualified_name(self, name: str) -> str: if self.type is not None: @@ -4804,9 +4793,9 @@ def check_no_global(self, if name in self.globals: prev_is_overloaded = isinstance(self.globals[name], OverloadedFuncDef) if is_overloaded_func and prev_is_overloaded: - self.fail("Nonconsecutive overload {} found".format(name), ctx) + self.fail(message_registry.NONCONSECUTIVE_OVERLOAD_FOUND.format(name), ctx) elif prev_is_overloaded: - self.fail("Definition of '{}' missing 'overload'".format(name), ctx) + self.fail(message_registry.DEFINITION_MISSING_OVERLOAD.format(name), ctx) else: self.name_already_defined(name, ctx, self.globals[name]) @@ -4824,8 +4813,7 @@ def name_not_defined(self, name: str, ctx: Context, namespace: Optional[str] = N # later on. Defer current target. self.record_incomplete_ref() return - message = 'Name "{}" is not defined'.format(name) - self.fail(message, ctx, code=codes.NAME_DEFINED) + self.fail(message_registry.NAME_NOT_DEFINED.format(name), ctx) if 'builtins.{}'.format(name) in SUGGESTED_TEST_FIXTURES: # The user probably has a missing definition in a test fixture. Let's verify. @@ -4876,8 +4864,8 @@ def already_defined(self, extra_msg = ' on line {}'.format(node.line) else: extra_msg = ' (possibly by an import)' - self.fail('{} "{}" already defined{}'.format(noun, unmangle(name), extra_msg), ctx, - code=codes.NO_REDEF) + self.fail(message_registry.NAME_ALREADY_DEFINED.format(noun, unmangle(name), extra_msg), + ctx) def name_already_defined(self, name: str, @@ -4898,11 +4886,10 @@ def is_local_name(self, name: str) -> bool: return self.is_defined_in_current_module(name) or '.' not in name def fail(self, - msg: str, + msg: ErrorMessage, ctx: Context, serious: bool = False, *, - code: Optional[ErrorCode] = None, blocker: bool = False) -> None: if (not serious and not self.options.check_untyped_defs and @@ -4911,10 +4898,7 @@ def fail(self, return # In case it's a bug and we don't really have context assert ctx is not None, msg - self.errors.report(ctx.get_line(), ctx.get_column(), msg, blocker=blocker, code=code) - - def fail_blocker(self, msg: str, ctx: Context) -> None: - self.fail(msg, ctx, blocker=True) + self.errors.report(ctx.get_line(), ctx.get_column(), msg.value, code=msg.code, blocker=blocker) def note(self, msg: str, ctx: Context, code: Optional[ErrorCode] = None) -> None: if (not self.options.check_untyped_defs and diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index 07e8e048decd..53a4c9b2802c 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -12,6 +12,8 @@ ) from mypy.semanal_shared import SemanticAnalyzerInterface from mypy.options import Options +from mypy.message_registry import ErrorMessage +from mypy import message_registry class EnumCallAnalyzer: @@ -33,7 +35,7 @@ def process_enum_call(self, s: AssignmentStmt, is_func_scope: bool) -> bool: if enum_call is None: return False if isinstance(lvalue, MemberExpr): - self.fail("Enum type as attribute is not supported", lvalue) + self.fail(message_registry.ENUM_ATTRIBUTE_UNSUPPORTED, lvalue) return False # Yes, it's a valid Enum definition. Add it to the symbol table. self.api.add_symbol(name, enum_call, s) @@ -106,15 +108,15 @@ def parse_enum_call_args(self, call: CallExpr, """ args = call.args if not all([arg_kind in [ARG_POS, ARG_NAMED] for arg_kind in call.arg_kinds]): - return self.fail_enum_call_arg("Unexpected arguments to %s()" % class_name, call) + return self.fail_enum_call_arg(message_registry.ENUM_CALL_UNEXPECTED_ARGS.format(class_name), call) if len(args) < 2: - return self.fail_enum_call_arg("Too few arguments for %s()" % class_name, call) + return self.fail_enum_call_arg(message_registry.ENUM_CALL_TOO_FEW_ARGS.format(class_name), call) if len(args) > 6: - return self.fail_enum_call_arg("Too many arguments for %s()" % class_name, call) + return self.fail_enum_call_arg(message_registry.ENUM_CALL_TOO_MANY_ARGS.format(class_name), call) valid_name = [None, 'value', 'names', 'module', 'qualname', 'type', 'start'] for arg_name in call.arg_names: if arg_name not in valid_name: - self.fail_enum_call_arg('Unexpected keyword argument "{}"'.format(arg_name), call) + self.fail_enum_call_arg(message_registry.ENUM_CALL_UNEXPECTED_KWARG.format(arg_name), call) value, names = None, None for arg_name, arg in zip(call.arg_names, args): if arg_name == 'value': @@ -127,7 +129,7 @@ def parse_enum_call_args(self, call: CallExpr, names = args[1] if not isinstance(value, (StrExpr, UnicodeExpr)): return self.fail_enum_call_arg( - "%s() expects a string literal as the first argument" % class_name, call) + message_registry.ENUM_CALL_EXPECTED_STRING_LITERAL.format(class_name), call) items = [] values: List[Optional[Expression]] = [] if isinstance(names, (StrExpr, UnicodeExpr)): @@ -151,30 +153,28 @@ def parse_enum_call_args(self, call: CallExpr, values.append(value) else: return self.fail_enum_call_arg( - "%s() with tuple or list expects strings or (name, value) pairs" % - class_name, - call) + message_registry.ENUM_CALL_EXPECTED_STRINGS_OR_PAIRS.format(class_name), call) elif isinstance(names, DictExpr): for key, value in names.items: if not isinstance(key, (StrExpr, UnicodeExpr)): return self.fail_enum_call_arg( - "%s() with dict literal requires string literals" % class_name, call) + message_registry.ENUM_CALL_DICT_EXPECTED_STRING_KEYS.format(class_name), + call) items.append(key.value) values.append(value) else: # TODO: Allow dict(x=1, y=2) as a substitute for {'x': 1, 'y': 2}? return self.fail_enum_call_arg( - "%s() expects a string, tuple, list or dict literal as the second argument" % - class_name, + message_registry.ENUM_CALL_EXPECTED_LITERAL.format(class_name), call) if len(items) == 0: - return self.fail_enum_call_arg("%s() needs at least one item" % class_name, call) + return self.fail_enum_call_arg(message_registry.ENUM_CALL_ATLEAST_ONE_ITEM.format(class_name), call) if not values: values = [None] * len(items) assert len(items) == len(values) return items, values, True - def fail_enum_call_arg(self, message: str, + def fail_enum_call_arg(self, message: ErrorMessage, context: Context) -> Tuple[List[str], List[Optional[Expression]], bool]: self.fail(message, context) @@ -182,5 +182,5 @@ def fail_enum_call_arg(self, message: str, # Helpers - def fail(self, msg: str, ctx: Context) -> None: + def fail(self, msg: ErrorMessage, ctx: Context) -> None: self.api.fail(msg, ctx) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 968a7d502ebe..b6b49a8e2e20 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -23,6 +23,8 @@ from mypy.options import Options from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.util import get_unique_redefinition_name +from mypy.message_registry import ErrorMessage +from mypy import message_registry # Matches "_prohibited" in typing.py, but adds __annotations__, which works at runtime but can't # easily be supported in a static checker. @@ -41,10 +43,6 @@ "__annotations__", ) -NAMEDTUP_CLASS_ERROR: Final = ( - "Invalid statement in NamedTuple definition; " 'expected "field_name: field_type [= default]"' -) - SELF_TVAR_NAME: Final = "_NT" @@ -94,10 +92,10 @@ def check_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool or None, if any of the types are not ready. """ if self.options.python_version < (3, 6) and not is_stub_file: - self.fail('NamedTuple class syntax is only supported in Python 3.6', defn) + self.fail(message_registry.NAMEDTUPLE_SUPPORTED_ABOVE_PY36, defn) return [], [], {} if len(defn.base_type_exprs) > 1: - self.fail('NamedTuple should be a single base', defn) + self.fail(message_registry.NAMEDTUPLE_SINGLE_BASE, defn) items: List[str] = [] types: List[Type] = [] default_items: Dict[str, Expression] = {} @@ -115,10 +113,10 @@ def check_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool if (isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, StrExpr)): continue - self.fail(NAMEDTUP_CLASS_ERROR, stmt) + self.fail(message_registry.NAMEDTUPLE_CLASS_ERROR, stmt) elif len(stmt.lvalues) > 1 or not isinstance(stmt.lvalues[0], NameExpr): # An assignment, but an invalid one. - self.fail(NAMEDTUP_CLASS_ERROR, stmt) + self.fail(message_registry.NAMEDTUPLE_CLASS_ERROR, stmt) else: # Append name and type in this case... name = stmt.lvalues[0].name @@ -133,15 +131,13 @@ def check_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool types.append(analyzed) # ...despite possible minor failures that allow further analyzis. if name.startswith('_'): - self.fail('NamedTuple field name cannot start with an underscore: {}' - .format(name), stmt) + self.fail(message_registry.NAMEDTUPLE_FIELD_NO_UNDERSCORE.format(name), stmt) if stmt.type is None or hasattr(stmt, 'new_syntax') and not stmt.new_syntax: - self.fail(NAMEDTUP_CLASS_ERROR, stmt) + self.fail(message_registry.NAMEDTUPLE_CLASS_ERROR, stmt) elif isinstance(stmt.rvalue, TempNode): # x: int assigns rvalue to TempNode(AnyType()) if default_items: - self.fail('Non-default NamedTuple fields cannot follow default fields', - stmt) + self.fail(message_registry.NAMEDTUPLE_FIELD_DEFAULT_AFTER_NONDEFAULT, stmt) else: default_items[name] = stmt.rvalue return items, types, default_items @@ -265,13 +261,13 @@ def parse_namedtuple_args(self, call: CallExpr, fullname: str # TODO: Share code with check_argument_count in checkexpr.py? args = call.args if len(args) < 2: - self.fail("Too few arguments for namedtuple()", call) + self.fail(message_registry.NAMEDTUPLE_TOO_FEW_ARGS, call) return None defaults: List[Expression] = [] if len(args) > 2: # Typed namedtuple doesn't support additional arguments. if fullname == 'typing.NamedTuple': - self.fail("Too many arguments for NamedTuple()", call) + self.fail(message_registry.NAMEDTUPLE_TOO_MANY_ARGS, call) return None for i, arg_name in enumerate(call.arg_names[2:], 2): if arg_name == 'defaults': @@ -281,18 +277,13 @@ def parse_namedtuple_args(self, call: CallExpr, fullname: str if isinstance(arg, (ListExpr, TupleExpr)): defaults = list(arg.items) else: - self.fail( - "List or tuple literal expected as the defaults argument to " - "namedtuple()", - arg - ) + self.fail(message_registry.NAMEDTUPLE_EXPECTED_LIST_TUPLE_DEFAULTS, arg) break if call.arg_kinds[:2] != [ARG_POS, ARG_POS]: - self.fail("Unexpected arguments to namedtuple()", call) + self.fail(message_registry.NAMEDTUPLE_UNEXPECTED_ARGS, call) return None if not isinstance(args[0], (StrExpr, BytesExpr, UnicodeExpr)): - self.fail( - "namedtuple() expects a string literal as the first argument", call) + self.fail(message_registry.NAMEDTUPLE_ARG_EXPECTED_STRING_LITERAL, call) return None typename = cast(Union[StrExpr, BytesExpr, UnicodeExpr], call.args[0]).value types: List[Type] = [] @@ -302,8 +293,7 @@ def parse_namedtuple_args(self, call: CallExpr, fullname: str str_expr = args[1] items = str_expr.value.replace(',', ' ').split() else: - self.fail( - "List or tuple literal expected as the second argument to namedtuple()", call) + self.fail(message_registry.NAMEDTUPLE_ARG_EXPECTED_LIST_TUPLE, call) return None else: listexpr = args[1] @@ -311,7 +301,7 @@ def parse_namedtuple_args(self, call: CallExpr, fullname: str # The fields argument contains just names, with implicit Any types. if any(not isinstance(item, (StrExpr, BytesExpr, UnicodeExpr)) for item in listexpr.items): - self.fail("String literal expected as namedtuple() item", call) + self.fail(message_registry.NAMEDTUPLE_EXPECTED_STRING_LITERAL, call) return None items = [cast(Union[StrExpr, BytesExpr, UnicodeExpr], item).value for item in listexpr.items] @@ -328,10 +318,11 @@ def parse_namedtuple_args(self, call: CallExpr, fullname: str types = [AnyType(TypeOfAny.unannotated) for _ in items] underscore = [item for item in items if item.startswith('_')] if underscore: - self.fail("namedtuple() field names cannot start with an underscore: " - + ', '.join(underscore), call) + self.fail( + message_registry.NAMEDTUPLE_FIELDS_NO_UNDERSCORE.format(', '.join(underscore)), + call) if len(defaults) > len(items): - self.fail("Too many defaults given in call to namedtuple()", call) + self.fail(message_registry.NAMEDTUPLE_TOO_MANY_DEFAULTS, call) defaults = defaults[:len(items)] return items, types, defaults, typename, True @@ -347,18 +338,18 @@ def parse_namedtuple_fields_with_types(self, nodes: List[Expression], context: C for item in nodes: if isinstance(item, TupleExpr): if len(item.items) != 2: - self.fail("Invalid NamedTuple field definition", item) + self.fail(message_registry.NAMEDTUPLE_INVALID_FIELD_DEFINITION, item) return None name, type_node = item.items if isinstance(name, (StrExpr, BytesExpr, UnicodeExpr)): items.append(name.value) else: - self.fail("Invalid NamedTuple() field name", item) + self.fail(message_registry.NAMEDTUPLE_INVALID_FIELD_NAME, item) return None try: type = expr_to_unanalyzed_type(type_node, self.options, self.api.is_stub_file) except TypeTranslationError: - self.fail('Invalid field type', type_node) + self.fail(message_registry.NAMEDTUPLE_INVALID_FIELD_TYPE, type_node) return None analyzed = self.api.anal_type(type) # Workaround #4987 and avoid introducing a bogus UnboundType @@ -369,7 +360,7 @@ def parse_namedtuple_fields_with_types(self, nodes: List[Expression], context: C return [], [], [], False types.append(analyzed) else: - self.fail("Tuple expected as NamedTuple() field", item) + self.fail(message_registry.NAMEDTUPLE_TUPLE_EXPECTED, item) return None return items, types, [], True @@ -519,7 +510,7 @@ def save_namedtuple_body(self, named_tuple_info: TypeInfo) -> Iterator[None]: continue ctx = named_tuple_info.names[prohibited].node assert ctx is not None - self.fail('Cannot overwrite NamedTuple attribute "{}"'.format(prohibited), + self.fail(message_registry.NAMEDTUPLE_CANNOT_OVERWRITE_ATTRIBUTE.format(prohibited), ctx) # Restore the names in the original symbol table. This ensures that the symbol @@ -541,5 +532,5 @@ def save_namedtuple_body(self, named_tuple_info: TypeInfo) -> Iterator[None]: # Helpers - def fail(self, msg: str, ctx: Context) -> None: + def fail(self, msg: ErrorMessage, ctx: Context) -> None: self.api.fail(msg, ctx) diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index 4d5077dbfe43..6ca863f8b74a 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -19,7 +19,8 @@ from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.typeanal import check_for_explicit_any, has_any_from_unimported_type from mypy.messages import MessageBuilder, format_type -from mypy.errorcodes import ErrorCode +from mypy import message_registry +from mypy.message_registry import ErrorMessage from mypy import errorcodes as codes @@ -76,12 +77,12 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> bool: newtype_class_info.tuple_type = old_type elif isinstance(old_type, Instance): if old_type.type.is_protocol: - self.fail("NewType cannot be used with protocol classes", s) + self.fail(message_registry.NEWTYPE_USED_WITH_PROTOCOL, s) newtype_class_info = self.build_newtype_typeinfo(name, old_type, old_type, s.line) else: if old_type is not None: - message = "Argument 2 to NewType(...) must be subclassable (got {})" - self.fail(message.format(format_type(old_type)), s, code=codes.VALID_NEWTYPE) + message = message_registry.NEWTYPE_ARG_MUST_BE_SUBCLASSABLE + self.fail(message.format(format_type(old_type)), s) # Otherwise the error was already reported. old_type = AnyType(TypeOfAny.from_error) object_type = self.api.named_type('__builtins__.object') @@ -120,14 +121,14 @@ def analyze_newtype_declaration(self, name = s.lvalues[0].name if s.type: - self.fail("Cannot declare the type of a NewType declaration", s) + self.fail(message_registry.CANNOT_DECLARE_TYPE_OF_NEWTYPE, s) names = self.api.current_symbol_table() existing = names.get(name) # Give a better error message than generic "Name already defined". if (existing and not isinstance(existing.node, PlaceholderNode) and not s.rvalue.analyzed): - self.fail('Cannot redefine "%s" as a NewType' % name, s) + self.fail(message_registry.CANNOT_REDEFINE_AS_NEWTYPE.format(name), s) # This dummy NewTypeExpr marks the call as sufficiently analyzed; it will be # overwritten later with a fully complete NewTypeExpr if there are no other @@ -145,20 +146,20 @@ def check_newtype_args(self, name: str, call: CallExpr, has_failed = False args, arg_kinds = call.args, call.arg_kinds if len(args) != 2 or arg_kinds[0] != ARG_POS or arg_kinds[1] != ARG_POS: - self.fail("NewType(...) expects exactly two positional arguments", context) + self.fail(message_registry.NEWTYPE_EXPECTS_TWO_ARGS, context) return None, False # Check first argument if not isinstance(args[0], (StrExpr, BytesExpr, UnicodeExpr)): - self.fail("Argument 1 to NewType(...) must be a string literal", context) + self.fail(message_registry.NEWTYPE_ARG_STRING_LITERAL, context) has_failed = True elif args[0].value != name: - msg = 'String argument 1 "{}" to NewType(...) does not match variable name "{}"' + msg = message_registry.NEWTYPE_ARG_VARNAME_MISMATCH self.fail(msg.format(args[0].value, name), context) has_failed = True # Check second argument - msg = "Argument 2 to NewType(...) must be a valid type" + msg = message_registry.NEWTYPE_ARG_INVALID_TYPE try: unanalyzed_type = expr_to_unanalyzed_type(args[1], self.options, self.api.is_stub_file) except TypeTranslationError: @@ -208,5 +209,5 @@ def build_newtype_typeinfo(self, name: str, old_type: Type, base_type: Instance, def make_argument(self, name: str, type: Type) -> Argument: return Argument(Var(name), type, None, ARG_POS) - def fail(self, msg: str, ctx: Context, *, code: Optional[ErrorCode] = None) -> None: - self.api.fail(msg, ctx, code=code) + def fail(self, msg: ErrorMessage, ctx: Context) -> None: + self.api.fail(msg, ctx) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 6b0fe1251693..39b7560508ea 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -1,6 +1,7 @@ """Shared definitions used by different parts of semantic analysis.""" from abc import abstractmethod +from mypy.message_registry import ErrorMessage from typing import Optional, List, Callable from typing_extensions import Final @@ -46,8 +47,8 @@ def lookup_fully_qualified_or_none(self, name: str) -> Optional[SymbolTableNode] raise NotImplementedError @abstractmethod - def fail(self, msg: str, ctx: Context, serious: bool = False, *, - blocker: bool = False, code: Optional[ErrorCode] = None) -> None: + def fail(self, msg: ErrorMessage, ctx: Context, serious: bool = False, *, + blocker: bool = False) -> None: raise NotImplementedError @abstractmethod diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 73eaa18ef490..ccd23e418e76 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -18,6 +18,7 @@ from mypy.scope import Scope from mypy.options import Options from mypy.errorcodes import ErrorCode +from mypy.message_registry import ErrorMessage from mypy import message_registry, errorcodes as codes @@ -72,17 +73,16 @@ def visit_instance(self, t: Instance) -> None: if isinstance(arg, TypeVarType): arg_values = arg.values if not arg_values: - self.fail('Type variable "{}" not valid as type ' - 'argument value for "{}"'.format( - arg.name, info.name), t, code=codes.TYPE_VAR) + self.fail( + message_registry.TYPEVAR_INVALID_TYPE_ARG.format(arg.name, info.name), + t) continue else: arg_values = [arg] self.check_type_var_values(info, arg_values, tvar.name, tvar.values, i + 1, t) if not is_subtype(arg, tvar.upper_bound): - self.fail('Type argument "{}" of "{}" must be ' - 'a subtype of "{}"'.format( - arg, info.name, tvar.upper_bound), t, code=codes.TYPE_VAR) + self.fail(message_registry.TYPE_ARG_INVALID_SUBTYPE.format( + arg, info.name, tvar.upper_bound), t) super().visit_instance(t) def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: str, @@ -92,16 +92,14 @@ def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: s not any(is_same_type(actual, value) for value in valids)): if len(actuals) > 1 or not isinstance(actual, Instance): - self.fail('Invalid type argument value for "{}"'.format( - type.name), context, code=codes.TYPE_VAR) + self.fail(message_registry.TYPE_ARG_INVALID_VALUE.format(type.name), context) else: class_name = '"{}"'.format(type.name) actual_type_name = '"{}"'.format(actual.type.name) self.fail( message_registry.INCOMPATIBLE_TYPEVAR_VALUE.format( arg_name, class_name, actual_type_name), - context, - code=codes.TYPE_VAR) + context) - def fail(self, msg: str, context: Context, *, code: Optional[ErrorCode] = None) -> None: - self.errors.report(context.get_line(), context.get_column(), msg, code=code) + def fail(self, msg: ErrorMessage, context: Context) -> None: + self.errors.report(context.get_line(), context.get_column(), msg.value, code=msg.code) diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index f70bbe427124..137f023ff248 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -15,12 +15,10 @@ from mypy.options import Options from mypy.typeanal import check_for_explicit_any, has_any_from_unimported_type from mypy.messages import MessageBuilder -from mypy.errorcodes import ErrorCode -from mypy import errorcodes as codes +from mypy.message_registry import ErrorMessage +from mypy import message_registry, errorcodes as codes + -TPDICT_CLASS_ERROR: Final = ( - "Invalid statement in TypedDict definition; " 'expected "field_name: field_type"' -) class TypedDictAnalyzer: @@ -71,7 +69,7 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> Tuple[bool, Optional[Typ if any(not isinstance(expr, RefExpr) or expr.fullname not in TPDICT_NAMES and not self.is_typeddict(expr) for expr in defn.base_type_exprs): - self.fail("All bases of a new TypedDict must be TypedDict types", defn) + self.fail(message_registry.TYPEDDICT_BASES_MUST_BE_TYPEDDICTS, defn) typeddict_bases = list(filter(self.is_typeddict, defn.base_type_exprs)) keys: List[str] = [] types = [] @@ -87,7 +85,7 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> Tuple[bool, Optional[Typ valid_items = base_items.copy() for key in base_items: if key in keys: - self.fail('Overwriting TypedDict field "{}" while merging' + self.fail(message_registry.TYPEDDICT_OVERWRITE_FIELD_IN_MERGE .format(key), defn) keys.extend(valid_items.keys()) types.extend(valid_items.values()) @@ -130,17 +128,17 @@ def analyze_typeddict_classdef_fields( if (not isinstance(stmt, PassStmt) and not (isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, (EllipsisExpr, StrExpr)))): - self.fail(TPDICT_CLASS_ERROR, stmt) + self.fail(message_registry.TYPEDDICT_CLASS_ERROR, stmt) elif len(stmt.lvalues) > 1 or not isinstance(stmt.lvalues[0], NameExpr): # An assignment, but an invalid one. - self.fail(TPDICT_CLASS_ERROR, stmt) + self.fail(message_registry.TYPEDDICT_CLASS_ERROR, stmt) else: name = stmt.lvalues[0].name if name in (oldfields or []): - self.fail('Overwriting TypedDict field "{}" while extending' + self.fail(message_registry.TYPEDDICT_OVERWRITE_FIELD_IN_EXTEND .format(name), stmt) if name in fields: - self.fail('Duplicate TypedDict key "{}"'.format(name), stmt) + self.fail(message_registry.TYPEDDICT_DUPLICATE_KEY.format(name), stmt) continue # Append name and type in this case... fields.append(name) @@ -153,15 +151,15 @@ def analyze_typeddict_classdef_fields( types.append(analyzed) # ...despite possible minor failures that allow further analyzis. if stmt.type is None or hasattr(stmt, 'new_syntax') and not stmt.new_syntax: - self.fail(TPDICT_CLASS_ERROR, stmt) + self.fail(message_registry.TYPEDDICT_CLASS_ERROR, stmt) elif not isinstance(stmt.rvalue, TempNode): # x: int assigns rvalue to TempNode(AnyType()) - self.fail('Right hand side values are not supported in TypedDict', stmt) + self.fail(message_registry.TYPEDDICT_RHS_VALUE_UNSUPPORTED, stmt) total: Optional[bool] = True if 'total' in defn.keywords: total = self.api.parse_bool(defn.keywords['total']) if total is None: - self.fail('Value of "total" must be True or False', defn) + self.fail(message_registry.TYPEDDICT_TOTAL_MUST_BE_BOOL_2, defn) total = True required_keys = set(fields) if total else set() return fields, types, required_keys @@ -202,8 +200,8 @@ def check_typeddict(self, else: if var_name is not None and name != var_name: self.fail( - 'First argument "{}" to TypedDict() does not match variable name "{}"'.format( - name, var_name), node, code=codes.NAME_MATCH) + message_registry.TYPEDDICT_ARG_NAME_MISMATCH.format( + name, var_name), node) if name != var_name or is_func_scope: # Give it a unique name derived from the line number. name += '@' + str(call.line) @@ -229,27 +227,27 @@ def parse_typeddict_args( # TODO: Share code with check_argument_count in checkexpr.py? args = call.args if len(args) < 2: - return self.fail_typeddict_arg("Too few arguments for TypedDict()", call) + return self.fail_typeddict_arg(message_registry.TYPEDDICT_TOO_FEW_ARGS, call) if len(args) > 3: - return self.fail_typeddict_arg("Too many arguments for TypedDict()", call) + return self.fail_typeddict_arg(message_registry.TYPEDDICT_TOO_MANY_ARGS, call) # TODO: Support keyword arguments if call.arg_kinds not in ([ARG_POS, ARG_POS], [ARG_POS, ARG_POS, ARG_NAMED]): - return self.fail_typeddict_arg("Unexpected arguments to TypedDict()", call) + return self.fail_typeddict_arg(message_registry.TYPEDDICT_UNEXPECTED_ARGS, call) if len(args) == 3 and call.arg_names[2] != 'total': return self.fail_typeddict_arg( - 'Unexpected keyword argument "{}" for "TypedDict"'.format(call.arg_names[2]), call) + message_registry.TYPEDDICT_CALL_UNEXPECTED_KWARG.format(call.arg_names[2]), call) if not isinstance(args[0], (StrExpr, BytesExpr, UnicodeExpr)): return self.fail_typeddict_arg( - "TypedDict() expects a string literal as the first argument", call) + message_registry.TYPEDDICT_CALL_EXPECTED_STRING_LITERAL, call) if not isinstance(args[1], DictExpr): return self.fail_typeddict_arg( - "TypedDict() expects a dictionary literal as the second argument", call) + message_registry.TYPEDDICT_CALL_EXPECTED_DICT, call) total: Optional[bool] = True if len(args) == 3: total = self.api.parse_bool(call.args[2]) if total is None: return self.fail_typeddict_arg( - 'TypedDict() "total" argument must be True or False', call) + message_registry.TYPEDDICT_TOTAL_MUST_BE_BOOL, call) dictexpr = args[1] res = self.parse_typeddict_fields_with_types(dictexpr.items, call) if res is None: @@ -283,17 +281,17 @@ def parse_typeddict_fields_with_types( key = field_name_expr.value items.append(key) if key in seen_keys: - self.fail('Duplicate TypedDict key "{}"'.format(key), field_name_expr) + self.fail(message_registry.TYPEDDICT_DUPLICATE_KEY.format(key), field_name_expr) seen_keys.add(key) else: name_context = field_name_expr or field_type_expr - self.fail_typeddict_arg("Invalid TypedDict() field name", name_context) + self.fail_typeddict_arg(message_registry.TYPEDDICT_INVALID_FIELD_NAME, name_context) return [], [], False try: type = expr_to_unanalyzed_type(field_type_expr, self.options, self.api.is_stub_file) except TypeTranslationError: - self.fail_typeddict_arg('Invalid field type', field_type_expr) + self.fail_typeddict_arg(message_registry.TYPEDDICT_INVALID_FIELD_TYPE, field_type_expr) return [], [], False analyzed = self.api.anal_type(type) if analyzed is None: @@ -301,7 +299,7 @@ def parse_typeddict_fields_with_types( types.append(analyzed) return items, types, True - def fail_typeddict_arg(self, message: str, + def fail_typeddict_arg(self, message: ErrorMessage, context: Context) -> Tuple[str, List[str], List[Type], bool, bool]: self.fail(message, context) return '', [], [], True, False @@ -326,5 +324,5 @@ def is_typeddict(self, expr: Expression) -> bool: return (isinstance(expr, RefExpr) and isinstance(expr.node, TypeInfo) and expr.node.typeddict_type is not None) - def fail(self, msg: str, ctx: Context, *, code: Optional[ErrorCode] = None) -> None: - self.api.fail(msg, ctx, code=code) + def fail(self, msg: ErrorMessage, ctx: Context) -> None: + self.api.fail(msg, ctx) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 07dc704e42ea..69f65ced4140 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -31,6 +31,7 @@ from mypy.plugin import Plugin, TypeAnalyzerPluginInterface, AnalyzeTypeContext from mypy.semanal_shared import SemanticAnalyzerCoreInterface from mypy.errorcodes import ErrorCode +from mypy.message_registry import ErrorMessage from mypy import nodes, message_registry, errorcodes as codes T = TypeVar('T') @@ -82,7 +83,7 @@ def analyze_type_alias(node: Expression, try: type = expr_to_unanalyzed_type(node, options, allow_new_syntax) except TypeTranslationError: - api.fail('Invalid type alias: expression is not a valid type', node) + api.fail(message_registry.INVALID_TYPE_ALIAS, node) return None analyzer = TypeAnalyser(api, tvar_scope, plugin, options, is_typeshed_stub, allow_new_syntax=allow_new_syntax, defining_alias=True, @@ -93,14 +94,16 @@ def analyze_type_alias(node: Expression, return res, analyzer.aliases_used -def no_subscript_builtin_alias(name: str, propose_alt: bool = True) -> str: - msg = '"{}" is not subscriptable'.format(name.split('.')[-1]) +def no_subscript_builtin_alias(name: str, propose_alt: bool = True) -> ErrorMessage: # This should never be called if the python_version is 3.9 or newer + variable_name = name.split('.')[-1] nongen_builtins = get_nongen_builtins((3, 8)) replacement = nongen_builtins[name] if replacement and propose_alt: - msg += ', use "{}" instead'.format(replacement) - return msg + return message_registry.NOT_SUBSCRIPTABLE_REPLACEMENT.format(variable_name, replacement) + + return message_registry.NOT_SUBSCRIPTABLE.format(variable_name) + class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface): @@ -191,7 +194,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) self.api.record_incomplete_ref() return AnyType(TypeOfAny.special_form) if node is None: - self.fail('Internal error (node is None, kind={})'.format(sym.kind), t) + self.fail(message_registry.TYPEANAL_INTERNAL_ERROR.format(sym.kind), t) return AnyType(TypeOfAny.special_form) fullname = node.fullname hook = self.plugin.get_type_analyze_hook(fullname) @@ -206,9 +209,9 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) tvar_def = self.tvar_scope.get_binding(sym) if isinstance(sym.node, ParamSpecExpr): if tvar_def is None: - self.fail('ParamSpec "{}" is unbound'.format(t.name), t) + self.fail(message_registry.PARAMSPEC_UNBOUND.format(t.name), t) return AnyType(TypeOfAny.from_error) - self.fail('Invalid location for ParamSpec "{}"'.format(t.name), t) + self.fail(message_registry.PARAMSPEC_INVALID_LOCATION.format(t.name), t) self.note( 'You can use ParamSpec as the first argument to Callable, e.g., ' "'Callable[{}, int]'".format(t.name), @@ -216,13 +219,12 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) ) return AnyType(TypeOfAny.from_error) if isinstance(sym.node, TypeVarExpr) and tvar_def is not None and self.defining_alias: - self.fail('Can\'t use bound type variable "{}"' - ' to define generic alias'.format(t.name), t) + self.fail(message_registry.NO_BOUND_TYPEVAR_GENERIC_ALIAS.format(t.name), t) return AnyType(TypeOfAny.from_error) if isinstance(sym.node, TypeVarExpr) and tvar_def is not None: assert isinstance(tvar_def, TypeVarType) if len(t.args) > 0: - self.fail('Type variable "{}" used with arguments'.format(t.name), t) + self.fail(message_registry.TYPEVAR_USED_WITH_ARGS.format(t.name), t) # Change the line number return TypeVarType( tvar_def.name, tvar_def.fullname, tvar_def.id, tvar_def.values, @@ -266,9 +268,7 @@ def cannot_resolve_type(self, t: UnboundType) -> None: # TODO: Move error message generation to messages.py. We'd first # need access to MessageBuilder here. Also move the similar # message generation logic in semanal.py. - self.api.fail( - 'Cannot resolve name "{}" (possible cyclic definition)'.format(t.name), - t) + self.api.fail(message_registry.CANNOT_RESOLVE_NAME.format('name', t.name), t) def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Optional[Type]: """Bind special type that is recognized through magic name such as 'typing.Any'. @@ -280,8 +280,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Opt elif fullname == 'typing.Any' or fullname == 'builtins.Any': return AnyType(TypeOfAny.explicit) elif fullname in ('typing.Final', 'typing_extensions.Final'): - self.fail("Final can be only used as an outermost qualifier" - " in a variable annotation", t) + self.fail(message_registry.ONLY_OUTERMOST_FINAL, t) return AnyType(TypeOfAny.from_error) elif (fullname == 'typing.Tuple' or (fullname == 'builtins.tuple' and (self.options.python_version >= (3, 9) or @@ -294,7 +293,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Opt if self.api.is_incomplete_namespace('builtins'): self.api.record_incomplete_ref() else: - self.fail('Name "tuple" is not defined', t) + self.fail(message_registry.BUILTIN_TUPLE_NOT_DEFINED, t) return AnyType(TypeOfAny.special_form) if len(t.args) == 0 and not t.empty_tuple_index: # Bare 'Tuple' is same as 'tuple' @@ -312,7 +311,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Opt return UnionType.make_union(items) elif fullname == 'typing.Optional': if len(t.args) != 1: - self.fail('Optional[...] must have exactly one type argument', t) + self.fail(message_registry.SINGLE_TYPE_ARG.format('Optional[...]'), t) return AnyType(TypeOfAny.from_error) item = self.anal_type(t.args[0]) return make_optional_type(item) @@ -331,16 +330,16 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Opt return None type_str = 'Type[...]' if fullname == 'typing.Type' else 'type[...]' if len(t.args) != 1: - self.fail(type_str + ' must have exactly one type argument', t) + self.fail(message_registry.SINGLE_TYPE_ARG.format(type_str), t) item = self.anal_type(t.args[0]) return TypeType.make_normalized(item, line=t.line) elif fullname == 'typing.ClassVar': if self.nesting_level > 0: - self.fail('Invalid type: ClassVar nested inside other type', t) + self.fail(message_registry.INVALID_NESTED_CLASSVAR, t) if len(t.args) == 0: return AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column) if len(t.args) != 1: - self.fail('ClassVar[...] must have at most one type argument', t) + self.fail(message_registry.CLASSVAR_ATMOST_ONE_TYPE_ARG, t) return AnyType(TypeOfAny.from_error) return self.anal_type(t.args[0]) elif fullname in ('mypy_extensions.NoReturn', 'typing.NoReturn'): @@ -349,8 +348,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Opt return self.analyze_literal_type(t) elif fullname in ('typing_extensions.Annotated', 'typing.Annotated'): if len(t.args) < 2: - self.fail("Annotated[...] must have exactly one type argument" - " and at least one annotation", t) + self.fail(message_registry.ANNOTATED_SINGLE_TYPE_ARG, t) return AnyType(TypeOfAny.from_error) return self.anal_type(t.args[0]) elif self.anal_type_guard_arg(t, fullname) is not None: @@ -391,7 +389,7 @@ def analyze_type_with_type_info( # The class has a Tuple[...] base class so it will be # represented as a tuple type. if args: - self.fail('Generic tuple types not supported', ctx) + self.fail(message_registry.GENERIC_TUPLE_UNSUPPORTED, ctx) return AnyType(TypeOfAny.from_error) return tup.copy_modified(items=self.anal_array(tup.items), fallback=instance) @@ -400,7 +398,7 @@ def analyze_type_with_type_info( # The class has a TypedDict[...] base class so it will be # represented as a typeddict type. if args: - self.fail('Generic TypedDict types not supported', ctx) + self.fail(message_registry.GENERIC_TYPED_DICT_UNSUPPORTED, ctx) return AnyType(TypeOfAny.from_error) # Create a named TypedDictType return td.copy_modified(item_types=self.anal_array(list(td.items.values())), @@ -448,9 +446,8 @@ def analyze_unbound_type_without_type_info(self, t: UnboundType, sym: SymbolTabl value = sym.node.name base_enum_short_name = sym.node.info.name if not defining_literal: - msg = message_registry.INVALID_TYPE_RAW_ENUM_VALUE.format( - base_enum_short_name, value) - self.fail(msg, t) + self.fail(message_registry.INVALID_TYPE_RAW_ENUM_VALUE.format( + base_enum_short_name, value), t) return AnyType(TypeOfAny.from_error) return LiteralType( value=value, @@ -467,23 +464,23 @@ def analyze_unbound_type_without_type_info(self, t: UnboundType, sym: SymbolTabl if isinstance(sym.node, Var): notes.append('See https://mypy.readthedocs.io/en/' 'stable/common_issues.html#variables-vs-type-aliases') - message = 'Variable "{}" is not valid as a type' + message = message_registry.VARIABLE_NOT_VALID_TYPE elif isinstance(sym.node, (SYMBOL_FUNCBASE_TYPES, Decorator)): - message = 'Function "{}" is not valid as a type' + message = message_registry.FUNCTION_NOT_VALID_TYPE notes.append('Perhaps you need "Callable[...]" or a callback protocol?') elif isinstance(sym.node, MypyFile): # TODO: suggest a protocol when supported. - message = 'Module "{}" is not valid as a type' + message = message_registry.MODULE_NOT_VALID_TYPE elif unbound_tvar: - message = 'Type variable "{}" is unbound' + message = message_registry.UNBOUND_TYPEVAR short = name.split('.')[-1] notes.append(('(Hint: Use "Generic[{}]" or "Protocol[{}]" base class' ' to bind "{}" inside a class)').format(short, short, short)) notes.append('(Hint: Use "{}" in function signature to bind "{}"' ' inside a function)'.format(short, short)) else: - message = 'Cannot interpret reference "{}" as a type' - self.fail(message.format(name), t, code=codes.VALID_TYPE) + message = message_registry.CANNOT_INTERPRET_AS_TYPE + self.fail(message.format(name), t) for note in notes: self.note(note, t, code=codes.VALID_TYPE) @@ -510,12 +507,12 @@ def visit_deleted_type(self, t: DeletedType) -> Type: return t def visit_type_list(self, t: TypeList) -> Type: - self.fail('Bracketed expression "[...]" is not valid as a type', t) + self.fail(message_registry.BRACKETED_EXPR_INVALID_TYPE, t) self.note('Did you mean "List[...]"?', t) return AnyType(TypeOfAny.from_error) def visit_callable_argument(self, t: CallableArgument) -> Type: - self.fail('Invalid type', t) + self.fail(message_registry.INVALID_TYPE, t) return AnyType(TypeOfAny.from_error) def visit_instance(self, t: Instance) -> Type: @@ -561,7 +558,7 @@ def anal_type_guard(self, t: Type) -> Optional[Type]: def anal_type_guard_arg(self, t: UnboundType, fullname: str) -> Optional[Type]: if fullname in ('typing_extensions.TypeGuard', 'typing.TypeGuard'): if len(t.args) != 1: - self.fail("TypeGuard must have exactly one type argument", t) + self.fail(message_registry.SINGLE_TYPE_ARG.format('TypeGuard'), t) return AnyType(TypeOfAny.from_error) return self.anal_type(t.args[0]) return None @@ -578,7 +575,7 @@ def visit_tuple_type(self, t: TupleType) -> Type: # Types such as (t1, t2, ...) only allowed in assignment statements. They'll # generate errors elsewhere, and Tuple[t1, t2, ...] must be used instead. if t.implicit and not self.allow_tuple_literal: - self.fail('Syntax error in type annotation', t, code=codes.SYNTAX) + self.fail(message_registry.ANNOTATION_SYNTAX_ERROR, t) if len(t.items) == 0: self.note('Suggestion: Use Tuple[()] instead of () for an empty tuple, or ' 'None for a function without a return value', t, code=codes.SYNTAX) @@ -590,7 +587,7 @@ def visit_tuple_type(self, t: TupleType) -> Type: return AnyType(TypeOfAny.from_error) star_count = sum(1 for item in t.items if isinstance(item, StarType)) if star_count > 1: - self.fail('At most one star type allowed in a tuple', t) + self.fail(message_registry.TUPLE_SINGLE_STAR_TYPE, t) if t.implicit: return TupleType([AnyType(TypeOfAny.from_error) for _ in t.items], self.named_type('builtins.tuple'), @@ -625,19 +622,19 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> Type: if t.base_type_name in ('builtins.int', 'builtins.bool'): # The only time it makes sense to use an int or bool is inside of # a literal type. - msg = "Invalid type: try using Literal[{}] instead?".format(repr(t.literal_value)) + msg = message_registry.INVALID_TYPE_USE_LITERAL.format(repr(t.literal_value)) elif t.base_type_name in ('builtins.float', 'builtins.complex'): # We special-case warnings for floats and complex numbers. - msg = "Invalid type: {} literals cannot be used as a type".format(t.simple_name()) + msg = message_registry.INVALID_LITERAL_TYPE.format(t.simple_name()) else: # And in all other cases, we default to a generic error message. # Note: the reason why we use a generic error message for strings # but not ints or bools is because whenever we see an out-of-place # string, it's unclear if the user meant to construct a literal type # or just misspelled a regular type. So we avoid guessing. - msg = 'Invalid type comment or annotation' + msg = message_registry.INVALID_ANNOTATION - self.fail(msg, t, code=codes.VALID_TYPE) + self.fail(msg, t) if t.note is not None: self.note(t.note, t, code=codes.VALID_TYPE) @@ -655,14 +652,14 @@ def visit_union_type(self, t: UnionType) -> Type: and self.api.is_stub_file is False and self.options.python_version < (3, 10) and self.api.is_future_flag_set('annotations') is False): - self.fail("X | Y syntax for unions requires Python 3.10", t) + self.fail(message_registry.PIPE_UNION_REQUIRES_PY310, t) return UnionType(self.anal_array(t.items), t.line) def visit_partial_type(self, t: PartialType) -> Type: assert False, "Internal error: Unexpected partial type" def visit_ellipsis_type(self, t: EllipsisType) -> Type: - self.fail('Unexpected "..."', t) + self.fail(message_registry.UNEXPECTED_ELLIPSIS, t) return AnyType(TypeOfAny.from_error) def visit_type_type(self, t: TypeType) -> Type: @@ -749,11 +746,11 @@ def analyze_callable_type(self, t: UnboundType) -> Type: # Callable[?, RET] (where ? is something invalid) # TODO(shantanu): change error to mention paramspec, once we actually have some # support for it - self.fail('The first argument to Callable must be a list of types or "..."', t) + self.fail(message_registry.CALLABLE_INVALID_FIRST_ARG, t) return AnyType(TypeOfAny.from_error) ret = maybe_ret else: - self.fail('Please use "Callable[[], ]" or "Callable"', t) + self.fail(message_registry.CALLABLE_INVALID_ARGS, t) return AnyType(TypeOfAny.from_error) assert isinstance(ret, CallableType) return ret.accept(self) @@ -775,7 +772,7 @@ def analyze_callable_args(self, arglist: TypeList) -> Optional[Tuple[List[Type], # Looking it up already put an error message in return None elif found.fullname not in ARG_KINDS_BY_CONSTRUCTOR: - self.fail('Invalid argument constructor "{}"'.format( + self.fail(message_registry.INVALID_ARG_CONSTRUCTOR.format( found.fullname), arg) return None else: @@ -783,7 +780,7 @@ def analyze_callable_args(self, arglist: TypeList) -> Optional[Tuple[List[Type], kind = ARG_KINDS_BY_CONSTRUCTOR[found.fullname] kinds.append(kind) if arg.name is not None and kind.is_star(): - self.fail("{} arguments should not have names".format( + self.fail(message_registry.ARGS_SHOULD_NOT_HAVE_NAMES.format( arg.constructor), arg) return None else: @@ -797,7 +794,7 @@ def analyze_callable_args(self, arglist: TypeList) -> Optional[Tuple[List[Type], def analyze_literal_type(self, t: UnboundType) -> Type: if len(t.args) == 0: - self.fail('Literal[...] must have at least one parameter', t) + self.fail(message_registry.LITERAL_AT_LEAST_ONE_ARG, t) return AnyType(TypeOfAny.from_error) output: List[Type] = [] @@ -846,16 +843,16 @@ def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> Optional[L # TODO: Once we start adding support for enums, make sure we report a custom # error for case 2 as well. if arg.type_of_any not in (TypeOfAny.from_error, TypeOfAny.special_form): - self.fail('Parameter {} of Literal[...] cannot be of type "Any"'.format(idx), ctx) + self.fail(message_registry.LITERAL_INDEX_CANNOT_BE_ANY.format(idx), ctx) return None elif isinstance(arg, RawExpressionType): # A raw literal. Convert it directly into a literal if we can. if arg.literal_value is None: name = arg.simple_name() if name in ('float', 'complex'): - msg = 'Parameter {} of Literal[...] cannot be of type "{}"'.format(idx, name) + msg = message_registry.LITERAL_INDEX_INVALID_TYPE.format(idx, name) else: - msg = 'Invalid type: Literal[...] cannot contain arbitrary expressions' + msg = message_registry.LITERAL_INVALID_EXPRESSION self.fail(msg, ctx) # Note: we deliberately ignore arg.note here: the extra info might normally be # helpful, but it generally won't make sense in the context of a Literal[...]. @@ -880,14 +877,14 @@ def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> Optional[L out.extend(union_result) return out else: - self.fail('Parameter {} of Literal[...] is invalid'.format(idx), ctx) + self.fail(message_registry.LITERAL_INVALID_PARAMETER.format(idx), ctx) return None def analyze_type(self, t: Type) -> Type: return t.accept(self) - def fail(self, msg: str, ctx: Context, *, code: Optional[ErrorCode] = None) -> None: - self.fail_func(msg, ctx, code=code) + def fail(self, msg: ErrorMessage, ctx: Context) -> None: + self.fail_func(msg, ctx) def note(self, msg: str, ctx: Context, *, code: Optional[ErrorCode] = None) -> None: self.note_func(msg, ctx, code=code) @@ -942,7 +939,7 @@ def bind_function_type_variables( defs: List[TypeVarLikeType] = [] for name, tvar in typevars: if not self.tvar_scope.allow_binding(tvar.fullname): - self.fail('Type variable "{}" is bound by an outer class'.format(name), defn) + self.fail(message_registry.TYPEVAR_BOUND_BY_OUTER_CLASS.format(name), defn) self.tvar_scope.bind_new(name, tvar) binding = self.tvar_scope.get_binding(tvar.fullname) assert binding is not None @@ -1017,10 +1014,12 @@ def tuple_type(self, items: List[Type]) -> TupleType: TypeVarLikeList = List[Tuple[str, TypeVarLikeExpr]] # Mypyc doesn't support callback protocols yet. -MsgCallback = Callable[[str, Context, DefaultNamedArg(Optional[ErrorCode], 'code')], None] +MsgCallback = Callable[[ErrorMessage, Context], None] +NoteCallback = Callable[[str, Context, DefaultNamedArg(Optional[ErrorCode], 'code')], None] + -def get_omitted_any(disallow_any: bool, fail: MsgCallback, note: MsgCallback, +def get_omitted_any(disallow_any: bool, fail: MsgCallback, note: NoteCallback, orig_type: Type, python_version: Tuple[int, int], fullname: Optional[str] = None, unexpanded_type: Optional[Type] = None) -> AnyType: @@ -1030,16 +1029,12 @@ def get_omitted_any(disallow_any: bool, fail: MsgCallback, note: MsgCallback, typ = orig_type # We use a dedicated error message for builtin generics (as the most common case). alternative = nongen_builtins[fullname] - fail(message_registry.IMPLICIT_GENERIC_ANY_BUILTIN.format(alternative), typ, - code=codes.TYPE_ARG) + fail(message_registry.IMPLICIT_GENERIC_ANY_BUILTIN.format(alternative), typ) else: typ = unexpanded_type or orig_type type_str = typ.name if isinstance(typ, UnboundType) else format_type_bare(typ) - fail( - message_registry.BARE_GENERIC.format(quote_type_string(type_str)), - typ, - code=codes.TYPE_ARG) + fail(message_registry.BARE_GENERIC.format(quote_type_string(type_str)), typ) base_type = get_proper_type(orig_type) base_fullname = ( base_type.type.fullname if isinstance(base_type, Instance) else fullname @@ -1064,7 +1059,7 @@ def get_omitted_any(disallow_any: bool, fail: MsgCallback, note: MsgCallback, return any_type -def fix_instance(t: Instance, fail: MsgCallback, note: MsgCallback, +def fix_instance(t: Instance, fail: MsgCallback, note: NoteCallback, disallow_any: bool, python_version: Tuple[int, int], use_generic_error: bool = False, unexpanded_type: Optional[Type] = None,) -> None: @@ -1091,8 +1086,8 @@ def fix_instance(t: Instance, fail: MsgCallback, note: MsgCallback, act = str(len(t.args)) if act == '0': act = 'none' - fail('"{}" expects {}, but {} given'.format( - t.type.name, s, act), t, code=codes.TYPE_ARG) + fail(message_registry.TYPE_ARG_COUNT_MISMATCH.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. @@ -1136,8 +1131,7 @@ def expand_type_alias(node: TypeAlias, args: List[Type], tp.column = ctx.column return tp if act_len != exp_len: - fail('Bad number of arguments for type alias, expected: %s, given: %s' - % (exp_len, act_len), ctx) + fail(message_registry.TYPE_ALIAS_ARG_COUNT_MISMATCH.format(exp_len, act_len), ctx) return set_any_tvars(node, ctx.line, ctx.column, from_error=True) typ = TypeAliasType(node, args, ctx.line, ctx.column) assert typ.alias is not None @@ -1166,7 +1160,7 @@ def set_any_tvars(node: TypeAlias, type_str = otype.name if isinstance(otype, UnboundType) else format_type_bare(otype) fail(message_registry.BARE_GENERIC.format(quote_type_string(type_str)), - Context(newline, newcolumn), code=codes.TYPE_ARG) + Context(newline, newcolumn)) any_type = AnyType(type_of_any, line=newline, column=newcolumn) return TypeAliasType(node, [any_type] * len(node.alias_tvars), newline, newcolumn) @@ -1337,7 +1331,7 @@ def make_optional_type(t: Type) -> Type: return UnionType([t, NoneType()], t.line, t.column) -def fix_instance_types(t: Type, fail: MsgCallback, note: MsgCallback, +def fix_instance_types(t: Type, fail: MsgCallback, note: NoteCallback, python_version: Tuple[int, int]) -> None: """Recursively fix all instance types (type argument count) in a given type. @@ -1349,7 +1343,7 @@ def fix_instance_types(t: Type, fail: MsgCallback, note: MsgCallback, class InstanceFixer(TypeTraverserVisitor): def __init__( - self, fail: MsgCallback, note: MsgCallback, python_version: Tuple[int, int] + self, fail: MsgCallback, note: NoteCallback, python_version: Tuple[int, int] ) -> None: self.fail = fail self.note = note diff --git a/test-data/unit/plugins/type_anal_hook.py b/test-data/unit/plugins/type_anal_hook.py index 66b24bcf323d..ed4d9eafe9ef 100644 --- a/test-data/unit/plugins/type_anal_hook.py +++ b/test-data/unit/plugins/type_anal_hook.py @@ -5,6 +5,7 @@ # The official name changed to NoneType but we have an alias for plugin compat reasons # so we'll keep testing that here. from mypy.types import NoneTyp +from mypy import message_registry class TypeAnalyzePlugin(Plugin): def get_type_analyze_hook(self, fullname: str @@ -17,7 +18,7 @@ def get_type_analyze_hook(self, fullname: str def signal_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type: if (len(ctx.type.args) != 1 or not isinstance(ctx.type.args[0], TypeList)): - ctx.api.fail('Invalid "Signal" type (expected "Signal[[t, ...]]")', ctx.context) + ctx.api.fail(message_registry.INVALID_SIGNAL_TYPE, ctx.context) return AnyType(TypeOfAny.from_error) args = ctx.type.args[0]