Skip to content

[3.11] GH-94036: Fix more attribute location quirks (GH-95028) #95156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1204,6 +1204,66 @@ def f():
end_column=7,
)

def test_attribute_augassign(self):
source = "(\n lhs \n . \n rhs \n ) += 42"
code = compile(source, "<test>", "exec")
self.assertOpcodeSourcePositionIs(
code, "LOAD_ATTR", line=4, end_line=4, column=5, end_column=8
)
self.assertOpcodeSourcePositionIs(
code, "STORE_ATTR", line=4, end_line=4, column=5, end_column=8
)

def test_attribute_del(self):
source = "del (\n lhs \n . \n rhs \n )"
code = compile(source, "<test>", "exec")
self.assertOpcodeSourcePositionIs(
code, "DELETE_ATTR", line=4, end_line=4, column=5, end_column=8
)

def test_attribute_load(self):
source = "(\n lhs \n . \n rhs \n )"
code = compile(source, "<test>", "exec")
self.assertOpcodeSourcePositionIs(
code, "LOAD_ATTR", line=4, end_line=4, column=5, end_column=8
)

def test_attribute_store(self):
source = "(\n lhs \n . \n rhs \n ) = 42"
code = compile(source, "<test>", "exec")
self.assertOpcodeSourcePositionIs(
code, "STORE_ATTR", line=4, end_line=4, column=5, end_column=8
)

def test_method_call(self):
source = "(\n lhs \n . \n rhs \n )()"
code = compile(source, "<test>", "exec")
self.assertOpcodeSourcePositionIs(
code, "LOAD_METHOD", line=4, end_line=4, column=5, end_column=8
)
self.assertOpcodeSourcePositionIs(
code, "CALL", line=4, end_line=5, column=5, end_column=10
)

def test_weird_attribute_position_regressions(self):
def f():
(bar.
baz)
(bar.
baz(
))
files().setdefault(
0
).setdefault(
0
)
for line, end_line, column, end_column in f.__code__.co_positions():
self.assertIsNotNone(line)
self.assertIsNotNone(end_line)
self.assertIsNotNone(column)
self.assertIsNotNone(end_column)
self.assertLessEqual((line, column), (end_line, end_column))


class TestExpressionStackSize(unittest.TestCase):
# These tests check that the computed stack size for a code object
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix incorrect source location info for some multi-line attribute accesses
and method calls.
43 changes: 23 additions & 20 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -4783,19 +4783,29 @@ is_import_originated(struct compiler *c, expr_ty e)
return flags & DEF_IMPORT;
}

// If an attribute access spans multiple lines, update the current start
// location to point to the attribute name.
static void
update_location_to_match_attr(struct compiler *c, expr_ty meth)
update_start_location_to_match_attr(struct compiler *c, expr_ty attr)
{
if (meth->lineno != meth->end_lineno) {
// Make start location match attribute
c->u->u_lineno = c->u->u_end_lineno = meth->end_lineno;
int len = (int)PyUnicode_GET_LENGTH(meth->v.Attribute.attr);
if (len <= meth->end_col_offset) {
c->u->u_col_offset = meth->end_col_offset - len;
assert(attr->kind == Attribute_kind);
if (c->u->u_lineno != attr->end_lineno) {
c->u->u_lineno = attr->end_lineno;
int len = (int)PyUnicode_GET_LENGTH(attr->v.Attribute.attr);
if (len <= attr->end_col_offset) {
c->u->u_col_offset = attr->end_col_offset - len;
}
else {
// GH-94694: Somebody's compiling weird ASTs. Just drop the columns:
c->u->u_col_offset = c->u->u_end_col_offset = -1;
c->u->u_col_offset = -1;
c->u->u_end_col_offset = -1;
}
// Make sure the end position still follows the start position, even for
// weird ASTs:
c->u->u_end_lineno = Py_MAX(c->u->u_lineno, c->u->u_end_lineno);
if (c->u->u_lineno == c->u->u_end_lineno) {
c->u->u_end_col_offset = Py_MAX(c->u->u_col_offset,
c->u->u_end_col_offset);
}
}
}
Expand Down Expand Up @@ -4842,7 +4852,7 @@ maybe_optimize_method_call(struct compiler *c, expr_ty e)
/* Alright, we can optimize the code. */
VISIT(c, expr, meth->v.Attribute.value);
SET_LOC(c, meth);
update_location_to_match_attr(c, meth);
update_start_location_to_match_attr(c, meth);
ADDOP_NAME(c, LOAD_METHOD, meth->v.Attribute.attr, names);
VISIT_SEQ(c, expr, e->v.Call.args);

Expand All @@ -4853,7 +4863,7 @@ maybe_optimize_method_call(struct compiler *c, expr_ty e)
};
}
SET_LOC(c, e);
update_location_to_match_attr(c, meth);
update_start_location_to_match_attr(c, meth);
ADDOP_I(c, PRECALL, argsl + kwdsl);
ADDOP_I(c, CALL, argsl + kwdsl);
return 1;
Expand Down Expand Up @@ -5863,23 +5873,18 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
/* The following exprs can be assignment targets. */
case Attribute_kind:
VISIT(c, expr, e->v.Attribute.value);
update_start_location_to_match_attr(c, e);
switch (e->v.Attribute.ctx) {
case Load:
{
int old_lineno = c->u->u_lineno;
c->u->u_lineno = e->end_lineno;
ADDOP_NAME(c, LOAD_ATTR, e->v.Attribute.attr, names);
c->u->u_lineno = old_lineno;
break;
}
case Store:
if (forbidden_name(c, e->v.Attribute.attr, e->v.Attribute.ctx)) {
return 0;
}
int old_lineno = c->u->u_lineno;
c->u->u_lineno = e->end_lineno;
ADDOP_NAME(c, STORE_ATTR, e->v.Attribute.attr, names);
c->u->u_lineno = old_lineno;
break;
case Del:
ADDOP_NAME(c, DELETE_ATTR, e->v.Attribute.attr, names);
Expand Down Expand Up @@ -5945,10 +5950,8 @@ compiler_augassign(struct compiler *c, stmt_ty s)
case Attribute_kind:
VISIT(c, expr, e->v.Attribute.value);
ADDOP_I(c, COPY, 1);
int old_lineno = c->u->u_lineno;
c->u->u_lineno = e->end_lineno;
update_start_location_to_match_attr(c, e);
ADDOP_NAME(c, LOAD_ATTR, e->v.Attribute.attr, names);
c->u->u_lineno = old_lineno;
break;
case Subscript_kind:
VISIT(c, expr, e->v.Subscript.value);
Expand Down Expand Up @@ -5980,7 +5983,7 @@ compiler_augassign(struct compiler *c, stmt_ty s)

switch (e->kind) {
case Attribute_kind:
c->u->u_lineno = e->end_lineno;
update_start_location_to_match_attr(c, e);
ADDOP_I(c, SWAP, 2);
ADDOP_NAME(c, STORE_ATTR, e->v.Attribute.attr, names);
break;
Expand Down