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/<drive>/...` 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