diff -r e4ee0b15cc4f Lib/test/test_ast.py --- a/Lib/test/test_ast.py Wed Apr 16 23:32:21 2014 +0530 +++ b/Lib/test/test_ast.py Thu Apr 17 11:17:32 2014 -0400 @@ -423,6 +423,16 @@ compile(empty_yield_from, "", "exec") self.assertIn("field value is required", str(cm.exception)) + def test_binop_col_offset(self): + # Issue 18374: sequences of binops don't get correct col_offsets + tree = ast.parse('4+5+6+7') # Check recursion beyond one level + parent_binop = tree.body[0].value + child_binop = parent_binop.left + grandchild_binop = child_binop.left + self.assertEqual(parent_binop.col_offset, 0) + self.assertEqual(child_binop.col_offset, 0) + self.assertEqual(grandchild_binop.col_offset, 0) + class ASTHelpers_Test(unittest.TestCase): diff -r e4ee0b15cc4f Python/Python-ast.c --- a/Python/Python-ast.c Wed Apr 16 23:32:21 2014 +0530 +++ b/Python/Python-ast.c Thu Apr 17 11:17:32 2014 -0400 @@ -1621,7 +1621,15 @@ p->v.BinOp.op = op; p->v.BinOp.right = right; p->lineno = lineno; - p->col_offset = col_offset; + if (p->v.BinOp.left->kind == BinOp_kind) { + /* Bug 18374: binops are left-associative, so the expr that generated + * this node actually started where its leftmost child started. + * Update our col_offset to match. + */ + p->col_offset = p->v.BinOp.left->col_offset; + } else { + p->col_offset = col_offset; + } return p; } diff -r e4ee0b15cc4f Python/compile.c --- a/Python/compile.c Wed Apr 16 23:32:21 2014 +0530 +++ b/Python/compile.c Thu Apr 17 11:17:32 2014 -0400 @@ -3420,6 +3420,13 @@ case BinOp_kind: VISIT(c, expr, e->v.BinOp.left); VISIT(c, expr, e->v.BinOp.right); + if (e->v.BinOp.left->kind == BinOp_kind) { + /* Bug 18374: binops are left-associative, so update this node's + * col_offset to start where the entire expression did. + */ + e->col_offset = e->v.BinOp.left->col_offset; + c->u->u_col_offset = e->col_offset; + } ADDOP(c, binop(c, e->v.BinOp.op)); break; case UnaryOp_kind: