diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index d113a00088f2c6c00b37c786baff11596827eba5..80b32dee0665f7a7a7890feef6737247aa502919 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -1145,6 +1145,27 @@ def test_complex_single_line_expression(self):
         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 e8ff4b5a28f55ff58c25f60d314e7d992db2738a..40d5a84f92d999abb2d13818c64d9cafd1bbdc7c 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -703,6 +703,57 @@ class A: pass
         )
         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 0000000000000000000000000000000000000000..6434788140f420129bc1b35371cf3e36dd442e74
--- /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 a71e7d31eecd28e09d1a6000c007079dce94d2e8..42f3730e220f58896b948699bc2b857e60d7fb3d 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -4788,8 +4788,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_lineno = meth->end_lineno;
-        c->u->u_col_offset = meth->end_col_offset - (int)PyUnicode_GetLength(meth->v.Attribute.attr)-1;
+        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;
+        }
+        else {
+            // GH-94694: Somebody's compiling weird ASTs. Just drop the columns:
+            c->u->u_col_offset = c->u->u_end_col_offset = -1;
+        }
     }
 }