llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clangd

Author: Alexandre (blaadje)

<details>
<summary>Changes</summary>

## Description

This change adds proper normalization of URIs between WSL and Windows paths in 
**PathMapping**.
It fixes the issue where using `--path-mappings=/mnt/c/=C:/` with `clangd.exe` 
from **WSL** produced hybrid paths mixing WSL and Windows fragments.

## What’s been done:

- In `clang-tools-extra/clangd/PathMapping.cpp` :

  - Normalize URIs containing encoded Windows fragments (`C:%5c...`).

  - Convert `/mnt/&lt;drive&gt;/...` into `/X:/...` when a drive letter is 
detected.

  - Adjust mapping logic to prevent hybrid paths.

- In `clang-tools-extra/clangd/unittests/PathMappingTests.cpp` :

  - Added test for WSL → Windows conversion (ClientToServer).
  - Added test for Windows → WSL conversion (ServerToClient).

## Resources :
- Fixes issue: https://github.com/clangd/clangd/issues/2462

---
Full diff: https://github.com/llvm/llvm-project/pull/155191.diff


2 Files Affected:

- (modified) clang-tools-extra/clangd/PathMapping.cpp (+70-5) 
- (modified) clang-tools-extra/clangd/unittests/PathMappingTests.cpp (+22) 


``````````diff
diff --git a/clang-tools-extra/clangd/PathMapping.cpp 
b/clang-tools-extra/clangd/PathMapping.cpp
index 4b93ff2c60c5c..68cdf0e3f037e 100644
--- a/clang-tools-extra/clangd/PathMapping.cpp
+++ b/clang-tools-extra/clangd/PathMapping.cpp
@@ -12,6 +12,7 @@
 #include "llvm/Support/Error.h"
 #include "llvm/Support/Path.h"
 #include <algorithm>
+#include <cctype>
 #include <optional>
 #include <tuple>
 
@@ -28,6 +29,30 @@ std::optional<std::string> doPathMapping(llvm::StringRef S,
     llvm::consumeError(Uri.takeError());
     return std::nullopt;
   }
+
+  std::string BodyStr = (*Uri).body().str();
+  std::replace(BodyStr.begin(), BodyStr.end(), '\\', '/');
+
+  bool DidPreferEmbeddedDrive = false;
+  if (BodyStr.rfind("/mnt/", 0) == 0 && BodyStr.size() > 6) {
+    for (size_t i = 0; i + 2 < BodyStr.size(); ++i) {
+      if (std::isalpha((unsigned char)BodyStr[i]) && BodyStr[i + 1] == ':' &&
+          BodyStr[i + 2] == '/') {
+        BodyStr = std::string("/") + BodyStr.substr(i);
+        DidPreferEmbeddedDrive = true;
+        break;
+      }
+    }
+  }
+
+  if (Dir == PathMapping::Direction::ClientToServer && DidPreferEmbeddedDrive) 
{
+    if (BodyStr.size() >= 3 && BodyStr[0] == '/' &&
+        std::isalpha((unsigned char)BodyStr[1]) && BodyStr[2] == ':') {
+      return URI((*Uri).scheme(), (*Uri).authority(), BodyStr).toString();
+    }
+  }
+
+  llvm::StringRef BodyRef(BodyStr);
   for (const auto &Mapping : Mappings) {
     const std::string &From = Dir == PathMapping::Direction::ClientToServer
                                   ? Mapping.ClientPath
@@ -35,13 +60,53 @@ std::optional<std::string> doPathMapping(llvm::StringRef S,
     const std::string &To = Dir == PathMapping::Direction::ClientToServer
                                 ? Mapping.ServerPath
                                 : Mapping.ClientPath;
-    llvm::StringRef Body = Uri->body();
-    if (Body.consume_front(From) && (Body.empty() || Body.front() == '/')) {
-      std::string MappedBody = (To + Body).str();
-      return URI(Uri->scheme(), Uri->authority(), MappedBody)
-          .toString();
+    llvm::StringRef Working = BodyRef;
+    if (Working.consume_front(From)) {
+      if (From.empty() || From.back() == '/' || Working.empty() ||
+          Working.front() == '/') {
+        llvm::StringRef Adjusted = Working;
+
+        char MappingDrive = 0;
+        if (To.size() >= 3 && To[0] == '/' &&
+            std::isalpha((unsigned char)To[1]) && To[2] == ':')
+          MappingDrive = To[1];
+        if (MappingDrive) {
+          for (size_t i = 0; i + 2 < (size_t)Working.size(); ++i) {
+            char c = Working[i];
+            if (std::isalpha((unsigned char)c) && Working[i + 1] == ':' &&
+                Working[i + 2] == '/') {
+              if (std::tolower((unsigned char)c) ==
+                  std::tolower((unsigned char)MappingDrive)) {
+                Adjusted = Working.substr(i + 3);
+                break;
+              }
+            }
+          }
+        }
+        std::string MappedBody = (To + Adjusted).str();
+        return URI((*Uri).scheme(), (*Uri).authority(), MappedBody).toString();
+      }
+    }
+  }
+
+  if (Dir == PathMapping::Direction::ServerToClient) {
+    for (const auto &Mapping : Mappings) {
+      const std::string &From = Mapping.ServerPath;
+      const std::string &To = Mapping.ClientPath;
+      if (From.empty())
+        continue;
+      llvm::StringRef W = BodyRef;
+      if (W.starts_with(From)) {
+        llvm::StringRef Rest = W.substr(From.size());
+        if (Rest.empty() || Rest.front() == '/') {
+          std::string MappedBody = (To + Rest).str();
+          return URI((*Uri).scheme(), (*Uri).authority(), MappedBody)
+              .toString();
+        }
+      }
     }
   }
+
   return std::nullopt;
 }
 
diff --git a/clang-tools-extra/clangd/unittests/PathMappingTests.cpp 
b/clang-tools-extra/clangd/unittests/PathMappingTests.cpp
index 2e26148495a01..a5bc54e7c1d72 100644
--- a/clang-tools-extra/clangd/unittests/PathMappingTests.cpp
+++ b/clang-tools-extra/clangd/unittests/PathMappingTests.cpp
@@ -212,6 +212,28 @@ TEST(ApplyPathMappingTests, MapsKeys) {
   EXPECT_EQ(*Params, *ExpectedParams);
 }
 
+
+TEST(DoPathMappingTests, HandlesEncodedWindowsFragmentsInbound) {
+  // Simulate a client sending a WSL-style path that embeds an encoded Windows
+  // fragment (percent-encoded backslashes and drive letter). The server should
+  // normalize and map it to a Windows-style path.
+  PathMappings Mappings{{"/mnt/c/", "/C:/"}};
+  const char *Orig = 
"file:///mnt/c/projects/cod2-asi/C:%5cUsers%5caukx%5cprojects%5ccod2-asi%5csrc%5cedge_detection.h";
+  std::optional<std::string> Mapped = doPathMapping(Orig, 
PathMapping::Direction::ClientToServer, Mappings);
+  ASSERT_TRUE(bool(Mapped));
+  EXPECT_EQ(*Mapped, 
std::string("file:///C:/Users/aukx/projects/cod2-asi/src/edge_detection.h"));
+}
+
+TEST(DoPathMappingTests, HandlesWindowsToWslOutbound) {
+  // Server returns Windows-style URIs; they should be mapped back to the
+  // client's WSL-style paths before sending out.
+  PathMappings Mappings{{"/mnt/c/", "/C:/"}};
+  const char *Orig = "file:///C:/projects/cod2-asi/src/edge_detection.h";
+  std::optional<std::string> Mapped = doPathMapping(Orig, 
PathMapping::Direction::ServerToClient, Mappings);
+  ASSERT_TRUE(bool(Mapped));
+  EXPECT_EQ(*Mapped, 
std::string("file:///mnt/c/projects/cod2-asi/src/edge_detection.h"));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang

``````````

</details>


https://github.com/llvm/llvm-project/pull/155191
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to