Skip to content

Commit

Permalink
Changed NodeNG.tolineno to use end_lineno when it is available (#1351)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
  • Loading branch information
superbobry and DanielNoord authored Feb 27, 2022
1 parent 0acb961 commit b6d1710
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 10 deletions.
5 changes: 5 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ Release date: TBA

Closes #1330

* Use the ``end_lineno`` attribute for the ``NodeNG.tolineno`` property
when it is available.

Closes #1350

* Add ``is_dataclass`` attribute to ``ClassDef`` nodes.

* Use ``sysconfig`` instead of ``distutils`` to determine the location of
Expand Down
2 changes: 2 additions & 0 deletions astroid/nodes/node_ng.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,8 @@ def fromlineno(self) -> Optional[int]:
@decorators.cachedproperty
def tolineno(self) -> Optional[int]:
"""The last line that this node appears on in the source code."""
if self.end_lineno is not None:
return self.end_lineno
if not self._astroid_fields:
# can't have children
last_child = None
Expand Down
14 changes: 12 additions & 2 deletions astroid/rebuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2197,11 +2197,21 @@ def visit_starred(self, node: "ast.Starred", parent: NodeNG) -> nodes.Starred:
def visit_tryexcept(self, node: "ast.Try", parent: NodeNG) -> nodes.TryExcept:
"""visit a TryExcept node by returning a fresh instance of it"""
if sys.version_info >= (3, 8):
# TryExcept excludes the 'finally' but that will be included in the
# end_lineno from 'node'. Therefore, we check all non 'finally'
# children to find the correct end_lineno and column.
end_lineno = node.end_lineno
end_col_offset = node.end_col_offset
all_children: List["ast.AST"] = [*node.body, *node.handlers, *node.orelse]
for child in reversed(all_children):
end_lineno = child.end_lineno
end_col_offset = child.end_col_offset
break
newnode = nodes.TryExcept(
lineno=node.lineno,
col_offset=node.col_offset,
end_lineno=node.end_lineno,
end_col_offset=node.end_col_offset,
end_lineno=end_lineno,
end_col_offset=end_col_offset,
parent=parent,
)
else:
Expand Down
11 changes: 7 additions & 4 deletions tests/unittest_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,14 @@ def test_callfunc_lineno(self) -> None:
strarg = callfunc.args[0]
self.assertIsInstance(strarg, nodes.Const)
if hasattr(sys, "pypy_version_info"):
lineno = 4
self.assertEqual(strarg.fromlineno, 4)
self.assertEqual(strarg.tolineno, 4)
else:
lineno = 5 if not PY38_PLUS else 4
self.assertEqual(strarg.fromlineno, lineno)
self.assertEqual(strarg.tolineno, lineno)
if not PY38_PLUS:
self.assertEqual(strarg.fromlineno, 5)
else:
self.assertEqual(strarg.fromlineno, 4)
self.assertEqual(strarg.tolineno, 5)
namearg = callfunc.args[1]
self.assertIsInstance(namearg, nodes.Name)
self.assertEqual(namearg.fromlineno, 5)
Expand Down
2 changes: 1 addition & 1 deletion tests/unittest_nodes_lineno.py
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ def test_end_lineno_try() -> None:
assert (t3.lineno, t3.col_offset) == (10, 0)
assert (t3.end_lineno, t3.end_col_offset) == (17, 8)
assert (t3.body[0].lineno, t3.body[0].col_offset) == (10, 0)
assert (t3.body[0].end_lineno, t3.body[0].end_col_offset) == (17, 8)
assert (t3.body[0].end_lineno, t3.body[0].end_col_offset) == (15, 8)
assert (t3.finalbody[0].lineno, t3.finalbody[0].col_offset) == (17, 4)
assert (t3.finalbody[0].end_lineno, t3.finalbody[0].end_col_offset) == (17, 8)

Expand Down
10 changes: 7 additions & 3 deletions tests/unittest_scoped_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1092,15 +1092,19 @@ def g1(x):
print(x)
@f(a=2,
b=3)
b=3,
)
def g2():
pass
"""
astroid = builder.parse(data)
self.assertEqual(astroid["g1"].fromlineno, 4)
self.assertEqual(astroid["g1"].tolineno, 5)
self.assertEqual(astroid["g2"].fromlineno, 9)
self.assertEqual(astroid["g2"].tolineno, 10)
if not PY38_PLUS:
self.assertEqual(astroid["g2"].fromlineno, 9)
else:
self.assertEqual(astroid["g2"].fromlineno, 10)
self.assertEqual(astroid["g2"].tolineno, 11)

def test_metaclass_error(self) -> None:
astroid = builder.parse(
Expand Down

0 comments on commit b6d1710

Please sign in to comment.