diff --git a/mypy/build.py b/mypy/build.py index ca66eb77992d..4c4a8caf5874 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -57,6 +57,7 @@ from mypy.renaming import VariableRenameVisitor from mypy.config_parser import parse_mypy_comments from mypy.freetree import free_tree +from mypy import errorcodes as codes # Switch to True to produce debug output related to fine-grained incremental @@ -2422,14 +2423,16 @@ def module_not_found(manager: BuildManager, line: int, caller_state: State, or (manager.options.python_version[0] >= 3 and moduleinfo.is_py3_std_lib_module(target))): errors.report( - line, 0, "No library stub file for standard library module '{}'".format(target)) + line, 0, "No library stub file for standard library module '{}'".format(target), + code=codes.IMPORT) errors.report(line, 0, stub_msg, severity='note', only_once=True) elif moduleinfo.is_third_party_module(target): - errors.report(line, 0, "No library stub file for module '{}'".format(target)) + errors.report(line, 0, "No library stub file for module '{}'".format(target), + code=codes.IMPORT) errors.report(line, 0, stub_msg, severity='note', only_once=True) else: note = "See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports" - errors.report(line, 0, "Cannot find module named '{}'".format(target)) + errors.report(line, 0, "Cannot find module named '{}'".format(target), code=codes.IMPORT) errors.report(line, 0, note, severity='note', only_once=True) errors.set_import_context(save_import_context) diff --git a/mypy/checker.py b/mypy/checker.py index 349291bd5dde..954ddd80fb0b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -70,7 +70,7 @@ from mypy.sharedparse import BINARY_MAGIC_METHODS from mypy.scope import Scope from mypy.typeops import tuple_fallback -from mypy import state +from mypy import state, errorcodes as codes from mypy.traverser import has_return_statement from mypy.errorcodes import ErrorCode @@ -961,7 +961,8 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str]) # This is a NoReturn function self.msg.fail(message_registry.INVALID_IMPLICIT_RETURN, defn) else: - self.msg.fail(message_registry.MISSING_RETURN_STATEMENT, defn) + self.msg.fail(message_registry.MISSING_RETURN_STATEMENT, defn, + code=codes.RETURN) self.return_types.pop() @@ -980,7 +981,8 @@ def check_default_args(self, item: FuncItem, body_is_trivial: bool) -> None: else: msg += 'argument "{}"'.format(name) self.check_simple_assignment(arg.variable.type, arg.initializer, - context=arg, msg=msg, lvalue_name='argument', rvalue_name='default') + context=arg, msg=msg, lvalue_name='argument', rvalue_name='default', + code=codes.ASSIGNMENT) def is_forward_op_method(self, method_name: str) -> bool: if self.options.python_version[0] == 2 and method_name == '__div__': @@ -1009,24 +1011,30 @@ 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) + self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef, + code=codes.NO_UNTYPED_DEF) if not has_return_statement(fdef): self.note('Use "-> None" if function does not return a value', fdef) else: - self.fail(message_registry.FUNCTION_TYPE_EXPECTED, fdef) + self.fail(message_registry.FUNCTION_TYPE_EXPECTED, fdef, + code=codes.NO_UNTYPED_DEF) elif isinstance(fdef.type, CallableType): ret_type = fdef.type.ret_type if is_unannotated_any(ret_type): - self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef) + self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef, + code=codes.NO_UNTYPED_DEF) 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) + self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef, + code=codes.NO_UNTYPED_DEF) 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) + self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef, + code=codes.NO_UNTYPED_DEF) if any(is_unannotated_any(t) for t in fdef.type.arg_types): - self.fail(message_registry.ARGUMENT_TYPE_EXPECTED, fdef) + self.fail(message_registry.ARGUMENT_TYPE_EXPECTED, fdef, + code=codes.NO_UNTYPED_DEF) def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None: self_type = fill_typevars_with_any(fdef.info) @@ -1975,7 +1983,8 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type rvalue_type, lvalue_type, infer_lvalue_type = self.check_member_assignment( instance_type, lvalue_type, rvalue, lvalue) else: - rvalue_type = self.check_simple_assignment(lvalue_type, rvalue, lvalue) + rvalue_type = self.check_simple_assignment(lvalue_type, rvalue, lvalue, + code=codes.ASSIGNMENT) # Special case: only non-abstract non-protocol classes can be assigned to # variables with explicit type Type[A], where A is protocol or abstract. @@ -2111,7 +2120,8 @@ def check_compatibility_super(self, lvalue: RefExpr, lvalue_type: Optional[Type] return self.check_subtype(compare_type, base_type, lvalue, message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT, 'expression has type', - 'base class "%s" defined the type as' % base.name()) + 'base class "%s" defined the type as' % base.name(), + code=codes.ASSIGNMENT) return True def lvalue_type_from_base(self, expr_node: Var, @@ -2675,7 +2685,8 @@ def check_simple_assignment(self, lvalue_type: Optional[Type], rvalue: Expressio context: Context, msg: str = message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT, lvalue_name: str = 'variable', - rvalue_name: str = 'expression') -> Type: + rvalue_name: str = 'expression', *, + code: Optional[ErrorCode] = None) -> Type: if self.is_stub and isinstance(rvalue, EllipsisExpr): # '...' is always a valid initializer in a stub. return AnyType(TypeOfAny.special_form) @@ -2690,7 +2701,7 @@ def check_simple_assignment(self, lvalue_type: Optional[Type], rvalue: Expressio elif lvalue_type: self.check_subtype(rvalue_type, lvalue_type, context, msg, '{} has type'.format(rvalue_name), - '{} has type'.format(lvalue_name)) + '{} has type'.format(lvalue_name), code=code) return rvalue_type def check_member_assignment(self, instance_type: Type, attribute_type: Type, @@ -2892,7 +2903,8 @@ def check_return_stmt(self, s: ReturnStmt) -> None: supertype_label='expected', supertype=return_type, context=s, - msg=message_registry.INCOMPATIBLE_RETURN_VALUE_TYPE) + msg=message_registry.INCOMPATIBLE_RETURN_VALUE_TYPE, + code=codes.RETURN_VALUE) else: # Empty returns are valid in Generators with Any typed returns, but not in # coroutines. @@ -3674,7 +3686,8 @@ def find_isinstance_check(self, node: Expression def check_subtype(self, subtype: Type, supertype: Type, context: Context, msg: str = message_registry.INCOMPATIBLE_TYPES, subtype_label: Optional[str] = None, - supertype_label: Optional[str] = None) -> bool: + supertype_label: Optional[str] = None, *, + code: Optional[ErrorCode] = None) -> bool: """Generate an error if the subtype is not compatible with supertype.""" if is_subtype(subtype, supertype): @@ -3697,7 +3710,7 @@ def check_subtype(self, subtype: Type, supertype: Type, context: Context, notes = append_invariance_notes([], subtype, supertype) if extra_info: msg += ' (' + ', '.join(extra_info) + ')' - self.fail(msg, context) + self.fail(msg, context, code=code) for note in notes: self.msg.note(note, context) if note_msg: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index e276ee920641..22373bf16463 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -59,6 +59,7 @@ from mypy.visitor import ExpressionVisitor from mypy.plugin import Plugin, MethodContext, MethodSigContext, FunctionContext from mypy.typeops import tuple_fallback +import mypy.errorcodes as codes # Type of callback user for checking individual function arguments. See # check_args() below for details. @@ -483,7 +484,8 @@ def check_typeddict_call_with_kwargs(self, callee: TypedDictType, lvalue_type=item_expected_type, rvalue=item_value, context=item_value, msg=message_registry.INCOMPATIBLE_TYPES, lvalue_name='TypedDict item "{}"'.format(item_name), - rvalue_name='expression') + rvalue_name='expression', + code=codes.TYPEDDICT_ITEM) return callee @@ -3214,7 +3216,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) + self.chk.fail(message_registry.TOO_FEW_ARGS_FOR_SUPER, e, code=codes.CALL_ARG) return AnyType(TypeOfAny.from_error) elif not e.info: # This has already been reported by the semantic analyzer. diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index adbb815252a5..01c2d4fdfd1c 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -29,22 +29,77 @@ def __str__(self) -> str: 'call-arg', "Check number, names and kinds of arguments in calls", 'General') # type: Final ARG_TYPE = ErrorCode( 'arg-type', "Check argument types in calls", 'General') # type: Final +CALL_OVERLOAD = ErrorCode( + 'call-overload', "Check that an overload variant matches arguments", 'General') # type: Final VALID_TYPE = ErrorCode( 'valid-type', "Check that type (annotation) is valid", 'General') # type: Final -MISSING_ANN = ErrorCode( +VAR_ANNOTATED = ErrorCode( 'var-annotated', "Require variable annotation if type can't be inferred", 'General') # type: Final OVERRIDE = ErrorCode( 'override', "Check that method override is compatible with base class", 'General') # type: Final +RETURN = ErrorCode( + 'return', "Check that function always returns a value", 'General') # type: Final RETURN_VALUE = ErrorCode( 'return-value', "Check that return value is compatible with signature", 'General') # type: Final ASSIGNMENT = ErrorCode( 'assignment', "Check that assigned value is compatible with target", 'General') # type: Final +TYPE_ARG = ErrorCode( + 'type-arg', "Check that generic type arguments are present", 'General') # type: Final +TYPE_VAR = ErrorCode( + 'type-var', "Check that type variable values are valid", 'General') # type: Final +UNION_ATTR = ErrorCode( + 'union-attr', "Check that attribute exists in each item of a union", 'General') # type: Final +INDEX = ErrorCode( + 'index', "Check indexing operations", 'General') # type: Final +OPERATOR = ErrorCode( + 'operator', "Check that operator is valid for operands", 'General') # type: Final +LIST_ITEM = ErrorCode( + 'list-item', "Check list items in a list expression [item, ...]", 'General') # type: Final +DICT_ITEM = ErrorCode( + 'dict-item', + "Check dict items in a dict expression {key: value, ...}", 'General') # type: Final +TYPEDDICT_ITEM = ErrorCode( + 'typeddict-item', "Check items when constructing TypedDict", 'General') # type: Final +HAS_TYPE = ErrorCode( + 'has-type', "Check that type of reference can be determined", 'General') # type: Final +IMPORT = ErrorCode( + 'import', "Require that imported module can be found or has stubs", 'General') # type: Final +NO_REDEF = ErrorCode( + 'no-redef', "Check that each name is defined once", 'General') # type: Final +FUNC_RETURNS_VALUE = ErrorCode( + 'func-returns-value', "Check that called function returns a value in value context", + 'General') # type: Final +ABSTRACT = ErrorCode( + 'abstract', "Prevent instantiation of classes with abstract attributes", + 'General') # type: Final +VALID_NEWTYPE = ErrorCode( + 'valid-newtype', "Check that argument 2 to NewType is valid", 'General') # type: Final + +# These error codes aren't enable by default. +NO_UNTYPED_DEF = ErrorCode( + 'no-untyped-def', "Check that every function has an annotation", 'General') # type: Final +NO_UNTYPED_CALL = ErrorCode( + 'no-untyped-call', + "Disallow calling functions without type annotations from annotated functions", + 'General') # type: Final +REDUNDANT_CAST = ErrorCode( + 'redundant-cast', "Check that cast changes type of expression", 'General') # type: Final +COMPARISON_OVERLAP = ErrorCode( + 'comparison-overlap', + "Check that types in comparisons and 'in' expressions overlap", 'General') # type: Final +NO_ANY_UNIMPORTED = ErrorCode( + 'no-any-unimported', 'Reject "Any" types from unfollowed imports', 'General') # type: Final +NO_ANY_RETURN = ErrorCode( + 'no-any-return', 'Reject returning value with "Any" type if return type is not "Any"', + 'General') # type: Final +# Syntax errors are often blocking. SYNTAX = ErrorCode( 'syntax', "Report syntax errors", 'General') # type: Final +# This is a catch-all for remaining uncategorized errors. MISC = ErrorCode( 'misc', "Miscenallenous other checks", 'General') # type: Final diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 02957c4c6802..3672401669c7 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -208,7 +208,7 @@ def parse_type_comment(type_comment: str, 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, e.offset, err_msg, blocker=True) + errors.report(line, e.offset, err_msg, blocker=True, code=codes.SYNTAX) return None, None else: raise diff --git a/mypy/message_registry.py b/mypy/message_registry.py index f18f235ad05c..195807216a2b 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -89,8 +89,6 @@ GENERIC_INSTANCE_VAR_CLASS_ACCESS = \ 'Access to generic instance variables via class is ambiguous' # type: Final BARE_GENERIC = 'Missing type parameters for generic type {}' # type: Final -# TODO: remove when the old semantic analyzer is gone -BARE_GENERIC_OLD = 'Missing type parameters for generic type' # type: Final IMPLICIT_GENERIC_ANY_BUILTIN = \ 'Implicit generic "Any". Use "{}" and specify generic parameters' # type: Final diff --git a/mypy/messages.py b/mypy/messages.py index 5ab629e9b32b..2023961c1d94 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -178,7 +178,7 @@ def has_no_attr(self, original_type: Type, typ: Type, member: str, context: Cont self.fail('Member "{}" is not assignable'.format(member), context) elif member == '__contains__': self.fail('Unsupported right operand type for in ({})'.format( - format_type(original_type)), context) + format_type(original_type)), context, code=codes.OPERATOR) elif member in op_methods.values(): # Access to a binary operator member (e.g. _add). This case does # not handle indexing operations. @@ -188,13 +188,13 @@ def has_no_attr(self, original_type: Type, typ: Type, member: str, context: Cont break elif member == '__neg__': self.fail('Unsupported operand type for unary - ({})'.format( - format_type(original_type)), context) + format_type(original_type)), context, code=codes.OPERATOR) elif member == '__pos__': self.fail('Unsupported operand type for unary + ({})'.format( - format_type(original_type)), context) + format_type(original_type)), context, code=codes.OPERATOR) elif member == '__invert__': self.fail('Unsupported operand type for ~ ({})'.format( - format_type(original_type)), context) + format_type(original_type)), context, code=codes.OPERATOR) elif member == '__getitem__': # Indexed get. # TODO: Fix this consistently in format_type @@ -203,18 +203,19 @@ def has_no_attr(self, original_type: Type, typ: Type, member: str, context: Cont format_type(original_type)), context) else: self.fail('Value of type {} is not indexable'.format( - format_type(original_type)), context) + format_type(original_type)), context, code=codes.INDEX) elif member == '__setitem__': # Indexed set. - self.fail('Unsupported target for indexed assignment', context) + self.fail('Unsupported target for indexed assignment', context, code=codes.INDEX) 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) + self.fail('Cannot call function of unknown type', context, code=codes.OPERATOR) else: - self.fail('{} not callable'.format(format_type(original_type)), context) + self.fail('{} not callable'.format(format_type(original_type)), context, + code=codes.OPERATOR) else: # The non-special case: a missing ordinary attribute. extra = '' @@ -254,7 +255,8 @@ def has_no_attr(self, original_type: Type, typ: Type, member: str, context: Cont 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) + typ_format, orig_type_format, member, extra), context, + code=codes.UNION_ATTR) return AnyType(TypeOfAny.from_error) def unsupported_operand_types(self, op: str, left_type: Any, @@ -280,7 +282,7 @@ def unsupported_operand_types(self, op: str, left_type: Any, else: msg = 'Unsupported operand types for {} ({} and {})'.format( op, left_str, right_str) - self.fail(msg, context) + self.fail(msg, context, code=codes.OPERATOR) def unsupported_left_operand(self, op: str, typ: Type, context: Context) -> None: @@ -289,7 +291,7 @@ def unsupported_left_operand(self, op: str, typ: Type, else: msg = 'Unsupported left operand type for {} ({})'.format( op, format_type(typ)) - self.fail(msg, context) + self.fail(msg, context, code=codes.OPERATOR) def not_callable(self, typ: Type, context: Context) -> Type: self.fail('{} not callable'.format(format_type(typ)), context) @@ -297,7 +299,8 @@ def not_callable(self, typ: Type, context: Context) -> Type: 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) + self.fail('Call to untyped function {} in typed context'.format(name), context, + code=codes.NO_UNTYPED_CALL) return AnyType(TypeOfAny.from_error) def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: Type, @@ -350,6 +353,7 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: target = 'to {} '.format(name) msg = '' + code = codes.MISC notes = [] # type: List[str] if callee_name == '': name = callee_name[1:-1] @@ -358,6 +362,7 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: callee.arg_types[0]) msg = '{} item {} has incompatible type {}; expected {}'.format( name.title(), n, actual_type_str, expected_type_str) + code = codes.LIST_ITEM elif callee_name == '': name = callee_name[1:-1] n -= 1 @@ -382,6 +387,7 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: msg = '{} entry {} has incompatible type {}: {}; expected {}: {}'.format( name.title(), n, key_type_str, value_type_str, expected_key_type_str, expected_value_type_str) + code = codes.DICT_ITEM elif callee_name == '': actual_type_str, expected_type_str = map(strip_quotes, format_type_distinctly(arg_type, @@ -442,6 +448,7 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: msg = 'Argument {} {}has incompatible type {}; expected {}'.format( arg_label, target, quote_type_string(arg_type_str), quote_type_string(expected_type_str)) + code = codes.ARG_TYPE if isinstance(expected_type, UnionType): expected_types = expected_type.items else: @@ -449,7 +456,7 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: for type in expected_types: if isinstance(arg_type, Instance) and isinstance(type, Instance): notes = append_invariance_notes(notes, arg_type, type) - self.fail(msg, context) + self.fail(msg, context, code=code) if notes: for note_msg in notes: self.note(note_msg, context) @@ -458,7 +465,7 @@ def invalid_index_type(self, index_type: Type, expected_type: Type, base_str: st 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) + index_str, base_str, expected_str), context, code=codes.INDEX) def too_few_arguments(self, callee: CallableType, context: Context, argument_names: Optional[Sequence[Optional[str]]]) -> None: @@ -477,15 +484,15 @@ def too_few_arguments(self, callee: CallableType, context: Context, msg += ' "{}" in call to {}'.format(args, callee_name) else: msg = 'Too few arguments' + for_function(callee) - self.fail(msg, context) + self.fail(msg, context, code=codes.CALL_ARG) 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) + self.fail(msg, context, code=codes.CALL_ARG) def too_many_arguments(self, callee: CallableType, context: Context) -> None: msg = 'Too many arguments' + for_function(callee) - self.fail(msg, context) + self.fail(msg, context, code=codes.CALL_ARG) def too_many_arguments_from_typed_dict(self, callee: CallableType, @@ -509,7 +516,7 @@ def too_many_positional_arguments(self, callee: CallableType, def unexpected_keyword_argument(self, callee: CallableType, name: str, context: Context) -> None: msg = 'Unexpected keyword argument "{}"'.format(name) + for_function(callee) - self.fail(msg, context) + self.fail(msg, context, code=codes.CALL_ARG) module = find_defining_module(self.modules, callee) if module: assert callee.definition is not None @@ -531,9 +538,10 @@ 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) + self.fail('{} does not return a value'.format(capitalize(name)), context, + code=codes.FUNC_RETURNS_VALUE) else: - self.fail('Function does not return a value', context) + self.fail('Function does not return a value', context, code=codes.FUNC_RETURNS_VALUE) def deleted_as_rvalue(self, typ: DeletedType, context: Context) -> None: """Report an error about using an deleted type as an rvalue.""" @@ -569,13 +577,13 @@ def no_variant_matches_arguments(self, num_args = len(arg_types) if num_args == 0: self.fail('All overload variants{} require at least one argument'.format(name_str), - context) + context, code=codes.CALL_OVERLOAD) elif num_args == 1: self.fail('No overload variant{} matches argument type {}' - .format(name_str, arg_types_str), context) + .format(name_str, arg_types_str), context, code=codes.CALL_OVERLOAD) else: self.fail('No overload variant{} matches argument types {}' - .format(name_str, arg_types_str), context) + .format(name_str, arg_types_str), context, code=codes.CALL_OVERLOAD) self.pretty_overload_matches(plausible_targets, overload, context, offset=2, max_items=2) @@ -605,7 +613,7 @@ def overload_signature_incompatible_with_supertype( overload: Overloaded, context: Context) -> None: target = self.override_target(name, name_in_super, supertype) self.fail('Signature of "{}" incompatible with {}'.format( - name, target), context) + name, target), context, code=codes.OVERRIDE) note_template = 'Overload variants must be defined in the same order as they are in "{}"' self.note(note_template.format(supertype), context) @@ -615,7 +623,7 @@ def signature_incompatible_with_supertype( context: Context) -> None: target = self.override_target(name, name_in_super, supertype) self.fail('Signature of "{}" incompatible with {}'.format( - name, target), context) + name, target), context, code=codes.OVERRIDE) def argument_incompatible_with_supertype( self, arg_num: int, name: str, type_name: Optional[str], @@ -625,7 +633,9 @@ def argument_incompatible_with_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 "{}"' - .format(arg_num, name, target, arg_type_in_supertype_f), context) + .format(arg_num, name, target, arg_type_in_supertype_f), + context, + code=codes.OVERRIDE) if name == "__eq__" and type_name: multiline_msg = self.comparison_method_example_msg(class_name=type_name) @@ -647,7 +657,9 @@ def return_type_incompatible_with_supertype( 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 {}' - .format(override_str, name, original_str, target), context) + .format(override_str, name, original_str, target), + context, + code=codes.OVERRIDE) def override_target(self, name: str, name_in_super: str, supertype: str) -> str: @@ -733,7 +745,7 @@ def string_interpolation_mixing_key_and_non_keys(self, context: Context) -> None self.fail('String interpolation mixes specifier with and without mapping keys', context) def cannot_determine_type(self, name: str, context: Context) -> None: - self.fail("Cannot determine type of '%s'" % name, context) + self.fail("Cannot determine type of '%s'" % name, context, code=codes.HAS_TYPE) 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) @@ -759,7 +771,7 @@ def cannot_instantiate_abstract_class(self, class_name: str, self.fail("Cannot instantiate abstract class '%s' with abstract " "attribute%s %s" % (class_name, plural_s(abstract_attributes), attrs), - context) + context, code=codes.ABSTRACT) def base_class_definitions_incompatible(self, name: str, base1: TypeInfo, base2: TypeInfo, @@ -807,14 +819,16 @@ 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) + context, + code=codes.TYPE_VAR) 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: {})' left_typ, right_typ = format_type_distinctly(left, right) - self.fail(message.format(kind, left_str, left_typ, right_str, right_typ), ctx) + self.fail(message.format(kind, left_str, left_typ, right_str, right_typ), ctx, + code=codes.COMPARISON_OVERLAP) def overload_inconsistently_applies_decorator(self, decorator: str, context: Context) -> None: self.fail( @@ -899,11 +913,12 @@ def unsupported_type_type(self, item: Type, context: Context) -> None: self.fail('Unsupported type Type[{}]'.format(format_type(item)), context) def redundant_cast(self, typ: Type, context: Context) -> None: - self.fail('Redundant cast to {}'.format(format_type(typ)), context) + self.fail('Redundant cast to {}'.format(format_type(typ)), context, + code=codes.REDUNDANT_CAST) 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) + ctx, code=codes.NO_ANY_UNIMPORTED) def need_annotation_for_var(self, node: SymbolNode, context: Context, python_version: Optional[Tuple[int, int]] = None) -> None: @@ -927,7 +942,8 @@ 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) + self.fail("Need type {} for '{}'{}".format(needed, unmangle(node.name()), hint), context, + code=codes.VAR_ANNOTATED) def explicit_any(self, ctx: Context) -> None: self.fail('Explicit "Any" is not allowed', ctx) @@ -947,7 +963,7 @@ def unexpected_typeddict_keys( missing = [key for key in expected_keys if key not in actual_set] self.fail('{} missing for TypedDict {}'.format( format_key_list(missing, short=True).capitalize(), format_type(typ)), - context) + context, code=codes.TYPEDDICT_ITEM) return else: extra = [key for key in actual_keys if key not in expected_set] @@ -956,7 +972,7 @@ def unexpected_typeddict_keys( # simplicity. self.fail('Extra {} for TypedDict {}'.format( format_key_list(extra, short=True), format_type(typ)), - context) + context, code=codes.TYPEDDICT_ITEM) return found = format_key_list(actual_keys, short=True) if not expected_keys: @@ -965,7 +981,8 @@ def unexpected_typeddict_keys( 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) + self.fail('Expected {} but found {}'.format(expected, found), context, + code=codes.TYPEDDICT_ITEM) def typeddict_key_must_be_string_literal( self, @@ -1019,7 +1036,7 @@ def disallowed_any_type(self, typ: Type, context: Context) -> None: 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) + self.fail(message, context, code=codes.NO_ANY_RETURN) def untyped_decorated_function(self, typ: Type, context: Context) -> None: if isinstance(typ, AnyType): diff --git a/mypy/semanal.py b/mypy/semanal.py index 846cc3418dbc..09b027a3a8d6 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1784,7 +1784,7 @@ def report_missing_module_attribute(self, import_id: str, source_id: str, import if matches: suggestion = "; maybe {}?".format(pretty_or(matches)) message += "{}".format(suggestion) - self.fail(message, context) + self.fail(message, context, code=codes.ATTR_DEFINED) self.add_unknown_imported_symbol(imported_id, context) if import_id == 'typing': @@ -4540,7 +4540,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) + self.fail("{} '{}' already defined{}".format(noun, unmangle(name), extra_msg), ctx, + code=codes.NO_REDEF) def name_already_defined(self, name: str, diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index ff9532dcd2ae..dc99e96385ee 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -19,6 +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 errorcodes as codes class NewTypeAnalyzer: @@ -74,7 +76,7 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> bool: 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) + self.fail(message.format(format_type(old_type)), s, code=codes.VALID_NEWTYPE) # Otherwise the error was already reported. old_type = AnyType(TypeOfAny.from_error) object_type = self.api.named_type('__builtins__.object') @@ -198,5 +200,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) -> None: - self.api.fail(msg, ctx) + def fail(self, msg: str, ctx: Context, *, code: Optional[ErrorCode] = None) -> None: + self.api.fail(msg, ctx, code=code) diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index c54c3bb6c601..e52612282873 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -5,7 +5,7 @@ operations, including subtype checks. """ -from typing import List +from typing import List, Optional from mypy.nodes import TypeInfo, Context, MypyFile, FuncItem, ClassDef, Block from mypy.types import Type, Instance, TypeVarType, AnyType @@ -15,7 +15,8 @@ from mypy.errors import Errors from mypy.scope import Scope from mypy.options import Options -from mypy import message_registry +from mypy.errorcodes import ErrorCode +from mypy import message_registry, errorcodes as codes class TypeArgumentAnalyzer(MixedTraverserVisitor): @@ -58,7 +59,7 @@ def visit_instance(self, t: Instance) -> None: if not arg_values: self.fail('Type variable "{}" not valid as type ' 'argument value for "{}"'.format( - arg.name, info.name()), t) + arg.name, info.name()), t, code=codes.TYPE_VAR) continue else: arg_values = [arg] @@ -66,7 +67,7 @@ def visit_instance(self, t: Instance) -> None: 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) + arg, info.name(), tvar.upper_bound), t, code=codes.TYPE_VAR) super().visit_instance(t) def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: str, @@ -77,12 +78,15 @@ def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: s for value in valids)): if len(actuals) > 1 or not isinstance(actual, Instance): self.fail('Invalid type argument value for "{}"'.format( - type.name()), context) + type.name()), context, code=codes.TYPE_VAR) 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) + self.fail( + message_registry.INCOMPATIBLE_TYPEVAR_VALUE.format( + arg_name, class_name, actual_type_name), + context, + code=codes.TYPE_VAR) - def fail(self, msg: str, context: Context) -> None: - self.errors.report(context.get_line(), context.get_column(), msg) + def fail(self, msg: str, context: Context, *, code: Optional[ErrorCode] = None) -> None: + self.errors.report(context.get_line(), context.get_column(), msg, code=code) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index f8d9a475660a..e2f5d909e92f 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -7,6 +7,7 @@ from typing import Callable, List, Optional, Set, Tuple, Iterator, TypeVar, Iterable from typing_extensions import Final +from mypy_extensions import DefaultNamedArg from mypy.messages import MessageBuilder, quote_type_string, format_type_bare from mypy.options import Options @@ -30,7 +31,7 @@ from mypy.plugin import Plugin, TypeAnalyzerPluginInterface, AnalyzeTypeContext from mypy.semanal_shared import SemanticAnalyzerCoreInterface from mypy.errorcodes import ErrorCode -from mypy import nodes, message_registry +from mypy import nodes, message_registry, errorcodes as codes T = TypeVar('T') @@ -431,7 +432,7 @@ def analyze_unbound_type_without_type_info(self, t: UnboundType, sym: SymbolTabl ' inside a function)'.format(short, short)) else: message = 'Cannot interpret reference "{}" as a type' - self.fail(message.format(name), t) + self.fail(message.format(name), t, code=codes.VALID_TYPE) for note in notes: self.note(note, t) @@ -488,7 +489,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) + self.fail('Syntax error in type annotation', t, code=codes.SYNTAX) if len(t.items) == 1: self.note('Suggestion: Is there a spurious trailing comma?', t) else: @@ -543,7 +544,7 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> Type: # or just misspelled a regular type. So we avoid guessing. msg = 'Invalid type comment or annotation' - self.fail(msg, t) + self.fail(msg, t, code=codes.VALID_TYPE) if t.note is not None: self.note(t.note, t) @@ -870,27 +871,32 @@ def tuple_type(self, items: List[Type]) -> TupleType: TypeVarList = List[Tuple[str, TypeVarExpr]] +# Mypyc doesn't support callback protocols yet. +FailCallback = Callable[[str, Context, DefaultNamedArg(Optional[ErrorCode], 'code')], None] -def get_omitted_any(disallow_any: bool, fail: Callable[[str, Context], None], + +def get_omitted_any(disallow_any: bool, fail: FailCallback, typ: Type, fullname: Optional[str] = None, unexpanded_type: Optional[Type] = None) -> AnyType: if disallow_any: if fullname in nongen_builtins: # 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) + fail(message_registry.IMPLICIT_GENERIC_ANY_BUILTIN.format(alternative), typ, + code=codes.TYPE_ARG) else: typ = unexpanded_type or typ 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) + fail(message_registry.BARE_GENERIC.format(quote_type_string(type_str)), typ, + code=codes.TYPE_ARG) any_type = AnyType(TypeOfAny.from_error, line=typ.line, column=typ.column) else: any_type = AnyType(TypeOfAny.from_omitted_generics, line=typ.line, column=typ.column) return any_type -def fix_instance(t: Instance, fail: Callable[[str, Context], None], +def fix_instance(t: Instance, fail: FailCallback, disallow_any: bool, use_generic_error: bool = False, unexpanded_type: Optional[Type] = None) -> None: """Fix a malformed instance by replacing all type arguments with Any. @@ -916,7 +922,7 @@ def fix_instance(t: Instance, fail: Callable[[str, Context], None], if act == '0': act = 'none' fail('"{}" expects {}, but {} given'.format( - t.type.name(), s, act), t) + t.type.name(), s, act), t, code=codes.TYPE_ARG) # Construct the correct number of type arguments, as # otherwise the type checker may crash as it expects # things to be right. @@ -925,7 +931,7 @@ def fix_instance(t: Instance, fail: Callable[[str, Context], None], def expand_type_alias(target: Type, alias_tvars: List[str], args: List[Type], - fail: Callable[[str, Context], None], no_args: bool, ctx: Context, *, + fail: FailCallback, no_args: bool, ctx: Context, *, unexpanded_type: Optional[Type] = None, disallow_any: bool = False) -> Type: """Expand a (generic) type alias target following the rules outlined in TypeAlias docstring. @@ -994,7 +1000,7 @@ def set_any_tvars(tp: Type, vars: List[str], newline: int, newcolumn: int, *, from_error: bool = False, disallow_any: bool = False, - fail: Optional[Callable[[str, Context], None]] = None, + fail: Optional[FailCallback] = None, unexpanded_type: Optional[Type] = None) -> Type: if from_error or disallow_any: type_of_any = TypeOfAny.from_error @@ -1006,7 +1012,7 @@ def set_any_tvars(tp: Type, vars: List[str], 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)) + Context(newline, newcolumn), code=codes.TYPE_ARG) any_type = AnyType(type_of_any, line=newline, column=newcolumn) return replace_alias_tvars(tp, vars, [any_type] * len(vars), newline, newcolumn) @@ -1176,7 +1182,7 @@ def make_optional_type(t: Type) -> Type: return UnionType([t, NoneType()], t.line, t.column) -def fix_instance_types(t: Type, fail: Callable[[str, Context], None]) -> None: +def fix_instance_types(t: Type, fail: FailCallback) -> None: """Recursively fix all instance types (type argument count) in a given type. For example 'Union[Dict, List[str, int]]' will be transformed into @@ -1186,7 +1192,7 @@ def fix_instance_types(t: Type, fail: Callable[[str, Context], None]) -> None: class InstanceFixer(TypeTraverserVisitor): - def __init__(self, fail: Callable[[str, Context], None]) -> None: + def __init__(self, fail: FailCallback) -> None: self.fail = fail def visit_instance(self, typ: Instance) -> None: diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 0893808345bb..74d0339ebffa 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -6,7 +6,12 @@ import m m.x # E: Module has no attribute "x" [attr-defined] 'x'.foobar # E: "str" has no attribute "foobar" [attr-defined] +from m import xx # E: Module 'm' has no attribute 'xx' [attr-defined] +from m import think # E: Module 'm' has no attribute 'think'; maybe "thing"? [attr-defined] +for x in 1: # E: "int" has no attribute "__iter__" (not iterable) [attr-defined] + pass [file m.py] +thing = 0 [builtins fixtures/module.pyi] [case testErrorCodeUndefinedName] @@ -33,6 +38,15 @@ def f(): # E: Type signature has too many arguments [syntax] # type: (int) -> None 1 +x = 0 # type: x y # E: syntax error in type comment 'x y' [syntax] + +[case testErrorCodeSyntaxError3] +# This is a bit inconsistent -- syntax error would be more logical? +x: 'a b' # E: Invalid type comment or annotation [valid-type] +for v in x: # type: int, int # E: Syntax error in type annotation [syntax] \ + # N: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) + pass + [case testErrorCodeSyntaxError_python2] 1 '' # E: invalid syntax [syntax] @@ -41,6 +55,14 @@ def f(): # E: Type signature has too many arguments [syntax] # type: (int) -> None 1 +x = 0 # type: x y # E: syntax error in type comment 'x y' [syntax] + +[case testErrorCodeSyntaxError3_python2] +def f(): pass +for v in f(): # type: int, int # E: Syntax error in type annotation [syntax] \ + # N: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) + pass + [case testErrorCodeIgnore1] 'x'.foobar # type: ignore[attr-defined] 'x'.foobar # type: ignore[xyz] # E: "str" has no attribute "foobar" [attr-defined] @@ -110,3 +132,372 @@ def h(x # type: xyz # type: ignore[foo] # E: Name 'xyz' is not defined [name ): # type () -> None pass + +[case testErrorCodeArgKindAndCount] +def f(x: int) -> None: pass # N: "f" defined here +f() # E: Too few arguments for "f" [call-arg] +f(1, 2) # E: Too many arguments for "f" [call-arg] +f(y=1) # E: Unexpected keyword argument "y" for "f" [call-arg] + +def g(*, x: int) -> None: pass +g() # E: Missing named argument "x" for "g" [call-arg] + +def h(x: int, y: int, z: int) -> None: pass +h(y=1, z=1) # E: Missing positional argument "x" in call to "h" [call-arg] +h(y=1) # E: Missing positional arguments "x", "z" in call to "h" [call-arg] + +[case testErrorCodeSuperArgs_python2] +class A: + def f(self): + pass +class B(A): + def f(self): # type: () -> None + super().f() # E: Too few arguments for "super" [call-arg] + +[case testErrorCodeArgType] +def f(x: int) -> None: pass +f('') # E: Argument 1 to "f" has incompatible type "str"; expected "int" [arg-type] + +class A: + def g(self, *, x: int) -> None: pass + +A().g(x='') # E: Argument "x" to "g" of "A" has incompatible type "str"; expected "int" [arg-type] + +[case testErrorCodeInvalidType] +def f(): pass + +x: f # E: Function "__main__.f" is not valid as a type [valid-type] \ + # N: Perhaps you need "Callable[...]" or a callback protocol? + +import sys +y: sys # E: Module "sys" is not valid as a type [valid-type] +z: y # E: Variable "__main__.y" is not valid as a type [valid-type] + +[case testErrorCodeNeedTypeAnnotation] +from typing import TypeVar + +T = TypeVar('T') +def f() -> T: pass +x = f() # E: Need type annotation for 'x' [var-annotated] +y = [] # E: Need type annotation for 'y' (hint: "y: List[] = ...") [var-annotated] +[builtins fixtures/list.pyi] + +[case testErrorCodeBadOverride] +from typing import overload + +class A: + def f(self) -> int: + return 0 +class B(A): + def f(self) -> str: # E: Return type "str" of "f" incompatible with return type "int" in supertype "A" [override] + return '' +class C(A): + def f(self, x: int) -> int: # E: Signature of "f" incompatible with supertype "A" [override] + return 0 +class D: + def f(self, x: int) -> int: + return 0 +class E(D): + def f(self, x: str) -> int: # E: Argument 1 of "f" is incompatible with supertype "D"; supertype defines the argument type as "int" [override] + return 0 + +class O: + @overload + def f(self, x: int) -> None: pass + @overload + def f(self, x: str) -> None: pass + def f(self, x): + pass + +class OO(O): + @overload # E: Signature of "f" incompatible with supertype "O" [override] \ + # N: Overload variants must be defined in the same order as they are in "O" + def f(self, x: str) -> None: pass + @overload + def f(self, x: int) -> None: pass + def f(self, x): + pass + +[case testErrorCodeReturnValue] +def f() -> int: + return '' # E: Incompatible return value type (got "str", expected "int") [return-value] + +[case testErrorCodeAssignment] +x: str = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment] + +def f(x: str = 0) -> None: # E: Incompatible default for argument "x" (default has type "int", argument has type "str") [assignment] + pass + +class A: + x = 0 + +class B(A): + x = '' # E: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int") [assignment] + +[case testErrorCodeMissingTypeArg] +# flags: --disallow-any-generics +from typing import List, TypeVar +x: List # E: Missing type parameters for generic type "List" [type-arg] +y: list # E: Implicit generic "Any". Use "typing.List" and specify generic parameters [type-arg] +T = TypeVar('T') +L = List[List[T]] +z: L # E: Missing type parameters for generic type "L" [type-arg] +[builtins fixtures/list.pyi] + +[case testErrorCodeUnionAttribute] +from typing import Union +class A: + x: int +class B: + y: str +a: Union[A, B] +a.x # E: Item "B" of "Union[A, B]" has no attribute "x" [union-attr] + +[case testErrorCodeFunctionHasNoAnnotation] +# flags: --disallow-untyped-defs + +def f(x): # E: Function is missing a type annotation [no-untyped-def] + pass + +def g(x: int): # E: Function is missing a return type annotation [no-untyped-def] + pass + +def h(x) -> None: # E: Function is missing a type annotation for one or more arguments [no-untyped-def] + pass + +def gen(): # E: Function is missing a return type annotation [no-untyped-def] \ + # N: Use "-> None" if function does not return a value + yield 1 + +def gen2(x: int): # E: Function is missing a return type annotation [no-untyped-def] + yield 1 + +async def asyncf(): # E: Function is missing a return type annotation [no-untyped-def] + return 0 + +async def asyncf2(x: int): # E: Function is missing a return type annotation [no-untyped-def] + return 0 +[typing fixtures/typing-full.pyi] + +[case testErrorCodeCallUntypedFunction] +# flags: --disallow-untyped-calls + +def f() -> None: + g() # E: Call to untyped function "g" in typed context [no-untyped-call] + +def g(): + pass + +[case testErrorCodeIndexing] +from typing import Dict +x: Dict[int, int] +x[''] # E: Invalid index type "str" for "Dict[int, int]"; expected type "int" [index] +1[''] # E: Value of type "int" is not indexable [index] +1[''] = 1 # E: Unsupported target for indexed assignment [index] +[builtins fixtures/dict.pyi] + +[case testErrorCodeInvalidTypeArg] +from typing import TypeVar, Generic + +T = TypeVar('T', int, str) +TT = TypeVar('TT', int, None) +S = TypeVar('S', bound=str) + +def f(x: T) -> T: + return x + +f(object()) # E: Value of type variable "T" of "f" cannot be "object" [type-var] + +def g(x: S) -> S: + return x + +g(1) # E: Value of type variable "S" of "g" cannot be "int" [type-var] + +class C(Generic[T]): pass +class D(Generic[S]): pass +class E(Generic[S, T]): pass + +x: C[object] # E: Value of type variable "T" of "C" cannot be "object" [type-var] +y: D[int] # E: Type argument "builtins.int" of "D" must be a subtype of "builtins.str" [type-var] +z: D[int, int] # E: "D" expects 1 type argument, but 2 given [type-arg] + +def h(a: TT, s: S) -> None: + b: C[TT] # E: Invalid type argument value for "C" [type-var] + c: C[S] # E: Type variable "S" not valid as type argument value for "C" [type-var] + +[case testErrorCodeOperators] +class A: pass +A() + 1 # E: Unsupported left operand type for + ("A") [operator] +1 in A() # E: Unsupported right operand type for in ("A") [operator] +A() < 1 # E: Unsupported left operand type for < ("A") [operator] +-A() # E: Unsupported operand type for unary - ("A") [operator] ++A() # E: Unsupported operand type for unary + ("A") [operator] +~A() # E: Unsupported operand type for ~ ("A") [operator] + +class B: + def __add__(self, other: int) -> 'B': + return self + def __radd__(self, other: int) -> 'B': + return self + def __contains__(self, other: int) -> int: + return 0 + +B() + '' # E: Unsupported operand types for + ("B" and "str") [operator] +'' + B() # E: Unsupported operand types for + ("str" and "B") [operator] +'' in B() # E: Unsupported operand types for in ("str" and "B") [operator] + +1() # E: "int" not callable [operator] + +[case testErrorCodeListOrDictItem] +from typing import List, Dict +x: List[int] = [''] # E: List item 0 has incompatible type "str"; expected "int" [list-item] +y: Dict[int, int] = {1: ''} # E: Dict entry 0 has incompatible type "int": "str"; expected "int": "int" [dict-item] +[builtins fixtures/dict.pyi] + +[case testErrorCodeTypedDict] +from typing_extensions import TypedDict +class D(TypedDict): + x: int +class E(TypedDict): + x: int + y: int + +a: D = {'x': ''} # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") [typeddict-item] +b: D = {'y': ''} # E: Extra key 'y' for TypedDict "D" [typeddict-item] +c = D(x=0) if int() else E(x=0, y=0) +c = {} # E: Expected TypedDict key 'x' but found no keys [typeddict-item] +[builtins fixtures/dict.pyi] + +[case testErrorCodeCannotDetermineType] +x # E: Cannot determine type of 'x' [has-type] +x = None + +[case testErrorCodeRedundantCast] +# flags: --warn-redundant-casts +from typing import cast + +x = cast(int, int()) # E: Redundant cast to "int" [redundant-cast] + +[case testErrorCodeInvalidCommentSignature] +def f(x): # E: Type signature has too few arguments [syntax] + # type: () -> None + pass + +def g(x): # E: Type signature has too many arguments [syntax] + # type: (int, int) -> None + pass + +[case testErrorCodeInvalidCommentSignature_python2] +def f(x): # E: Type signature has too few arguments [syntax] + # type: () -> None + pass + +def g(x): # E: Type signature has too many arguments [syntax] + # type: (int, int) -> None + pass + +[case testErrorCodeNonOverlappingEquality] +# flags: --strict-equality +if int() == str(): # E: Non-overlapping equality check (left operand type: "int", right operand type: "str") [comparison-overlap] + pass +if int() != str(): # E: Non-overlapping equality check (left operand type: "int", right operand type: "str") [comparison-overlap] + pass +if int() is str(): # E: Non-overlapping identity check (left operand type: "int", right operand type: "str") [comparison-overlap] + pass +[builtins fixtures/primitives.pyi] + +[case testErrorCodeMissingModule] +from defusedxml import xyz # E: No library stub file for module 'defusedxml' [import] \ + # N: (Stub files are from https://github.com/python/typeshed) +from nonexistent import foobar # E: Cannot find module named 'nonexistent' [import] \ + # N: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports +import nonexistent2 # E: Cannot find module named 'nonexistent2' [import] +from nonexistent3 import * # E: Cannot find module named 'nonexistent3' [import] +from pkg import bad # E: Module 'pkg' has no attribute 'bad' [attr-defined] +from pkg.bad2 import bad3 # E: Cannot find module named 'pkg.bad2' [import] +[file pkg/__init__.py] + +[case testErrorCodeAlreadyDefined] +x: int +x: str # E: Name 'x' already defined on line 1 [no-redef] + +def f(): + pass +def f(): # E: Name 'f' already defined on line 4 [no-redef] + pass + +[case testErrorCodeMissingReturn] +def f() -> int: # E: Missing return statement [return] + x = 0 + +[case testErrorCodeFunctionDoesNotReturnValue] +from typing import Callable + +def f() -> None: pass + +x = f() # E: "f" does not return a value [func-returns-value] + +class A: + def g(self) -> None: pass + +y = A().g() # E: "g" of "A" does not return a value [func-returns-value] + +c: Callable[[], None] +z = c() # E: Function does not return a value [func-returns-value] + +[case testErrorCodeInstantiateAbstract] +from abc import abstractmethod + +class A: + @abstractmethod + def f(self): pass + +class B(A): + pass + +B() # E: Cannot instantiate abstract class 'B' with abstract attribute 'f' [abstract] + +[case testErrorCodeNewTypeNotSubclassable] +from typing import Union, NewType + +X = NewType('X', Union[int, str]) # E: Argument 2 to NewType(...) must be subclassable (got "Union[int, str]") [valid-newtype] + +[case testErrorCodeOverloadVariant] +from typing import overload + +@overload +def f(x: int) -> int: ... +@overload +def f(x: str) -> str: ... +def f(x): + return x + +f(object()) # E: No overload variant of "f" matches argument type "object" [call-overload] \ + # N: Possible overload variants: \ + # N: def f(x: int) -> int \ + # N: def f(x: str) -> str + +f() # E: All overload variants of "f" require at least one argument [call-overload] \ + # N: Possible overload variants: \ + # N: def f(x: int) -> int \ + # N: def f(x: str) -> str + +f(1, 1) # E: No overload variant of "f" matches argument types "int", "int" [call-overload] \ + # N: Possible overload variants: \ + # N: def f(x: int) -> int \ + # N: def f(x: str) -> str + +[case testErrorCodeAnyFromUnfollowedImport] +# flags: --disallow-any-unimported +from m import C # type: ignore +def f(x: C) -> None: # E: Argument 1 to "f" becomes "Any" due to an unfollowed import [no-any-unimported] + pass + +def g() -> C: ... # E: Return type becomes "Any" due to an unfollowed import [no-any-unimported] + +[case testErrorCodeReturnAny] +# flags: --warn-return-any +def f(): pass + +def g() -> int: + return f() # E: Returning Any from function declared to return "int" [no-any-return]