Bug Description
multi_patch corrupts a file when sequential edits shift byte offsets unexpectedly. The second (and later) edits can match the wrong position and merge with surrounding lines, producing syntactically invalid output.
Steps to Reproduce
- A file contains two regions to patch — the first region includes adjacent content (e.g. type aliases) that is not part of
old_string but happens to be deleted by the replacement.
- Call
multi_patch with two edits in the same call:
- Edit 1: Replace a struct definition block.
- Edit 2: Replace a single line that appears later in the file inside an
async move { … } block.
- Observe that Edit 1 silently removes extra lines (type alias declarations) that precede the matched struct, shifting subsequent line positions.
- Edit 2 then finds its
old_string at the wrong byte offset and stitches the replacement to the tail of the preceding line instead of replacing only the target line.
Concrete example from a real session:
Edit 2 old_string:
let start = this.started_at;
Edit 2 new_string:
let start = std::time::Instant::now();
Resulting corrupted line in the file:
let handle = tokio::spawn(async mov let start = std::time::Instant::now();
The closing e { of async move { was consumed and the newline was dropped, merging both lines into one invalid token sequence.
Expected Behavior
Each edit in multi_patch should operate on the result of the previous edit without corrupting surrounding context. If Edit 1 changes the file, Edit 2 should find its old_string correctly in the updated content and replace only that string — preserving adjacent characters, newlines, and indentation.
Actual Behavior
When an earlier edit removes more content than strictly matched by old_string (e.g. trailing/leading whitespace or adjacent declarations get swept up), the byte-offset used by the next edit is wrong. The tool inserts the replacement at the wrong position, truncating the preceding line and joining it with the replacement text without a newline separator.
Using undo on the file confirmed the corruption: restoring the file added back both the silently-deleted type aliases and fixed the merged tokio::spawn line, proving the root cause is cascading offset corruption between sequential edits.
Environment
- Tool:
multi_patch
- File type: Rust (
.rs)
- Number of edits in one call: 2
- Edit 1 scope: multi-line struct + impl block replacement
- Edit 2 scope: single-line replacement inside a nested async closure
Bug Description
multi_patchcorrupts a file when sequential edits shift byte offsets unexpectedly. The second (and later) edits can match the wrong position and merge with surrounding lines, producing syntactically invalid output.Steps to Reproduce
old_stringbut happens to be deleted by the replacement.multi_patchwith two edits in the same call:async move { … }block.old_stringat the wrong byte offset and stitches the replacement to the tail of the preceding line instead of replacing only the target line.Concrete example from a real session:
Edit 2
old_string:Edit 2
new_string:Resulting corrupted line in the file:
The closing
e {ofasync move {was consumed and the newline was dropped, merging both lines into one invalid token sequence.Expected Behavior
Each edit in
multi_patchshould operate on the result of the previous edit without corrupting surrounding context. If Edit 1 changes the file, Edit 2 should find itsold_stringcorrectly in the updated content and replace only that string — preserving adjacent characters, newlines, and indentation.Actual Behavior
When an earlier edit removes more content than strictly matched by
old_string(e.g. trailing/leading whitespace or adjacent declarations get swept up), the byte-offset used by the next edit is wrong. The tool inserts the replacement at the wrong position, truncating the preceding line and joining it with the replacement text without a newline separator.Using
undoon the file confirmed the corruption: restoring the file added back both the silently-deleted type aliases and fixed the mergedtokio::spawnline, proving the root cause is cascading offset corruption between sequential edits.Environment
multi_patch.rs)