mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	[3.11] GH-94694: Fix column offsets for multi-line method lookups (GH-94721)
(cherry picked from commit 264b3ddfd5)
			
			
This commit is contained in:
		
							parent
							
								
									7b5737a51a
								
							
						
					
					
						commit
						e5c8ad3e15
					
				
					 4 changed files with 85 additions and 2 deletions
				
			
		|  | @ -1145,6 +1145,27 @@ def test_complex_single_line_expression(self): | ||||||
|         self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', |         self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', | ||||||
|             line=1, end_line=1, column=0, end_column=27, occurrence=4) |             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): | class TestExpressionStackSize(unittest.TestCase): | ||||||
|     # These tests check that the computed stack size for a code object |     # These tests check that the computed stack size for a code object | ||||||
|  |  | ||||||
|  | @ -703,6 +703,57 @@ class A: pass | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(result_lines, expected_error.splitlines()) |         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 | @cpython_only | ||||||
| @requires_debug_ranges() | @requires_debug_ranges() | ||||||
| class CPythonTracebackErrorCaretTests(TracebackErrorLocationCaretTests): | class CPythonTracebackErrorCaretTests(TracebackErrorLocationCaretTests): | ||||||
|  |  | ||||||
|  | @ -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. | ||||||
|  | @ -4788,8 +4788,15 @@ update_location_to_match_attr(struct compiler *c, expr_ty meth) | ||||||
| { | { | ||||||
|     if (meth->lineno != meth->end_lineno) { |     if (meth->lineno != meth->end_lineno) { | ||||||
|         // Make start location match attribute
 |         // Make start location match attribute
 | ||||||
|         c->u->u_lineno = meth->end_lineno; |         c->u->u_lineno = c->u->u_end_lineno = meth->end_lineno; | ||||||
|         c->u->u_col_offset = meth->end_col_offset - (int)PyUnicode_GetLength(meth->v.Attribute.attr)-1; |         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; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Brandt Bucher
						Brandt Bucher