fix(edit): reject an edit whose strings differ only in line ending style - #46849
devYRPauli wants to merge 1 commit into
Conversation
The identity guard runs on the raw input. The tool then converts both strings to the file's line ending 36 lines later. Strings that differ only in line ending style therefore pass the guard and become the same text. The tool reported "Edited file successfully" and "Replacements: 1" with an empty patch, and the file did not change. Run the same check again after the conversion. The v1 tool already does this: packages/opencode/src/tool/edit.ts:683 holds the guard inside replace(), which its caller reaches with both strings already converted.
|
The following comment was made by an LLM, it may be inaccurate: Based on my search results, I found two related PRs that address similar line ending issues in the edit tool:
These PRs appear to be part of a broader set of fixes addressing CRLF and line ending consistency in the edit tool. While they're not exact duplicates of PR #46849, they're addressing related issues in the same |
|
Neither PR covers this one. #45931 changes #45888 changes The issues are separate. #45888 closes #45880. This PR closes #45927. #45888 also does not remove the case here. On a pure CRLF file both strings still convert to CRLF and become identical. On a mixed file both convert to LF and become identical. The tool still reports success in each case, so the guard is still needed. |
|
#47088 fixes the same defect on |
Issue for this PR
Closes #45927
Type of change
What does this PR do?
packages/core/src/tool/edit.tsrejects an edit whereoldStringequalsnewString. That check runs on the raw input at line 127. The tool converts both strings to the file's line ending at lines 163-164, 36 lines later.Two strings that differ only in line ending style pass the raw check and become the same text after the conversion.
source.text.replace(oldString, newString)is then the identity function.countOccurrencesstill returns 1, so the tool reports success anddiffLinesproduces a patch with no content.Measured on
dev(69c172e), through the real tool, on a CRLF file holdingbefore\r\nrest\r\n, witholdString: "before\r\nrest"andnewString: "before\nrest":The model reads
Replacements: 1and believes the edit landed. An LF file gives the same result with the arguments the other way round, because both strings convert to\n.This PR runs the same check again after the conversion. The v1 tool already works this way:
packages/opencode/src/tool/edit.ts:683holds the guard insidereplace(), and its caller at:129-132converts both strings before it calls in. Fed the same input, v1 throwsNo changes to apply: oldString and newString are identical.After the change the tool returns that error and writes nothing:
The raw check at line 127 stays where it is. It fails before the permission prompt and before the file read, which is still the right place for the case it catches.
How did you verify your code works?
New case in
packages/core/test/tool-edit.test.ts, next to the existingpreserves BOM and CRLF line endingscase. It writes a CRLF file, calls the tool with strings that differ only in line ending style, and asserts the error, the unchanged file content, and zero writes.With the guard removed the new case fails and the other 10 pass, so it tests this fix and nothing else.
Screenshots / recordings
Not a UI change.
Checklist