From ee70d41881600b67c3d98d41c060b81a22a45210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Mon, 22 Nov 2021 22:40:26 +0100 Subject: [PATCH] Add ``end_lineno`` and ``end_col_offset`` to ``MessageLocationTuple`` (#5343) --- pylint/checkers/base_checker.py | 6 +++- pylint/lint/pylinter.py | 44 +++++++++++++++++++++++++---- pylint/message/message.py | 8 +++++- pylint/testutils/unittest_linter.py | 4 +++ pylint/typing.py | 2 ++ tests/lint/unittest_lint.py | 20 +++++++++++-- tests/message/unittest_message.py | 4 +++ tests/testutils/test_output_line.py | 7 +---- 8 files changed, 79 insertions(+), 16 deletions(-) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index 328adcb4d2..269979daf3 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -116,8 +116,12 @@ def add_message( args: Any = None, confidence: Optional[Confidence] = None, col_offset: Optional[int] = None, + end_lineno: Optional[int] = None, + end_col_offset: Optional[int] = None, ) -> None: - self.linter.add_message(msgid, line, node, args, confidence, col_offset) + self.linter.add_message( + msgid, line, node, args, confidence, col_offset, end_lineno, end_col_offset + ) def check_consistency(self): """Check the consistency of msgid. diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py index 913bc4a22d..53453af404 100644 --- a/pylint/lint/pylinter.py +++ b/pylint/lint/pylinter.py @@ -1413,14 +1413,30 @@ def _add_one_message( args: Optional[Any], confidence: Optional[interfaces.Confidence], col_offset: Optional[int], + end_lineno: Optional[int], + end_col_offset: Optional[int], ) -> None: """After various checks have passed a single Message is passed to the reporter and added to stats""" message_definition.check_message_definition(line, node) - if line is None and node is not None: - line = node.fromlineno - if col_offset is None and hasattr(node, "col_offset"): - col_offset = node.col_offset # type: ignore[union-attr] + + # Look up "location" data of node if not yet supplied + if node: + if not line: + line = node.fromlineno + # pylint: disable=fixme + # TODO: Initialize col_offset on every node (can be None) -> astroid + if not col_offset and hasattr(node, "col_offset"): + col_offset = node.col_offset + # pylint: disable=fixme + # TODO: Initialize end_lineno on every node (can be None) -> astroid + # See https://github.com/PyCQA/astroid/issues/1273 + if not end_lineno and hasattr(node, "end_lineno"): + end_lineno = node.end_lineno + # pylint: disable=fixme + # TODO: Initialize end_col_offset on every node (can be None) -> astroid + if not end_col_offset and hasattr(node, "end_col_offset"): + end_col_offset = node.end_col_offset # should this message be displayed if not self.is_message_enabled(message_definition.msgid, line, confidence): @@ -1463,7 +1479,14 @@ def _add_one_message( message_definition.msgid, message_definition.symbol, MessageLocationTuple( - abspath, path, module or "", obj, line or 1, col_offset or 0 + abspath, + path, + module or "", + obj, + line or 1, + col_offset or 0, + end_lineno, + end_col_offset, ), msg, confidence, @@ -1478,6 +1501,8 @@ def add_message( args: Optional[Any] = None, confidence: Optional[interfaces.Confidence] = None, col_offset: Optional[int] = None, + end_lineno: Optional[int] = None, + end_col_offset: Optional[int] = None, ) -> None: """Adds a message given by ID or name. @@ -1492,7 +1517,14 @@ def add_message( message_definitions = self.msgs_store.get_message_definitions(msgid) for message_definition in message_definitions: self._add_one_message( - message_definition, line, node, args, confidence, col_offset + message_definition, + line, + node, + args, + confidence, + col_offset, + end_lineno, + end_col_offset, ) def add_ignored_message( diff --git a/pylint/message/message.py b/pylint/message/message.py index 178079223f..b431d502b7 100644 --- a/pylint/message/message.py +++ b/pylint/message/message.py @@ -25,6 +25,8 @@ "obj", "line", "column", + "end_line", + "end_column", ], ) @@ -59,7 +61,10 @@ def __new__( cls, msg_id: str, symbol: str, - location: Union[Tuple[str, str, str, str, int, int], MessageLocationTuple], + location: Union[ + Tuple[str, str, str, str, int, int], + MessageLocationTuple, + ], msg: str, confidence: Optional[Confidence], ) -> "Message": @@ -68,6 +73,7 @@ def __new__( "In pylint 3.0, Messages will only accept a MessageLocationTuple as location parameter", DeprecationWarning, ) + location = location + (None, None) # type: ignore[assignment] # Temporary fix until deprecation return _MsgBase.__new__( cls, msg_id, diff --git a/pylint/testutils/unittest_linter.py b/pylint/testutils/unittest_linter.py index 2d9e42e657..b8fbb8c91a 100644 --- a/pylint/testutils/unittest_linter.py +++ b/pylint/testutils/unittest_linter.py @@ -34,8 +34,12 @@ def add_message( args: Any = None, confidence: Optional[Confidence] = None, col_offset: Optional[int] = None, + end_lineno: Optional[int] = None, + end_col_offset: Optional[int] = None, ) -> None: # Do not test col_offset for now since changing Message breaks everything + # pylint: disable=fixme + # TODO: Test end_lineno and end_col_offset :) self._messages.append(MessageTest(msg_id, line, node, args, confidence)) @staticmethod diff --git a/pylint/typing.py b/pylint/typing.py index 7ea72cff3f..5f1524180c 100644 --- a/pylint/typing.py +++ b/pylint/typing.py @@ -52,6 +52,8 @@ class MessageLocationTuple(NamedTuple): obj: str line: int column: int + end_line: Optional[int] = None + end_column: Optional[int] = None class ManagedMessage(NamedTuple): diff --git a/tests/lint/unittest_lint.py b/tests/lint/unittest_lint.py index a167b03fd8..865d7640b7 100644 --- a/tests/lint/unittest_lint.py +++ b/tests/lint/unittest_lint.py @@ -500,7 +500,14 @@ def test_addmessage(linter: PyLinter) -> None: description="Warning without any associated confidence level.", ), location=MessageLocationTuple( - abspath="0123", path="0123", module="0123", obj="", line=1, column=0 + abspath="0123", + path="0123", + module="0123", + obj="", + line=1, + column=0, + end_line=None, + end_column=None, ), ) assert linter.reporter.messages[1] == Message( @@ -512,7 +519,14 @@ def test_addmessage(linter: PyLinter) -> None: description="Warning without any associated confidence level.", ), location=MessageLocationTuple( - abspath="0123", path="0123", module="0123", obj="", line=2, column=0 + abspath="0123", + path="0123", + module="0123", + obj="", + line=2, + column=0, + end_line=None, + end_column=None, ), ) @@ -616,6 +630,8 @@ def test_analyze_explicit_script(linter: PyLinter) -> None: obj="", line=2, column=0, + end_line=None, + end_column=None, ), ) diff --git a/tests/message/unittest_message.py b/tests/message/unittest_message.py index 3214531502..64cadf91a8 100644 --- a/tests/message/unittest_message.py +++ b/tests/message/unittest_message.py @@ -34,6 +34,8 @@ def build_message( obj="4", line=5, column=6, + end_line=5, + end_column=9, ) w1234_location_values = MessageLocationTuple( abspath="7", @@ -42,6 +44,8 @@ def build_message( obj="10", line=11, column=12, + end_line=11, + end_column=14, ) expected = ( "2:5:6: E1234: Duplicate keyword argument %r in %s call (duplicate-keyword-arg)" diff --git a/tests/testutils/test_output_line.py b/tests/testutils/test_output_line.py index d13db49781..9280835082 100644 --- a/tests/testutils/test_output_line.py +++ b/tests/testutils/test_output_line.py @@ -21,12 +21,7 @@ def inner(confidence: Confidence = HIGH) -> Message: symbol="missing-docstring", msg_id="C0123", location=MessageLocationTuple( - "abspath", - "path", - "module", - "obj", - 1, - 2, + "abspath", "path", "module", "obj", 1, 2, 1, 3 ), msg="msg", confidence=confidence,