Skip to content

Migrate core to use ErrorMessage class #12004

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5da91cb
Migrate fastparse to use ErrorMessage class
tushar-deepsource Jan 17, 2022
7eeefbe
Migrate typeanal.py
tushar-deepsource Jan 17, 2022
8020024
Fix regression
tushar-deepsource Jan 17, 2022
2110701
Fix lint issues
tushar-deepsource Jan 17, 2022
ecc552c
Migrate semanal
tushar-deepsource Jan 18, 2022
88f2626
Merge branch 'master' into fastparse-messages
tushar-deepsource Jan 18, 2022
3752a6d
Merge branch 'semanal-messages' into fastparse-messages
tushar-deepsource Jan 18, 2022
e3e2845
Fix porting mistakes
tushar-deepsource Jan 18, 2022
c5215ca
More porting fixes
tushar-deepsource Jan 18, 2022
e4ead79
Remove unnecessary changes
tushar-deepsource Jan 18, 2022
afd8f8d
Plugins still use the old call signature
tushar-deepsource Jan 18, 2022
a9d00e6
Formatting
tushar-deepsource Jan 18, 2022
bbaa519
Small fixes
tushar-deepsource Jan 18, 2022
ea4fe57
Another small fix
tushar-deepsource Jan 18, 2022
cf4a8bd
Fix failing tests
tushar-deepsource Jan 18, 2022
ab6c756
Fix weird failing test
tushar-deepsource Jan 18, 2022
a396e4d
Fix interface
tushar-deepsource Jan 18, 2022
a106d5c
Merge branch 'master' into fastparse-messages
tushar-deepsource Jan 19, 2022
0a42056
Add todos
tushar-deepsource Jan 20, 2022
e2b9059
Merge branch 'master' into fastparse-messages
tushar-deepsource Feb 7, 2022
0039d3d
Update mypy/semanal.py
tushar-deepsource Feb 7, 2022
07dadfe
Fix failing test
tushar-deepsource Feb 7, 2022
661b7e7
Merge branch 'master' into fastparse-messages
tushar-deepsource Feb 22, 2022
f2d0b3b
Remove duplicate line
tushar-deepsource Feb 22, 2022
0fa39a0
Merge branch 'master' into fastparse-messages
tushar-deepsource Apr 25, 2022
6ea2f65
Fix type errors
tushar-deepsource Apr 25, 2022
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
2 changes: 1 addition & 1 deletion mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ def analyze_class_attribute_access(itype: Instance,
if is_method:
mx.msg.cant_assign_to_method(mx.context)
if isinstance(node.node, TypeInfo):
mx.msg.fail(message_registry.CANNOT_ASSIGN_TO_TYPE, mx.context)
mx.msg.fail(message_registry.CANNOT_ASSIGN_TO_TYPE.value, mx.context)

# If a final attribute was declared on `self` in `__init__`, then it
# can't be accessed on the class object.
Expand Down
26 changes: 17 additions & 9 deletions mypy/errorcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,20 @@ def __str__(self) -> str:
return '<ErrorCode {}>'.format(self.code)


ATTR_DEFINED: Final = ErrorCode("attr-defined", "Check that attribute exists", "General")
NAME_DEFINED: Final = ErrorCode("name-defined", "Check that name is defined", "General")
ATTR_DEFINED: Final[ErrorCode] = ErrorCode(
"attr-defined", "Check that attribute exists", "General"
)
NAME_DEFINED: Final[ErrorCode] = ErrorCode("name-defined", "Check that name is defined", "General")
CALL_ARG: Final[ErrorCode] = ErrorCode(
"call-arg", "Check number, names and kinds of arguments in calls", "General"
)
ARG_TYPE: Final = ErrorCode("arg-type", "Check argument types in calls", "General")
CALL_OVERLOAD: Final = ErrorCode(
"call-overload", "Check that an overload variant matches arguments", "General"
)
VALID_TYPE: Final = ErrorCode("valid-type", "Check that type (annotation) is valid", "General")
VALID_TYPE: Final[ErrorCode] = ErrorCode(
"valid-type", "Check that type (annotation) is valid", "General"
)
VAR_ANNOTATED: Final = ErrorCode(
"var-annotated", "Require variable annotation if type can't be inferred", "General"
)
Expand All @@ -53,7 +57,9 @@ def __str__(self) -> str:
ASSIGNMENT: Final = ErrorCode(
"assignment", "Check that assigned value is compatible with target", "General"
)
TYPE_ARG: Final = ErrorCode("type-arg", "Check that generic type arguments are present", "General")
TYPE_ARG: Final[ErrorCode] = ErrorCode(
"type-arg", "Check that generic type arguments are present", "General"
)
TYPE_VAR: Final = ErrorCode("type-var", "Check that type variable values are valid", "General")
Copy link
Member

Choose a reason for hiding this comment

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

Why some consts are : Final = ErrorCode() and some are : Final[ErrorCode] = ErrorCode()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

mypyc bug. It doesn't recognize just Final for some reason, it has to be told. The bug only shows up when that error code is used inside the ErrorMessage tuple, so to keep a minimal diff I'm only migrating the error codes that have been used in these PRs.

Eventually as I make more PRs they will all become Final[ErrorCode]

UNION_ATTR: Final = ErrorCode(
"union-attr", "Check that attribute exists in each item of a union", "General"
Expand All @@ -75,14 +81,16 @@ def __str__(self) -> str:
IMPORT: Final = ErrorCode(
"import", "Require that imported module can be found or has stubs", "General"
)
NO_REDEF: Final = ErrorCode("no-redef", "Check that each name is defined once", "General")
NO_REDEF: Final[ErrorCode] = ErrorCode(
"no-redef", "Check that each name is defined once", "General"
)
FUNC_RETURNS_VALUE: Final = ErrorCode(
"func-returns-value", "Check that called function returns a value in value context", "General"
)
ABSTRACT: Final = ErrorCode(
"abstract", "Prevent instantiation of classes with abstract attributes", "General"
)
VALID_NEWTYPE: Final = ErrorCode(
VALID_NEWTYPE: Final[ErrorCode] = ErrorCode(
"valid-newtype", "Check that argument 2 to NewType is valid", "General"
)
STRING_FORMATTING: Final = ErrorCode(
Expand Down Expand Up @@ -139,10 +147,10 @@ def __str__(self) -> str:
"General",
default_enabled=False,
)
NAME_MATCH: Final = ErrorCode(
NAME_MATCH: Final[ErrorCode] = ErrorCode(
"name-match", "Check that type definition has consistent naming", "General"
)
NO_OVERLOAD_IMPL: Final = ErrorCode(
NO_OVERLOAD_IMPL: Final[ErrorCode] = ErrorCode(
"no-overload-impl",
"Check that overloaded functions outside stub files have an implementation",
"General",
Expand All @@ -162,7 +170,7 @@ def __str__(self) -> str:


# Syntax errors are often blocking.
SYNTAX: Final = ErrorCode("syntax", "Report syntax errors", "General")
SYNTAX: Final[ErrorCode] = ErrorCode("syntax", "Report syntax errors", "General")

# This is an internal marker code for a whole-file ignore. It is not intended to
# be user-visible.
Expand Down
48 changes: 22 additions & 26 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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 infer_reachability_of_if_statement, mark_block_unreachable
Expand Down Expand Up @@ -156,10 +157,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: Final = re.compile(r'[^#]*#\s*type:\s*ignore\s*(.*)')


Expand Down Expand Up @@ -250,8 +247,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
Expand All @@ -263,7 +260,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
Copy link
Member

Choose a reason for hiding this comment

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

can we refactor report to also work with ErroCode instances?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is doable, but from what I've seen that's not necessary.

I could do that in a separate PR if it seems like a good to have.

errors.report(line, column, err_msg.value, code=err_msg.code)
else:
raise SyntaxError
else:
Expand Down Expand Up @@ -341,21 +339,20 @@ 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,
code: codes.ErrorCode = codes.SYNTAX) -> None:
if blocker or not self.options.ignore_errors:
self.errors.report(line, column, msg, blocker=blocker, code=code)
self.errors.report(line, column, msg.value, blocker=blocker, code=msg.code)

def fail_merge_overload(self, node: IfStmt) -> None:
self.fail(
"Condition can't be inferred, unable to merge overloads",
message_registry.FAILED_TO_MERGE_OVERLOADS,
line=node.line,
column=node.column,
blocker=False,
code=codes.MISC,
)

def visit(self, node: Optional[AST]) -> Any:
Expand Down Expand Up @@ -760,7 +757,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,
Expand Down Expand Up @@ -833,7 +830,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',
Expand All @@ -852,13 +849,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
Expand Down Expand Up @@ -986,7 +982,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,
Expand Down Expand Up @@ -1687,9 +1683,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:
Expand Down Expand Up @@ -1720,7 +1716,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)
Expand All @@ -1733,25 +1729,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)

Expand All @@ -1763,7 +1759,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

Expand Down
24 changes: 12 additions & 12 deletions mypy/fastparse2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, parse_type_ignore_tag,
TYPE_IGNORE_PATTERN, INVALID_TYPE_IGNORE
TYPE_IGNORE_PATTERN
)
from mypy.options import Options
from mypy.util import bytes_to_human_readable_repr
Expand Down Expand Up @@ -176,9 +177,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:
Expand Down Expand Up @@ -353,7 +354,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,
Expand Down Expand Up @@ -408,7 +409,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)
Expand All @@ -423,13 +424,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)
Expand Down Expand Up @@ -526,7 +526,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)

Expand Down Expand Up @@ -568,7 +568,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
Expand Down Expand Up @@ -709,7 +709,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 <expr>, <anything but a name>" is not supported',
self.fail(message_registry.EXCEPT_EXPR_NOTNAME_UNSUPPORTED,
item.lineno, item.col_offset)
vs.append(None)
types = [self.visit(h.type) for h in handlers]
Expand Down
Loading