Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add error codes to many additional messages #7334

Merged
merged 39 commits into from
Aug 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c48ee62
Update error code of invalid argument messages
JukkaL Aug 12, 2019
2fe6373
Change error code for invalid argument types
JukkaL Aug 12, 2019
09c3570
Update error code of invalid type messages
JukkaL Aug 12, 2019
836d2ae
Add error codes for missing variable annotation
JukkaL Aug 12, 2019
d15923a
Change error code of invalid override errors
JukkaL Aug 12, 2019
08b98dd
Give error code for incompatible return value type
JukkaL Aug 12, 2019
7283416
Change error code for incompatible assignments
JukkaL Aug 12, 2019
a0f96f2
Change error code for missing generic type argument
JukkaL Aug 12, 2019
512fe64
Change error message for missing union attribute + fix self check
JukkaL Aug 12, 2019
f4638e9
Update test case
JukkaL Aug 12, 2019
bec13f3
Update error code when function annotation is missing
JukkaL Aug 12, 2019
a43e048
Update error code when calling an unannotated function
JukkaL Aug 12, 2019
0a0f490
Fix some has no attribute error codes
JukkaL Aug 12, 2019
6fb597b
Add error code for indexing operations
JukkaL Aug 13, 2019
9335b28
Add error code for incompatible type variable value
JukkaL Aug 13, 2019
c33a5ef
Add error codes to binary and unary operations
JukkaL Aug 13, 2019
0b11215
Add error codes for invalid list/dict items
JukkaL Aug 13, 2019
5b464d2
Add error codes for TypedDict literals
JukkaL Aug 13, 2019
b036992
Add error code when cannot determine type
JukkaL Aug 13, 2019
18d7730
Add error code for redundant cast
JukkaL Aug 13, 2019
3b55d70
Add test cases
JukkaL Aug 13, 2019
6f06f23
Add error code for non-overlapping comparisons
JukkaL Aug 13, 2019
88a5cfb
Add error code for missing module/stub
JukkaL Aug 13, 2019
b2eb60e
Add error code for invalid callable
JukkaL Aug 13, 2019
73b8b74
Improve test case
JukkaL Aug 13, 2019
5efb120
Add code for syntax error in type comment
JukkaL Aug 13, 2019
e3c10cd
Add more error codes for syntax errors in types
JukkaL Aug 13, 2019
9815a59
Add error code to errors about redefinition
JukkaL Aug 13, 2019
4ac28e0
Add error code for missing return statement
JukkaL Aug 13, 2019
63758e6
Fix bug
JukkaL Aug 13, 2019
92e5794
Add error code for function not returning a value
JukkaL Aug 13, 2019
f6befe4
Add error code for instantiating a class with abstract attributes
JukkaL Aug 13, 2019
6db278c
Add error code for invalid NewType base types
JukkaL Aug 13, 2019
c364741
Add error code for no matching overload item
JukkaL Aug 13, 2019
195e58d
Add error code for Any from unfollowed import
JukkaL Aug 13, 2019
8a1a399
Add error code for incorrectly returning Any
JukkaL Aug 13, 2019
6efdd30
Remove debug print
JukkaL Aug 13, 2019
7cef388
Try to fix mypyc
JukkaL Aug 14, 2019
1bec9c0
Add feedback
JukkaL Aug 14, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down
45 changes: 29 additions & 16 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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()

Expand All @@ -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__':
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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):
Expand All @@ -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:
Expand Down
6 changes: 4 additions & 2 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down
57 changes: 56 additions & 1 deletion mypy/errorcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have the same code for all ill-defined NewTypes not just specifically second argument?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error seems to be much more common than any other NewType-related errors, so I think it's worth having an error code for specifically this case.


# These error codes aren't enable by default.
NO_UNTYPED_DEF = ErrorCode(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am just curious why the below block is separated with an empty line. Maybe add a comment?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. These errors aren't enabled by default.

'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
2 changes: 1 addition & 1 deletion mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions mypy/message_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading