diff --git a/astroid/mixins.py b/astroid/mixins.py index 700df095e5..71a6edb912 100644 --- a/astroid/mixins.py +++ b/astroid/mixins.py @@ -30,16 +30,25 @@ class BlockRangeMixIn: def blockstart_tolineno(self): return self.lineno - def _elsed_block_range(self, lineno, orelse, last=None): + def _elsed_block_range( + self, lineno: int, orelse: list[nodes.NodeNG], last: int | None = None + ) -> tuple[int, int]: """handle block line numbers range for try/finally, for, if and while statements """ - if lineno == self.fromlineno: + # If at the end of the node, return same line + if lineno == self.tolineno: return lineno, lineno if orelse: - if lineno >= orelse[0].fromlineno: + # If the lineno is beyond the body of the node we check the orelse + if lineno >= self.body[-1].tolineno + 1: + # If the orelse has a scope of its own we determine the block range there + if isinstance(orelse[0], BlockRangeMixIn): + return orelse[0]._elsed_block_range(lineno, orelse[0].orelse) + # Return last line of orelse return lineno, orelse[-1].tolineno - return lineno, orelse[0].fromlineno - 1 + # If the lineno is within the body we take the last line of the body + return lineno, self.body[-1].tolineno return lineno, last or self.tolineno diff --git a/tests/unittest_nodes.py b/tests/unittest_nodes.py index a3e2e12935..741153c7c3 100644 --- a/tests/unittest_nodes.py +++ b/tests/unittest_nodes.py @@ -325,11 +325,22 @@ class IfNodeTest(_NodeTest): pass else: raise + + if 1: + print() + elif ( + 2 + and 3 + ): + print() + else: + # This is using else in a comment + raise """ def test_if_elif_else_node(self) -> None: """test transformation for If node""" - self.assertEqual(len(self.astroid.body), 4) + self.assertEqual(len(self.astroid.body), 5) for stmt in self.astroid.body: self.assertIsInstance(stmt, nodes.If) self.assertFalse(self.astroid.body[0].orelse) # simple If @@ -338,13 +349,49 @@ def test_if_elif_else_node(self) -> None: self.assertIsInstance(self.astroid.body[3].orelse[0].orelse[0], nodes.If) def test_block_range(self) -> None: + """Test block_range of various scope constructs""" # XXX ensure expected values - self.assertEqual(self.astroid.block_range(1), (0, 22)) - self.assertEqual(self.astroid.block_range(10), (0, 22)) # XXX (10, 22) ? + # Module + self.assertEqual(self.astroid.block_range(1), (0, 33)) + self.assertEqual(self.astroid.block_range(10), (0, 33)) # XXX (10, 33) ? + + # if + self.assertEqual(self.astroid.body[0].block_range(2), (2, 3)) + self.assertEqual(self.astroid.body[0].block_range(3), (3, 3)) + + # if ... else self.assertEqual(self.astroid.body[1].block_range(5), (5, 6)) self.assertEqual(self.astroid.body[1].block_range(6), (6, 6)) - self.assertEqual(self.astroid.body[1].orelse[0].block_range(7), (7, 8)) - self.assertEqual(self.astroid.body[1].orelse[0].block_range(8), (8, 8)) + self.assertEqual(self.astroid.body[1].block_range(7), (7, 8)) + self.assertEqual(self.astroid.body[1].block_range(8), (8, 8)) + + # if ... elif + self.assertEqual(self.astroid.body[2].block_range(10), (10, 11)) + self.assertEqual(self.astroid.body[2].block_range(11), (11, 11)) + self.assertEqual(self.astroid.body[2].block_range(12), (12, 13)) + self.assertEqual(self.astroid.body[2].block_range(13), (13, 13)) + + # if ... elif ... elif ... else + self.assertEqual(self.astroid.body[3].block_range(15), (15, 16)) + self.assertEqual(self.astroid.body[3].block_range(16), (16, 16)) + self.assertEqual(self.astroid.body[3].block_range(17), (17, 18)) + self.assertEqual(self.astroid.body[3].block_range(18), (18, 18)) + self.assertEqual(self.astroid.body[3].block_range(19), (19, 20)) + self.assertEqual(self.astroid.body[3].block_range(20), (20, 20)) + self.assertEqual(self.astroid.body[3].block_range(21), (21, 22)) + self.assertEqual(self.astroid.body[3].block_range(22), (22, 22)) + + # if ... elif ... else + self.assertEqual(self.astroid.body[4].block_range(24), (24, 25)) + self.assertEqual(self.astroid.body[4].block_range(25), (25, 25)) + self.assertEqual(self.astroid.body[4].block_range(26), (26, 30)) + self.assertEqual(self.astroid.body[4].block_range(27), (27, 30)) + self.assertEqual(self.astroid.body[4].block_range(28), (28, 30)) + self.assertEqual(self.astroid.body[4].block_range(29), (29, 30)) + self.assertEqual(self.astroid.body[4].block_range(30), (30, 30)) + self.assertEqual(self.astroid.body[4].block_range(31), (31, 33)) + self.assertEqual(self.astroid.body[4].block_range(32), (32, 33)) + self.assertEqual(self.astroid.body[4].block_range(33), (33, 33)) @staticmethod @pytest.mark.filterwarnings("ignore:.*is_sys_guard:DeprecationWarning")