Skip to content

Migrate fastparse to use ErrorMessage class #14753

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

Merged
merged 6 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/errorcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,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
74 changes: 31 additions & 43 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from mypy import defaults, errorcodes as codes, message_registry
from mypy.errors import Errors
from mypy.message_registry import ErrorMessage
from mypy.nodes import (
ARG_NAMED,
ARG_NAMED_OPT,
Expand Down Expand Up @@ -241,10 +242,6 @@ def ast3_parse(
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 @@ -343,8 +340,8 @@ def parse_type_comment(
except SyntaxError:
if errors is not None:
stripped_type = type_comment.split("#", 2)[0].strip()
err_msg = f'{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 @@ -355,7 +352,9 @@ def parse_type_comment(
ignored: list[str] | None = parse_type_ignore_tag(tag)
if ignored is None:
if errors is not None:
errors.report(line, column, INVALID_TYPE_IGNORE, code=codes.SYNTAX)
errors.report(
line, column, message_registry.INVALID_TYPE_IGNORE.value, code=codes.SYNTAX
)
else:
raise SyntaxError
else:
Expand Down Expand Up @@ -418,24 +417,16 @@ def __init__(self, options: Options, is_stub: bool, errors: Errors) -> None:
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,
line: int,
column: int,
blocker: bool = True,
code: codes.ErrorCode = codes.SYNTAX,
) -> 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=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: AST | None) -> Any:
Expand Down Expand Up @@ -490,10 +481,7 @@ def translate_stmt_list(
if ignores:
joined_ignores = ", ".join(ignores)
self.fail(
(
"type ignore with error code is not supported for modules; "
f'use `# mypy: disable-error-code="{joined_ignores}"`'
),
message_registry.TYPE_IGNORE_WITH_ERRCODE_ON_MODULE.format(joined_ignores),
line=min(self.type_ignores),
column=0,
blocker=False,
Expand Down Expand Up @@ -848,7 +836,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, blocker=False)
self.fail(message_registry.INVALID_TYPE_IGNORE, ti.lineno, -1, blocker=False)
body = self.fix_function_overloads(self.translate_stmt_list(mod.body, ismodule=True))
return MypyFile(body, self.imports, False, self.type_ignores)

Expand Down Expand Up @@ -920,7 +908,7 @@ def do_func_def(
arg_types.insert(0, AnyType(TypeOfAny.special_form))
except SyntaxError:
stripped_type = n.type_comment.split("#", 2)[0].strip()
err_msg = f'{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(
Expand All @@ -940,18 +928,20 @@ def do_func_def(
func_type = None
if any(arg_types) or return_type:
if len(arg_types) != 1 and any(isinstance(t, EllipsisType) for t in arg_types):
self.fail(message_registry.ELLIPSIS_WITH_OTHER_TYPEARGS, lineno, n.col_offset)
elif len(arg_types) > len(arg_kinds):
self.fail(
"Ellipses cannot accompany other argument types in function type signature",
message_registry.TYPE_SIGNATURE_TOO_MANY_ARGS,
lineno,
n.col_offset,
)
elif len(arg_types) > len(arg_kinds):
self.fail(
"Type signature has too many arguments", lineno, n.col_offset, blocker=False
blocker=False,
)
elif len(arg_types) < len(arg_kinds):
self.fail(
"Type signature has too few arguments", lineno, n.col_offset, blocker=False
message_registry.TYPE_SIGNATURE_TOO_FEW_ARGS,
lineno,
n.col_offset,
blocker=False,
)
else:
func_type = CallableType(
Expand Down Expand Up @@ -1094,7 +1084,7 @@ def make_argument(
return argument

def fail_arg(self, msg: str, arg: ast3.arg) -> None:
self.fail(msg, arg.lineno, arg.col_offset)
self.fail(ErrorMessage(msg), arg.lineno, arg.col_offset)

# ClassDef(identifier name,
# expr* bases,
Expand Down Expand Up @@ -1819,9 +1809,9 @@ def parent(self) -> AST | None:
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 All @@ -1841,7 +1831,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: str | None = None
default_type = AnyType(TypeOfAny.special_form)
Expand All @@ -1854,25 +1844,21 @@ 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", f.lineno, f.col_offset)
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(
constructor
),
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(
constructor
),
message_registry.MULTIPLE_VALUES_FOR_TYPE_KWARG.format(constructor),
f.lineno,
f.col_offset,
)
Expand All @@ -1881,7 +1867,7 @@ def visit_Call(self, e: Call) -> Type:
typ = converted
else:
self.fail(
f'Unexpected argument "{k.arg}" for argument constructor',
message_registry.ARG_CONSTRUCTOR_UNEXPECTED_ARG.format(k.arg),
value.lineno,
value.col_offset,
)
Expand All @@ -1896,7 +1882,9 @@ def _extract_argument_name(self, n: ast3.expr) -> str | None:
elif isinstance(n, NameConstant) and str(n.value) == "None":
return None
self.fail(
f"Expected string literal for argument name, got {type(n).__name__}", self.line, 0
message_registry.ARG_NAME_EXPECTED_STRING_LITERAL.format(type(n).__name__),
self.line,
0,
)
return None

Expand Down
41 changes: 40 additions & 1 deletion mypy/message_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
"Expected TypedDict key to be string literal"
)
MALFORMED_ASSERT: Final = ErrorMessage("Assertion is always true, perhaps remove parentheses?")
DUPLICATE_TYPE_SIGNATURES: Final = "Function has duplicate type signatures"
DUPLICATE_TYPE_SIGNATURES: Final = ErrorMessage("Function has duplicate type signatures")
DESCRIPTOR_SET_NOT_CALLABLE: Final = ErrorMessage("{}.__set__ is not callable")
DESCRIPTOR_GET_NOT_CALLABLE: Final = "{}.__get__ is not callable"
MODULE_LEVEL_GETATTRIBUTE: Final = ErrorMessage(
Expand Down Expand Up @@ -274,3 +274,42 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
DATACLASS_FIELD_ALIAS_MUST_BE_LITERAL: Final = (
'"alias" argument to dataclass field must be a string literal'
)

# fastparse
FAILED_TO_MERGE_OVERLOADS: Final = ErrorMessage(
"Condition can't be inferred, unable to merge overloads"
)
TYPE_IGNORE_WITH_ERRCODE_ON_MODULE: Final = ErrorMessage(
"type ignore with error code is not supported for modules; "
'use `# mypy: disable-error-code="{}"`',
codes.SYNTAX,
)
INVALID_TYPE_IGNORE: Final = ErrorMessage('Invalid "type: ignore" comment', codes.SYNTAX)
TYPE_COMMENT_SYNTAX_ERROR_VALUE: Final = ErrorMessage(
'syntax error in type 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
)