[clang-tools-extra] [clangd] Fix offset invalid on line. (PR #202267)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 10 01:25:47 PDT 2026
https://github.com/playerC updated https://github.com/llvm/llvm-project/pull/202267
>From 5ae898162a317c3b30c9cfa7ac580cf94fb16c86 Mon Sep 17 00:00:00 2001
From: player <playerc at msn.cn>
Date: Mon, 8 Jun 2026 14:27:34 +0800
Subject: [PATCH] [clangd] Fix offset invalid on line.
Fixed the offset invalid on line error,triggered by a
`textDocument/didChange` notification when `range.end.character`
exceeds the corresponding line's length.
---
clang-tools-extra/clangd/SourceCode.cpp | 8 ++-
.../clangd/unittests/SourceCodeTests.cpp | 55 ++++++++++++++++++-
2 files changed, 57 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp
index 21c078fd2cdb9..60aba1da64559 100644
--- a/clang-tools-extra/clangd/SourceCode.cpp
+++ b/clang-tools-extra/clangd/SourceCode.cpp
@@ -1132,8 +1132,9 @@ llvm::Error applyChange(std::string &Contents,
if (!StartIndex)
return StartIndex.takeError();
+ // End position may longer than current line .
const Position &End = Change.range->end;
- llvm::Expected<size_t> EndIndex = positionToOffset(Contents, End, false);
+ llvm::Expected<size_t> EndIndex = positionToOffset(Contents, End, true);
inferFinalNewline(EndIndex, Contents, End);
if (!EndIndex)
return EndIndex.takeError();
@@ -1153,13 +1154,14 @@ llvm::Error applyChange(std::string &Contents,
ssize_t ComputedRangeLength =
lspLength(Contents.substr(*StartIndex, *EndIndex - *StartIndex));
- if (Change.rangeLength && ComputedRangeLength != *Change.rangeLength)
+ // CoumputedRangeLength may less equal than rangeLength.
+ if (Change.rangeLength && ComputedRangeLength > *Change.rangeLength)
return error(llvm::errc::invalid_argument,
"Change's rangeLength ({0}) doesn't match the "
"computed range length ({1}).",
*Change.rangeLength, ComputedRangeLength);
- Contents.replace(*StartIndex, *EndIndex - *StartIndex, Change.text);
+ Contents.replace(*StartIndex, ComputedRangeLength, Change.text);
return llvm::Error::success();
}
diff --git a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
index 801d535c1b9d0..c08e24e1e0961 100644
--- a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
@@ -968,10 +968,57 @@ TEST(ApplyEditsTest, WrongRangeLength) {
Change.range->end.line = 0;
Change.range->end.character = 2;
Change.rangeLength = 10;
+
+ // when rangeLength doesn't match, just use computed range length.
+ EXPECT_THAT_ERROR(applyChange(Code, Change),
+ llvm::Succeeded());
+}
+
+TEST(ApplyEditsTest, InvalidOffsetOfLine){
+ std::string Code = "0123456789\n";
+
+ TextDocumentContentChangeEvent Change;
+
+ // NOTE : just use range.start and end to emulate the computed range
+ // length.
+
+ // computedRangeLength > rangeLength , expect error.
+ Change.range.emplace();
+ Change.range->start.line = 0;
+ Change.range->start.character = 0;
+ Change.range->end.line = 0;
+ Change.range->end.character = 5;
+ Change.rangeLength = 2;
EXPECT_THAT_ERROR(applyChange(Code, Change),
- FailedWithMessage("Change's rangeLength (10) doesn't match "
- "the computed range length (2)."));
+ FailedWithMessage("Change's rangeLength (2) doesn't match "
+ "the computed range length (5)."));
+
+ // computedRangeLength < rangeLength ,use computed, expect success.
+ Change.range.emplace();
+ Change.range->start.line = 0;
+ Change.range->start.character = 0;
+ Change.range->end.line = 0;
+ Change.range->end.character = 3;
+ Change.rangeLength = 6;
+ Change.text = "SIX";
+
+ EXPECT_THAT_ERROR(applyChange(Code, Change),
+ llvm::Succeeded());
+ EXPECT_EQ(Code, "SIX3456789\n");
+
+ // computedRangeLength == rangeLength , expect success.
+ Change.range.emplace();
+ Change.range->start.line = 0;
+ Change.range->start.character = 0;
+ Change.range->end.line = 0;
+ Change.range->end.character = 3;
+ Change.rangeLength = 3;
+ Change.text = "THREE";
+
+ EXPECT_THAT_ERROR(applyChange(Code, Change),
+ llvm::Succeeded());
+ EXPECT_EQ(Code, "THREE3456789\n");
}
// Test that we correct observed buggy edits from Neovim.
@@ -1055,9 +1102,11 @@ TEST(ApplyEditsTest, EndCharOutOfRange) {
Change.range->end.character = 100;
Change.text = "foo";
+ // OK to replace whole line.
EXPECT_THAT_ERROR(
applyChange(Code, Change),
- FailedWithMessage("utf-16 offset 100 is invalid for line 0"));
+ llvm::Succeeded());
+ EXPECT_EQ(Code, "foo\n");
}
TEST(ApplyEditsTest, StartLineOutOfRange) {
More information about the cfe-commits
mailing list