summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrandt Bucher <brandtbucher@microsoft.com>2022-07-10 00:22:23 (GMT)
committerGitHub <noreply@github.com>2022-07-10 00:22:23 (GMT)
commit264b3ddfd561d97204ffb30be6a7d1fb0555e560 (patch)
tree7c18cb52295ed1a678fdbd1e9d0722ff70eba84c
parenta10cf2f6b3766f9dbbe54bdaacfb3f2ca406ea3d (diff)
downloadcpython-264b3ddfd561d97204ffb30be6a7d1fb0555e560.zip
cpython-264b3ddfd561d97204ffb30be6a7d1fb0555e560.tar.gz
cpython-264b3ddfd561d97204ffb30be6a7d1fb0555e560.tar.bz2
GH-94694: Fix column offsets for multi-line method lookups (GH-94697)
-rw-r--r--Lib/test/test_compile.py21
-rw-r--r--Lib/test/test_traceback.py51
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-07-08-16-44-11.gh-issue-94694.VkL2CM.rst4
-rw-r--r--Python/compile.c11
4 files changed, 85 insertions, 2 deletions
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index ab1685d..bc022a9 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -1181,6 +1181,27 @@ f(
self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
line=1, end_line=1, column=0, end_column=27, occurrence=4)
+ def test_multiline_assert_rewritten_as_method_call(self):
+ # GH-94694: Don't crash if pytest rewrites a multiline assert as a
+ # method call with the same location information:
+ tree = ast.parse("assert (\n42\n)")
+ old_node = tree.body[0]
+ new_node = ast.Expr(
+ ast.Call(
+ ast.Attribute(
+ ast.Name("spam", ast.Load()),
+ "eggs",
+ ast.Load(),
+ ),
+ [],
+ [],
+ )
+ )
+ ast.copy_location(new_node, old_node)
+ ast.fix_missing_locations(new_node)
+ tree.body[0] = new_node
+ compile(tree, "<test>", "exec")
+
class TestExpressionStackSize(unittest.TestCase):
# These tests check that the computed stack size for a code object
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 722c265..72d67bf 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -702,6 +702,57 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
)
self.assertEqual(result_lines, expected_error.splitlines())
+ def test_multiline_method_call_a(self):
+ def f():
+ (None
+ .method
+ )()
+ actual = self.get_exception(f)
+ expected = [
+ f"Traceback (most recent call last):",
+ f" File \"{__file__}\", line {self.callable_line}, in get_exception",
+ f" callable()",
+ f" ^^^^^^^^^^",
+ f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
+ f" .method",
+ f" ^^^^^^",
+ ]
+ self.assertEqual(actual, expected)
+
+ def test_multiline_method_call_b(self):
+ def f():
+ (None.
+ method
+ )()
+ actual = self.get_exception(f)
+ expected = [
+ f"Traceback (most recent call last):",
+ f" File \"{__file__}\", line {self.callable_line}, in get_exception",
+ f" callable()",
+ f" ^^^^^^^^^^",
+ f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
+ f" method",
+ f" ^^^^^^",
+ ]
+ self.assertEqual(actual, expected)
+
+ def test_multiline_method_call_c(self):
+ def f():
+ (None
+ . method
+ )()
+ actual = self.get_exception(f)
+ expected = [
+ f"Traceback (most recent call last):",
+ f" File \"{__file__}\", line {self.callable_line}, in get_exception",
+ f" callable()",
+ f" ^^^^^^^^^^",
+ f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
+ f" . method",
+ f" ^^^^^^",
+ ]
+ self.assertEqual(actual, expected)
+
@cpython_only
@requires_debug_ranges()
class CPythonTracebackErrorCaretTests(TracebackErrorLocationCaretTests):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-07-08-16-44-11.gh-issue-94694.VkL2CM.rst b/Misc/NEWS.d/next/Core and Builtins/2022-07-08-16-44-11.gh-issue-94694.VkL2CM.rst
new file mode 100644
index 0000000..6434788
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-07-08-16-44-11.gh-issue-94694.VkL2CM.rst
@@ -0,0 +1,4 @@
+Fix an issue that could cause code with multi-line method lookups to have
+misleading or incorrect column offset information. In some cases (when
+compiling a hand-built AST) this could have resulted in a hard crash of the
+interpreter.
diff --git a/Python/compile.c b/Python/compile.c
index f36a6e8..427fb2a 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -4736,8 +4736,15 @@ update_location_to_match_attr(struct compiler *c, expr_ty meth)
{
if (meth->lineno != meth->end_lineno) {
// Make start location match attribute
- c->u->u_loc.lineno = meth->end_lineno;
- c->u->u_loc.col_offset = meth->end_col_offset - (int)PyUnicode_GetLength(meth->v.Attribute.attr)-1;
+ c->u->u_loc.lineno = c->u->u_loc.end_lineno = meth->end_lineno;
+ int len = (int)PyUnicode_GET_LENGTH(meth->v.Attribute.attr);
+ if (len <= meth->end_col_offset) {
+ c->u->u_loc.col_offset = meth->end_col_offset - len;
+ }
+ else {
+ // GH-94694: Somebody's compiling weird ASTs. Just drop the columns:
+ c->u->u_loc.col_offset = c->u->u_loc.end_col_offset = -1;
+ }
}
}