Skip to content

Commit eb1ba3b

Browse files
committed
Add orelse_lineno and orelse_col_offset to nodes.If
1 parent f181d7b commit eb1ba3b

File tree

4 files changed

+46
-1
lines changed

4 files changed

+46
-1
lines changed

ChangeLog

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ What's New in astroid 2.12.0?
66
=============================
77
Release date: TBA
88

9-
9+
* Add ``orelse_lineno`` and ``orelse_col_offset`` attributes to ``nodes.If``.
1010

1111
What's New in astroid 2.11.1?
1212
=============================

astroid/nodes/node_classes.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3034,6 +3034,12 @@ def __init__(
30343034
self.is_orelse: bool = False
30353035
"""Whether the if-statement is the orelse-block of another if statement."""
30363036

3037+
self.orelse_lineno: Optional[int] = None
3038+
"""The line number of the ``else`` keyword."""
3039+
3040+
self.orelse_col_offset: Optional[int] = None
3041+
"""The column offset of the ``else`` keyword."""
3042+
30373043
super().__init__(
30383044
lineno=lineno,
30393045
col_offset=col_offset,
@@ -3047,6 +3053,9 @@ def postinit(
30473053
test: Optional[NodeNG] = None,
30483054
body: Optional[typing.List[NodeNG]] = None,
30493055
orelse: Optional[typing.List[NodeNG]] = None,
3056+
*,
3057+
orelse_lineno: Optional[int] = None,
3058+
orelse_col_offset: Optional[int] = None,
30503059
) -> None:
30513060
"""Do some setup after initialisation.
30523061
@@ -3055,6 +3064,10 @@ def postinit(
30553064
:param body: The contents of the block.
30563065
30573066
:param orelse: The contents of the ``else`` block.
3067+
3068+
:param orelse_lineno: The line number of the ``else`` keyword.
3069+
3070+
:param orelse_lineno: The column offset of the ``else`` keyword.
30583071
"""
30593072
self.test = test
30603073
if body is not None:
@@ -3063,6 +3076,8 @@ def postinit(
30633076
self.orelse = orelse
30643077
if isinstance(self.parent, If) and self in self.parent.orelse:
30653078
self.is_orelse = True
3079+
self.orelse_lineno = orelse_lineno
3080+
self.orelse_col_offset = orelse_col_offset
30663081

30673082
@cached_property
30683083
def blockstart_tolineno(self):

astroid/rebuilder.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,20 @@ def visit_global(self, node: "ast.Global", parent: NodeNG) -> nodes.Global:
13821382
self._global_names[-1].setdefault(name, []).append(newnode)
13831383
return newnode
13841384

1385+
def _find_else_keyword(self, node: "ast.If") -> Tuple[Optional[int], Optional[int]]:
1386+
"""Get the line number and column offset of the `else` keyword."""
1387+
if not self._data or not node.orelse:
1388+
return None, None
1389+
1390+
end_lineno = node.orelse[0].lineno - 1
1391+
1392+
# pylint: disable-next=unsubscriptable-object
1393+
data = "\n".join(self._data[node.lineno - 1 : end_lineno])
1394+
for t in generate_tokens(StringIO(data).readline):
1395+
if t.type == token.NAME and t.string == "else":
1396+
return node.lineno + t.start[0] - 1, t.start[1]
1397+
return None, None
1398+
13851399
def visit_if(self, node: "ast.If", parent: NodeNG) -> nodes.If:
13861400
"""visit an If node by returning a fresh instance of it"""
13871401
newnode = nodes.If(
@@ -1392,10 +1406,15 @@ def visit_if(self, node: "ast.If", parent: NodeNG) -> nodes.If:
13921406
end_col_offset=getattr(node, "end_col_offset", None),
13931407
parent=parent,
13941408
)
1409+
1410+
orelse_lineno, orelse_col_offset = self._find_else_keyword(node)
1411+
13951412
newnode.postinit(
13961413
self.visit(node.test, newnode),
13971414
[self.visit(child, newnode) for child in node.body],
13981415
[self.visit(child, newnode) for child in node.orelse],
1416+
orelse_lineno=orelse_lineno,
1417+
orelse_col_offset=orelse_col_offset,
13991418
)
14001419
return newnode
14011420

tests/unittest_nodes.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,17 @@ def test_block_range(self) -> None:
343343
self.assertEqual(self.astroid.body[1].orelse[0].block_range(7), (7, 8))
344344
self.assertEqual(self.astroid.body[1].orelse[0].block_range(8), (8, 8))
345345

346+
def test_orelse_line_numbering(self) -> None:
347+
"""Test the position info for the `else` keyword."""
348+
assert self.astroid.body[0].orelse_lineno is None
349+
assert self.astroid.body[0].orelse_col_offset is None
350+
assert self.astroid.body[1].orelse_lineno == 7
351+
assert self.astroid.body[1].orelse_col_offset == 0
352+
assert self.astroid.body[2].orelse_lineno is None
353+
assert self.astroid.body[2].orelse_col_offset is None
354+
assert self.astroid.body[3].orelse[0].orelse[0].orelse_lineno == 21
355+
assert self.astroid.body[3].orelse[0].orelse[0].orelse_col_offset == 0
356+
346357
@staticmethod
347358
@pytest.mark.filterwarnings("ignore:.*is_sys_guard:DeprecationWarning")
348359
def test_if_sys_guard() -> None:

0 commit comments

Comments
 (0)