Skip to content

fix(compiler): use span-overlap check for signal mutation ranges (#2785)#3019

Merged
viniciusdacal merged 2 commits intomainfrom
fix/mutation-range-overlap-2785
Apr 28, 2026
Merged

fix(compiler): use span-overlap check for signal mutation ranges (#2785)#3019
viniciusdacal merged 2 commits intomainfrom
fix/mutation-range-overlap-2785

Conversation

@viniciusdacal
Copy link
Copy Markdown
Contributor

Summary

Closes #2785.

is_in_mutation_range in signal_transformer.rs used a point-membership check on ident.span.start (pos >= start && pos < end). The contract relied on mutation_analyzer always recording a span that begins exactly at the identifier's first character — robust today, but silently broken by any future tightening (e.g., the operator-only span for +=, or a span recorded on an inner sub-expression).

Replaced the predicate with a half-open span-overlap check (ident_start < range.end && ident_end > range.start), renamed it to overlaps_mutation_range, and applied it at all three call sites:

  • visit_identifier_reference (signal reads)
  • visit_assignment_expression (LHS of =, +=, etc.)
  • visit_update_expression (++, --)

Today this is observationally equivalent to the old check — every recorded range happens to start at the identifier's start, so both predicates agree. The change makes the predicate robust to future analyzer changes.

Public API Changes

None — internal compiler logic only.

Test plan

  • Three new regression tests in signal_transformer::tests inject a custom mutation range whose start is greater than the identifier's start and assert .value is suppressed at each call site.
  • All 32 prior signal_transformer tests still pass.
  • Adversarial review confirmed RED→GREEN: tests fail with the old point-check predicate and pass with the new overlap predicate.
  • cargo test --all, cargo clippy --all-targets -- -D warnings, cargo fmt --all -- --check all green.
  • Pre-push hooks (build/typecheck/lint/rust-test/rust-clippy/rust-fmt/trojan-source) all green.

🤖 Generated with Claude Code

viniciusdacal and others added 2 commits April 28, 2026 13:02
Replace the point-membership check in `is_in_mutation_range` with a
half-open span-overlap check (`ident.start < range.end && ident.end >
range.start`), and rename the predicate to `overlaps_mutation_range`.

The previous check only tested whether the identifier's first character
sat inside a recorded mutation range. That happened to work because
`mutation_analyzer` always records a span starting at the identifier's
start, but any future tightening (e.g., the operator-only span for
`+=`, or a span recorded on an inner sub-expression) would silently
misclassify the identifier as outside the range and double-handle it
by appending `.value` on top of the mutation rewrite.

Applied the new predicate at all three call sites: identifier reads,
assignment-expression LHS, and update-expression targets. Added three
regression tests that inject a mutation range whose start is greater
than the identifier's start and assert `.value` is suppressed at each
site.

Closes #2785

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…2785]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@viniciusdacal viniciusdacal merged commit 3accbd6 into main Apr 28, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

compiler(signal): is_in_mutation_range uses point check, not span overlap

1 participant