From 14acfd3d02a3173d5e852cd1f51212a25381f6ff Mon Sep 17 00:00:00 2001 From: Tushar Sadhwani Date: Wed, 22 Feb 2023 00:50:05 +0530 Subject: [PATCH 1/4] Migrate fastparse to use ErrorMessage class --- mypy/fastparse.py | 72 +++++++++++++++++----------------------- mypy/message_registry.py | 42 ++++++++++++++++++++++- 2 files changed, 72 insertions(+), 42 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index ef1fdf61af2e..7b0318541bfc 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -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, @@ -243,8 +244,6 @@ def ast3_parse( 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*(.*)") @@ -343,8 +342,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 @@ -356,7 +355,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: @@ -419,24 +420,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: @@ -489,10 +482,7 @@ def translate_stmt_list( ): if self.type_ignores[min(self.type_ignores)]: self.fail( - ( - "type ignore with error code is not supported for modules; " - "use `# mypy: disable-error-code=...`" - ), + message_registry.TYPE_IGNORE_WITH_ERRCODE_ON_MODULE, line=min(self.type_ignores), column=0, blocker=False, @@ -845,7 +835,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) @@ -917,7 +907,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( @@ -937,18 +927,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( @@ -1091,7 +1083,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, @@ -1816,9 +1808,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: @@ -1838,7 +1830,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) @@ -1851,15 +1843,13 @@ 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, ) @@ -1867,9 +1857,7 @@ def visit_Call(self, e: Call) -> Type: 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, ) @@ -1878,7 +1866,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, ) @@ -1893,7 +1881,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 diff --git a/mypy/message_registry.py b/mypy/message_registry.py index e00aca2869bd..542fa29eef11 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -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( @@ -274,3 +274,43 @@ 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 +) From f4b8d1057b628d9fdfe548ec6d7195c66cc8a630 Mon Sep 17 00:00:00 2001 From: Tushar Sadhwani Date: Wed, 22 Feb 2023 00:51:31 +0530 Subject: [PATCH 2/4] remove unused global --- mypy/fastparse.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 7b0318541bfc..fe8e43fe8c7b 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -242,8 +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" - TYPE_IGNORE_PATTERN: Final = re.compile(r"[^#]*#\s*type:\s*ignore\s*(.*)") From 7597702f43640c7217f51c23f3943b61151c5c42 Mon Sep 17 00:00:00 2001 From: Tushar Sadhwani Date: Wed, 22 Feb 2023 00:56:31 +0530 Subject: [PATCH 3/4] Fix mypyc inference --- mypy/errorcodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 3d8b1096ed4f..91ac358f29fb 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -221,7 +221,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. From fcfbc4a65e55a15b5265c7043c5c7633daf8b78c Mon Sep 17 00:00:00 2001 From: Tushar Sadhwani Date: Wed, 22 Feb 2023 00:57:32 +0530 Subject: [PATCH 4/4] Remove unnecessary comment --- mypy/message_registry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 542fa29eef11..3832a7bf6a38 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -291,7 +291,6 @@ def with_additional_msg(self, info: str) -> ErrorMessage: 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 )