https://github.com/playerC updated 
https://github.com/llvm/llvm-project/pull/202267

>From 5ae898162a317c3b30c9cfa7ac580cf94fb16c86 Mon Sep 17 00:00:00 2001
From: player <[email protected]>
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) {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to