Skip to content

Commit 064462a

Browse files
authored
[3.11] GH-94036: Fix more attribute location quirks (GH-95028) (GH-95156)
(cherry picked from commit 900bfc5)
1 parent bbdacb4 commit 064462a

File tree

3 files changed

+85
-20
lines changed

3 files changed

+85
-20
lines changed

Lib/test/test_compile.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,66 @@ def f():
12041204
end_column=7,
12051205
)
12061206

1207+
def test_attribute_augassign(self):
1208+
source = "(\n lhs \n . \n rhs \n ) += 42"
1209+
code = compile(source, "<test>", "exec")
1210+
self.assertOpcodeSourcePositionIs(
1211+
code, "LOAD_ATTR", line=4, end_line=4, column=5, end_column=8
1212+
)
1213+
self.assertOpcodeSourcePositionIs(
1214+
code, "STORE_ATTR", line=4, end_line=4, column=5, end_column=8
1215+
)
1216+
1217+
def test_attribute_del(self):
1218+
source = "del (\n lhs \n . \n rhs \n )"
1219+
code = compile(source, "<test>", "exec")
1220+
self.assertOpcodeSourcePositionIs(
1221+
code, "DELETE_ATTR", line=4, end_line=4, column=5, end_column=8
1222+
)
1223+
1224+
def test_attribute_load(self):
1225+
source = "(\n lhs \n . \n rhs \n )"
1226+
code = compile(source, "<test>", "exec")
1227+
self.assertOpcodeSourcePositionIs(
1228+
code, "LOAD_ATTR", line=4, end_line=4, column=5, end_column=8
1229+
)
1230+
1231+
def test_attribute_store(self):
1232+
source = "(\n lhs \n . \n rhs \n ) = 42"
1233+
code = compile(source, "<test>", "exec")
1234+
self.assertOpcodeSourcePositionIs(
1235+
code, "STORE_ATTR", line=4, end_line=4, column=5, end_column=8
1236+
)
1237+
1238+
def test_method_call(self):
1239+
source = "(\n lhs \n . \n rhs \n )()"
1240+
code = compile(source, "<test>", "exec")
1241+
self.assertOpcodeSourcePositionIs(
1242+
code, "LOAD_METHOD", line=4, end_line=4, column=5, end_column=8
1243+
)
1244+
self.assertOpcodeSourcePositionIs(
1245+
code, "CALL", line=4, end_line=5, column=5, end_column=10
1246+
)
1247+
1248+
def test_weird_attribute_position_regressions(self):
1249+
def f():
1250+
(bar.
1251+
baz)
1252+
(bar.
1253+
baz(
1254+
))
1255+
files().setdefault(
1256+
0
1257+
).setdefault(
1258+
0
1259+
)
1260+
for line, end_line, column, end_column in f.__code__.co_positions():
1261+
self.assertIsNotNone(line)
1262+
self.assertIsNotNone(end_line)
1263+
self.assertIsNotNone(column)
1264+
self.assertIsNotNone(end_column)
1265+
self.assertLessEqual((line, column), (end_line, end_column))
1266+
12071267

12081268
class TestExpressionStackSize(unittest.TestCase):
12091269
# These tests check that the computed stack size for a code object
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix incorrect source location info for some multi-line attribute accesses
2+
and method calls.

Python/compile.c

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4783,19 +4783,29 @@ is_import_originated(struct compiler *c, expr_ty e)
47834783
return flags & DEF_IMPORT;
47844784
}
47854785

4786+
// If an attribute access spans multiple lines, update the current start
4787+
// location to point to the attribute name.
47864788
static void
4787-
update_location_to_match_attr(struct compiler *c, expr_ty meth)
4789+
update_start_location_to_match_attr(struct compiler *c, expr_ty attr)
47884790
{
4789-
if (meth->lineno != meth->end_lineno) {
4790-
// Make start location match attribute
4791-
c->u->u_lineno = c->u->u_end_lineno = meth->end_lineno;
4792-
int len = (int)PyUnicode_GET_LENGTH(meth->v.Attribute.attr);
4793-
if (len <= meth->end_col_offset) {
4794-
c->u->u_col_offset = meth->end_col_offset - len;
4791+
assert(attr->kind == Attribute_kind);
4792+
if (c->u->u_lineno != attr->end_lineno) {
4793+
c->u->u_lineno = attr->end_lineno;
4794+
int len = (int)PyUnicode_GET_LENGTH(attr->v.Attribute.attr);
4795+
if (len <= attr->end_col_offset) {
4796+
c->u->u_col_offset = attr->end_col_offset - len;
47954797
}
47964798
else {
47974799
// GH-94694: Somebody's compiling weird ASTs. Just drop the columns:
4798-
c->u->u_col_offset = c->u->u_end_col_offset = -1;
4800+
c->u->u_col_offset = -1;
4801+
c->u->u_end_col_offset = -1;
4802+
}
4803+
// Make sure the end position still follows the start position, even for
4804+
// weird ASTs:
4805+
c->u->u_end_lineno = Py_MAX(c->u->u_lineno, c->u->u_end_lineno);
4806+
if (c->u->u_lineno == c->u->u_end_lineno) {
4807+
c->u->u_end_col_offset = Py_MAX(c->u->u_col_offset,
4808+
c->u->u_end_col_offset);
47994809
}
48004810
}
48014811
}
@@ -4842,7 +4852,7 @@ maybe_optimize_method_call(struct compiler *c, expr_ty e)
48424852
/* Alright, we can optimize the code. */
48434853
VISIT(c, expr, meth->v.Attribute.value);
48444854
SET_LOC(c, meth);
4845-
update_location_to_match_attr(c, meth);
4855+
update_start_location_to_match_attr(c, meth);
48464856
ADDOP_NAME(c, LOAD_METHOD, meth->v.Attribute.attr, names);
48474857
VISIT_SEQ(c, expr, e->v.Call.args);
48484858

@@ -4853,7 +4863,7 @@ maybe_optimize_method_call(struct compiler *c, expr_ty e)
48534863
};
48544864
}
48554865
SET_LOC(c, e);
4856-
update_location_to_match_attr(c, meth);
4866+
update_start_location_to_match_attr(c, meth);
48574867
ADDOP_I(c, PRECALL, argsl + kwdsl);
48584868
ADDOP_I(c, CALL, argsl + kwdsl);
48594869
return 1;
@@ -5863,23 +5873,18 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
58635873
/* The following exprs can be assignment targets. */
58645874
case Attribute_kind:
58655875
VISIT(c, expr, e->v.Attribute.value);
5876+
update_start_location_to_match_attr(c, e);
58665877
switch (e->v.Attribute.ctx) {
58675878
case Load:
58685879
{
5869-
int old_lineno = c->u->u_lineno;
5870-
c->u->u_lineno = e->end_lineno;
58715880
ADDOP_NAME(c, LOAD_ATTR, e->v.Attribute.attr, names);
5872-
c->u->u_lineno = old_lineno;
58735881
break;
58745882
}
58755883
case Store:
58765884
if (forbidden_name(c, e->v.Attribute.attr, e->v.Attribute.ctx)) {
58775885
return 0;
58785886
}
5879-
int old_lineno = c->u->u_lineno;
5880-
c->u->u_lineno = e->end_lineno;
58815887
ADDOP_NAME(c, STORE_ATTR, e->v.Attribute.attr, names);
5882-
c->u->u_lineno = old_lineno;
58835888
break;
58845889
case Del:
58855890
ADDOP_NAME(c, DELETE_ATTR, e->v.Attribute.attr, names);
@@ -5945,10 +5950,8 @@ compiler_augassign(struct compiler *c, stmt_ty s)
59455950
case Attribute_kind:
59465951
VISIT(c, expr, e->v.Attribute.value);
59475952
ADDOP_I(c, COPY, 1);
5948-
int old_lineno = c->u->u_lineno;
5949-
c->u->u_lineno = e->end_lineno;
5953+
update_start_location_to_match_attr(c, e);
59505954
ADDOP_NAME(c, LOAD_ATTR, e->v.Attribute.attr, names);
5951-
c->u->u_lineno = old_lineno;
59525955
break;
59535956
case Subscript_kind:
59545957
VISIT(c, expr, e->v.Subscript.value);
@@ -5980,7 +5983,7 @@ compiler_augassign(struct compiler *c, stmt_ty s)
59805983

59815984
switch (e->kind) {
59825985
case Attribute_kind:
5983-
c->u->u_lineno = e->end_lineno;
5986+
update_start_location_to_match_attr(c, e);
59845987
ADDOP_I(c, SWAP, 2);
59855988
ADDOP_NAME(c, STORE_ATTR, e->v.Attribute.attr, names);
59865989
break;

0 commit comments

Comments
 (0)