Pin the right-associative ^ branch of Render with a test
samePrecedenceNeedsParens special-cases precPower to bracket the left child instead of the right, and that branch had zero coverage: none of the three round-1 cases exercised ^. If the special case were ever simplified away, (2^3)^2 would render as 2^3^2, which reparses as 2^(3^2) -- 64 silently becoming 512. Adds two rows to TestRenderRoundTripsThroughReparse: (2^3)^2 (must keep its brackets) and 2^3^2 (must not gain any), both checked via the existing Parse -> Render -> Parse -> reflect.DeepEqual round trip. Verified by temporarily deleting the precPower special case: the new right_associative_power_needs_left_parens subtest fails exactly as expected, confirming the test actually exercises the branch.
1 file changed,
+10
−0
internal/sheets/calc/translate_test.go+10 −0
Intent
diff --git a/internal/sheets/calc/translate_test.go b/internal/sheets/calc/translate_test.go| index dade354..8dd959d 100644 |
| --- a/internal/sheets/calc/translate_test.go |
| +++ b/internal/sheets/calc/translate_test.go |
| @@ -95,6 +95,16 @@ func TestRenderRoundTripsThroughReparse(t *testing.T) { |
| // same-precedence RIGHT operand: without them "8-3-1" |
| // reparses as (8-3)-1 — 6 would silently become 4. |
| {"left_associative_subtraction_needs_right_parens", "8-(3-1)"}, |
| + // ^ is the one right-associative level, so it needs parens on |
| + // a same-precedence LEFT child instead of the right: without |
| + // them "2^3^2" reparses as 2^(3^2) — 64 would silently become |
| + // 512. |
| + {"right_associative_power_needs_left_parens", "(2^3)^2"}, |
| + // The mirror case: a right child at the same precedence as its |
| + // right-associative parent must NOT gain parens — this is the |
| + // branch with the least test coverage, and the one where a |
| + // wrong or deleted associativity check would go unnoticed. |
| + {"right_associative_power_keeps_bare_right_child", "2^3^2"}, |
| } |
| for _, tc := range cases { |
| t.Run(tc.name, func(t *testing.T) { |