From 10c391bdbb39209210e00a0d174754e8322be056 Mon Sep 17 00:00:00 2001 From: Emilio Graff <1@emil.io> Date: Sat, 6 Jan 2024 12:44:00 -0800 Subject: [PATCH 1/3] Compute `end_line` and `end_column` for `FuncDef` so they don't include the body --- mypy/fastparse.py | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 13b9b5c8a871e..5179573fb7031 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -985,9 +985,45 @@ def do_func_def( _dummy_fallback, ) - # End position is always the same. - end_line = getattr(n, "end_lineno", None) - end_column = getattr(n, "end_col_offset", None) + # We don't want the end positions for functions to include the entire + # body, which is what `ast` gives us; we want to highlight only the + # function signature as the error / note. + + # First, check for return typehint + ret = n.returns + if ret is not None: + # There is a return typehint; highlight to the end of it. + end_line = ret.end_lineno + end_column = ret.end_col_offset + elif len(n.body) > 0: + # There's no return type, but there is a body (which includes + # docstrings). + def_line = n.lineno + body_line = n.body[0].lineno + if def_line == body_line: + # Single line definition, e.g. `def foo(): pass` + # NOTE: this will not highlight the colon in the nonstandard + # case of `def foo():pass` + end_line = def_line + end_column = n.body[0].col_offset - 1 + else: + # "Proper" body starting on a different line after `:` + # We highlight up to the line in which the body starts + # but at column 0, effectively ending at the end of the + # previous line. + # NOTE: this causes funny highlighting in the non-standard + # case below: + # + # def foo(x: int + # ): pass + # + # The error will show from `d` of `def to `t` of `int`. + end_line = n.body[0].lineno + end_column = 0 + else: + # Fall back to whole function. + end_line = getattr(n, "end_lineno", None) + end_column = getattr(n, "end_col_offset", None) self.class_and_function_stack.pop() self.class_and_function_stack.append("F") From 618440ab1cbf2cde4ce393b468a8ea098f13a11a Mon Sep 17 00:00:00 2001 From: Emilio Graff <1@emil.io> Date: Sat, 6 Jan 2024 12:45:41 -0800 Subject: [PATCH 2/3] Report end line and column in `SemanticAnalyzer` --- mypy/semanal.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index e0a3db2bff1b1..f2d956065e0ad 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -6520,12 +6520,28 @@ def fail( return # In case it's a bug and we don't really have context assert ctx is not None, msg - self.errors.report(ctx.line, ctx.column, msg, blocker=blocker, code=code) + self.errors.report( + ctx.line, + ctx.column, + msg, + blocker=blocker, + end_line=ctx.end_line, + end_column=ctx.end_column, + code=code, + ) def note(self, msg: str, ctx: Context, code: ErrorCode | None = None) -> None: if not self.in_checked_function(): return - self.errors.report(ctx.line, ctx.column, msg, severity="note", code=code) + self.errors.report( + ctx.line, + ctx.column, + msg, + severity="note", + end_line=ctx.end_line, + end_column=ctx.end_column, + code=code, + ) def incomplete_feature_enabled(self, feature: str, ctx: Context) -> bool: if feature not in self.options.enable_incomplete_feature: From 070becb1a2ea9c637ba2542a94957a124c97e300 Mon Sep 17 00:00:00 2001 From: Emilio Graff <1@emil.io> Date: Sat, 6 Jan 2024 13:58:56 -0800 Subject: [PATCH 3/3] Revert "Compute `end_line` and `end_column` for `FuncDef` so they don't include the body" This reverts commit 10c391bdbb39209210e00a0d174754e8322be056. We cannot do this globally as it breaks a lot of stuff. --- mypy/fastparse.py | 42 +++--------------------------------------- 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 5179573fb7031..13b9b5c8a871e 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -985,45 +985,9 @@ def do_func_def( _dummy_fallback, ) - # We don't want the end positions for functions to include the entire - # body, which is what `ast` gives us; we want to highlight only the - # function signature as the error / note. - - # First, check for return typehint - ret = n.returns - if ret is not None: - # There is a return typehint; highlight to the end of it. - end_line = ret.end_lineno - end_column = ret.end_col_offset - elif len(n.body) > 0: - # There's no return type, but there is a body (which includes - # docstrings). - def_line = n.lineno - body_line = n.body[0].lineno - if def_line == body_line: - # Single line definition, e.g. `def foo(): pass` - # NOTE: this will not highlight the colon in the nonstandard - # case of `def foo():pass` - end_line = def_line - end_column = n.body[0].col_offset - 1 - else: - # "Proper" body starting on a different line after `:` - # We highlight up to the line in which the body starts - # but at column 0, effectively ending at the end of the - # previous line. - # NOTE: this causes funny highlighting in the non-standard - # case below: - # - # def foo(x: int - # ): pass - # - # The error will show from `d` of `def to `t` of `int`. - end_line = n.body[0].lineno - end_column = 0 - else: - # Fall back to whole function. - end_line = getattr(n, "end_lineno", None) - end_column = getattr(n, "end_col_offset", None) + # End position is always the same. + end_line = getattr(n, "end_lineno", None) + end_column = getattr(n, "end_col_offset", None) self.class_and_function_stack.pop() self.class_and_function_stack.append("F")