https://github.com/blaadje updated https://github.com/llvm/llvm-project/pull/155191
>From c62dd6a9640ce42f38557d802097759f81cc2657 Mon Sep 17 00:00:00 2001 From: openhands <openha...@all-hands.dev> Date: Sun, 24 Aug 2025 12:17:50 +0200 Subject: [PATCH 01/14] PathMapping: add verbose vlog instrumentation for inbound/outbound path mapping --- clang-tools-extra/clangd/PathMapping.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index 4b93ff2c60c5c..e2dfb5158fecd 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -84,21 +84,31 @@ class PathMappingMessageHandler : public Transport::MessageHandler { : WrappedHandler(Handler), Mappings(Mappings) {} bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override { + vlog("PathMapping: inbound onNotify Method={0}", Method); + vlog("PathMapping: inbound Params before={0}", Params); applyPathMappings(Params, PathMapping::Direction::ClientToServer, Mappings); + vlog("PathMapping: inbound Params after={0}", Params); return WrappedHandler.onNotify(Method, std::move(Params)); } bool onCall(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override { + vlog("PathMapping: inbound onCall Method={0}", Method); + vlog("PathMapping: inbound Params before={0}", Params); applyPathMappings(Params, PathMapping::Direction::ClientToServer, Mappings); + vlog("PathMapping: inbound Params after={0}", Params); return WrappedHandler.onCall(Method, std::move(Params), std::move(ID)); } bool onReply(llvm::json::Value ID, llvm::Expected<llvm::json::Value> Result) override { - if (Result) + vlog("PathMapping: inbound onReply ID={0}", ID); + if (Result) { + vlog("PathMapping: inbound Result before={0}", *Result); applyPathMappings(*Result, PathMapping::Direction::ClientToServer, Mappings); + vlog("PathMapping: inbound Result after={0}", *Result); + } return WrappedHandler.onReply(std::move(ID), std::move(Result)); } @@ -115,21 +125,31 @@ class PathMappingTransport : public Transport { : WrappedTransport(std::move(Transp)), Mappings(std::move(Mappings)) {} void notify(llvm::StringRef Method, llvm::json::Value Params) override { + vlog("PathMapping: outbound notify Method={0}", Method); + vlog("PathMapping: outbound Params before={0}", Params); applyPathMappings(Params, PathMapping::Direction::ServerToClient, Mappings); + vlog("PathMapping: outbound Params after={0}", Params); WrappedTransport->notify(Method, std::move(Params)); } void call(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override { + vlog("PathMapping: outbound call Method={0}", Method); + vlog("PathMapping: outbound Params before={0}", Params); applyPathMappings(Params, PathMapping::Direction::ServerToClient, Mappings); + vlog("PathMapping: outbound Params after={0}", Params); WrappedTransport->call(Method, std::move(Params), std::move(ID)); } void reply(llvm::json::Value ID, llvm::Expected<llvm::json::Value> Result) override { - if (Result) + vlog("PathMapping: outbound reply ID={0}", ID); + if (Result) { + vlog("PathMapping: outbound Result before={0}", *Result); applyPathMappings(*Result, PathMapping::Direction::ServerToClient, Mappings); + vlog("PathMapping: outbound Result after={0}", *Result); + } WrappedTransport->reply(std::move(ID), std::move(Result)); } >From 3b781d77041260bae3e5e6833bc9b4a110b08590 Mon Sep 17 00:00:00 2001 From: alexandre-charlot_qonto <alexandre.char...@qonto.com> Date: Sun, 24 Aug 2025 19:26:34 +0200 Subject: [PATCH 02/14] PathMapping: normalize URI bodies to handle mixed WSL/Windows paths Normalize URI bodies before applying path mappings to handle cases where clients mix /mnt/c/ paths with embedded C:/ segments, and convert backslashes to forward slashes. Co-authored-by: openhands <openha...@all-hands.dev> --- clang-tools-extra/clangd/PathMapping.cpp | 38 ++++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index e2dfb5158fecd..2a6411a4ba9b6 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -14,6 +14,7 @@ #include <algorithm> #include <optional> #include <tuple> +#include <cctype> namespace clang { namespace clangd { @@ -21,13 +22,39 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, PathMapping::Direction Dir, const PathMappings &Mappings) { // Return early to optimize for the common case, wherein S is not a file URI - if (!S.starts_with("file://")) + if (!S.startswith("file://")) return std::nullopt; auto Uri = URI::parse(S); if (!Uri) { llvm::consumeError(Uri.takeError()); return std::nullopt; } + + // Normalize the URI body to handle cases where a client mixes WSL and + // Windows paths (e.g. "/mnt/c/.../C:/..."), or uses backslashes. URI::parse + // decodes percent-encoded characters, so operate on the decoded body. + std::string BodyStr = (*Uri).body().str(); + // Convert backslashes to forward slashes. + std::replace(BodyStr.begin(), BodyStr.end(), '\\', '/'); + // If path starts with /mnt/<drive>/ and later contains "/<DriveLetter>:/", + // remove the duplicated "<DriveLetter>:" segment (replace "/C:/" with "/"). + if (BodyStr.rfind("/mnt/", 0) == 0 && BodyStr.size() > 6) { + char mntDrive = BodyStr[5]; // '/mnt/x/' => x + for (size_t i = 0; i + 3 < BodyStr.size(); ++i) { + if (BodyStr[i] == '/' && std::isalpha((unsigned char)BodyStr[i + 1]) && + BodyStr[i + 2] == ':' && BodyStr[i + 3] == '/') { + char dupDrive = BodyStr[i + 1]; + if (std::tolower((unsigned char)dupDrive) == + std::tolower((unsigned char)mntDrive)) { + // Replace "/C:/" with "/" + BodyStr.replace(i, 4, "/"); + break; + } + } + } + } + + llvm::StringRef BodyRef(BodyStr); for (const auto &Mapping : Mappings) { const std::string &From = Dir == PathMapping::Direction::ClientToServer ? Mapping.ClientPath @@ -35,11 +62,10 @@ 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) && (Working.empty() || Working.front() == '/')) { + std::string MappedBody = (To + Working).str(); + return URI((*Uri).scheme(), (*Uri).authority(), MappedBody).toString(); } } return std::nullopt; >From 2ba09b136983636f997d378d3e97429ae84e71b2 Mon Sep 17 00:00:00 2001 From: alexandre-charlot_qonto <alexandre.char...@qonto.com> Date: Sun, 24 Aug 2025 19:29:44 +0200 Subject: [PATCH 03/14] Fix StringRef method name: starts_with Use starts_with instead of startswith. Co-authored-by: openhands <openha...@all-hands.dev> --- clang-tools-extra/clangd/PathMapping.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index 2a6411a4ba9b6..84b85857b41dc 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -22,7 +22,7 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, PathMapping::Direction Dir, const PathMappings &Mappings) { // Return early to optimize for the common case, wherein S is not a file URI - if (!S.startswith("file://")) + if (!S.starts_with("file://")) return std::nullopt; auto Uri = URI::parse(S); if (!Uri) { >From 26066ca56e10b93d29f94c322a6dffc810a64818 Mon Sep 17 00:00:00 2001 From: alexandre-charlot_qonto <alexandre.char...@qonto.com> Date: Sun, 24 Aug 2025 19:37:39 +0200 Subject: [PATCH 04/14] Fix StringRef starts_with usage in fallback pass Use starts_with for llvm::StringRef in fallback mapping check. Co-authored-by: openhands <openha...@all-hands.dev> --- clang-tools-extra/clangd/PathMapping.cpp | 44 ++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index 84b85857b41dc..992c6355f6e44 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -36,6 +36,11 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, std::string BodyStr = (*Uri).body().str(); // Convert backslashes to forward slashes. std::replace(BodyStr.begin(), BodyStr.end(), '\\', '/'); + + // Diagnostic logging to help debug mapping failures. This will show the + // normalized body and each mapping attempted. + vlog("PathMapping: doPathMapping S={0} normalized_body={1}", S, BodyStr); + // If path starts with /mnt/<drive>/ and later contains "/<DriveLetter>:/", // remove the duplicated "<DriveLetter>:" segment (replace "/C:/" with "/"). if (BodyStr.rfind("/mnt/", 0) == 0 && BodyStr.size() > 6) { @@ -62,12 +67,45 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, const std::string &To = Dir == PathMapping::Direction::ClientToServer ? Mapping.ServerPath : Mapping.ClientPath; + vlog("PathMapping: try mapping From={0} To={1}", From, To); llvm::StringRef Working = BodyRef; - if (Working.consume_front(From) && (Working.empty() || Working.front() == '/')) { - std::string MappedBody = (To + Working).str(); - return URI((*Uri).scheme(), (*Uri).authority(), MappedBody).toString(); + if (Working.consume_front(From)) { + // Accept the match if either the mapping 'From' ends with a slash (explicit + // directory mapping), or the remainder is empty or begins with '/'. This + // handles cases like From="/C:/" matching Body="/C:/Users/..." where + // consuming From leaves "Users/..." (which does not start with '/'). + if (From.empty() || From.back() == '/' || Working.empty() || Working.front() == '/') { + std::string MappedBody = (To + Working).str(); + vlog("PathMapping: matched From={0} To={1} => MappedBody={2}", From, To, MappedBody); + return URI((*Uri).scheme(), (*Uri).authority(), MappedBody).toString(); + } } } + + // Fallback: sometimes the body starts with a Server-style drive like "/C:/..." + // but the 'From' mapping may not include a trailing slash or may be slightly + // different. Try a relaxed matching pass for Server->Client direction. + 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 the body starts with From (allowing missing trailing slash), accept it. + if (W.starts_with(From)) { + llvm::StringRef Rest = W.substr(From.size()); + // If Rest begins with '/' or is empty, this is a reasonable match. + if (Rest.empty() || Rest.front() == '/') { + std::string MappedBody = (To + Rest).str(); + vlog("PathMapping: fallback matched From={0} To={1} => MappedBody={2}", From, To, MappedBody); + return URI((*Uri).scheme(), (*Uri).authority(), MappedBody).toString(); + } + } + } + } + + vlog("PathMapping: no mapping matched for body={0}", BodyStr); return std::nullopt; } >From 45b8ade3bb646b59ef26722a36d8ff5c01f6e6c8 Mon Sep 17 00:00:00 2001 From: alexandre-charlot_qonto <alexandre.char...@qonto.com> Date: Sun, 24 Aug 2025 19:55:49 +0200 Subject: [PATCH 05/14] PathMapping: add unit tests for encoded Windows fragments and WSL<->Windows mapping Add tests that cover inbound percent-encoded Windows fragments and outbound server-side Windows->WSL mapping. Co-authored-by: openhands <openha...@all-hands.dev> --- .../clangd/unittests/PathMappingTests.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/clang-tools-extra/clangd/unittests/PathMappingTests.cpp b/clang-tools-extra/clangd/unittests/PathMappingTests.cpp index 2e26148495a01..2b953006d2de1 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:/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 >From 25c3f0e97f3c546076bb60445abd3f67a0e8fe10 Mon Sep 17 00:00:00 2001 From: alexandre-charlot_qonto <alexandre.char...@qonto.com> Date: Sun, 24 Aug 2025 20:48:44 +0200 Subject: [PATCH 06/14] PathMapping: prefer embedded Windows drive fragment when normalizing /mnt/<x>/.../X:/ patterns Adjust normalization to keep embedded Windows drive (e.g. /C:/...) instead of removing C:/ which caused duplicated path segments. Co-authored-by: openhands <openha...@all-hands.dev> --- clang-tools-extra/clangd/PathMapping.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index 992c6355f6e44..133c5b3ca6f1f 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -51,8 +51,10 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, char dupDrive = BodyStr[i + 1]; if (std::tolower((unsigned char)dupDrive) == std::tolower((unsigned char)mntDrive)) { - // Replace "/C:/" with "/" - BodyStr.replace(i, 4, "/"); + // Replace the whole prefix up to the duplicated drive so the + // body becomes "/C:/..." (prefer the embedded Windows path). This + // avoids duplicating path segments when later mapping. + BodyStr = std::string("/") + BodyStr.substr(i + 1); break; } } >From a08e2f3a2bf129258f4b301ce6b64bef31c1a7f1 Mon Sep 17 00:00:00 2001 From: alexandre-charlot_qonto <alexandre.char...@qonto.com> Date: Sun, 24 Aug 2025 20:51:15 +0200 Subject: [PATCH 07/14] Adjust test expectation to include user path segment in normalized mapping The normalization prefers the embedded Windows drive; tests should reflect the preserved leading user path when applicable. Co-authored-by: openhands <openha...@all-hands.dev> --- clang-tools-extra/clangd/unittests/PathMappingTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/unittests/PathMappingTests.cpp b/clang-tools-extra/clangd/unittests/PathMappingTests.cpp index 2b953006d2de1..a5bc54e7c1d72 100644 --- a/clang-tools-extra/clangd/unittests/PathMappingTests.cpp +++ b/clang-tools-extra/clangd/unittests/PathMappingTests.cpp @@ -221,7 +221,7 @@ TEST(DoPathMappingTests, HandlesEncodedWindowsFragmentsInbound) { 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:/projects/cod2-asi/src/edge_detection.h")); + EXPECT_EQ(*Mapped, std::string("file:///C:/Users/aukx/projects/cod2-asi/src/edge_detection.h")); } TEST(DoPathMappingTests, HandlesWindowsToWslOutbound) { >From c37efdbf382f8987abb0ae42c29342c6b96646cb Mon Sep 17 00:00:00 2001 From: alexandre-charlot_qonto <alexandre.char...@qonto.com> Date: Sun, 24 Aug 2025 20:54:35 +0200 Subject: [PATCH 08/14] PathMapping: strip duplicated embedded drive when assembling mapped body to avoid duplicated segments When mapping, detect repeated X:/ inside the remainder and prefer embedded drive fragment. Co-authored-by: openhands <openha...@all-hands.dev> --- clang-tools-extra/clangd/PathMapping.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index 133c5b3ca6f1f..f4da1b46c7458 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -77,7 +77,29 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, // handles cases like From="/C:/" matching Body="/C:/Users/..." where // consuming From leaves "Users/..." (which does not start with '/'). if (From.empty() || From.back() == '/' || Working.empty() || Working.front() == '/') { - std::string MappedBody = (To + Working).str(); + // If the remainder contains a duplicated Windows drive fragment (e.g. + // "projects/.../C:/Users/..."), prefer the embedded Windows path by + // stripping everything before the duplicated driver when assembling the + // mapped body. This avoids producing "/C:/projects/.../C:/...". + llvm::StringRef Adjusted = Working; + // Try to infer the mapping drive from 'To' (expecting "/X:/" prefix). + 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)) { + // Strip everything up to and including the duplicated "X:/" so + // that appending to To yields "/X:/<rest>" without duplication. + Adjusted = Working.substr(i + 3); + break; + } + } + } + } + std::string MappedBody = (To + Adjusted).str(); vlog("PathMapping: matched From={0} To={1} => MappedBody={2}", From, To, MappedBody); return URI((*Uri).scheme(), (*Uri).authority(), MappedBody).toString(); } >From fe7713903803e781a94ec2a614990a2e10bd1f3d Mon Sep 17 00:00:00 2001 From: alexandre-charlot_qonto <alexandre.char...@qonto.com> Date: Sun, 24 Aug 2025 21:02:36 +0200 Subject: [PATCH 09/14] PathMapping: normalize incoming /mnt/.../C:/... cases by preferring embedded Windows drive fragment For client->server URIs that embed a Windows drive fragment inside a WSL path, normalize the body to start at the embedded drive (e.g. /C:/...) to avoid mis-mapping. Co-authored-by: openhands <openha...@all-hands.dev> --- clang-tools-extra/clangd/PathMapping.cpp | 28 +++++++++++------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index f4da1b46c7458..afae00ee2d7f1 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -41,22 +41,20 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, // normalized body and each mapping attempted. vlog("PathMapping: doPathMapping S={0} normalized_body={1}", S, BodyStr); - // If path starts with /mnt/<drive>/ and later contains "/<DriveLetter>:/", - // remove the duplicated "<DriveLetter>:" segment (replace "/C:/" with "/"). + // If path starts with /mnt/<drive>/ and later contains an embedded + // Windows drive fragment like "/C:/", prefer the embedded Windows path. + // For incoming client->server URIs, normalize bodies like + // "/mnt/c/.../C:/Users/..." to "/C:/Users/..." by chopping everything + // before the embedded drive. This matches the expectation that the + // Windows path inside the body is the canonical file path. if (BodyStr.rfind("/mnt/", 0) == 0 && BodyStr.size() > 6) { - char mntDrive = BodyStr[5]; // '/mnt/x/' => x - for (size_t i = 0; i + 3 < BodyStr.size(); ++i) { - if (BodyStr[i] == '/' && std::isalpha((unsigned char)BodyStr[i + 1]) && - BodyStr[i + 2] == ':' && BodyStr[i + 3] == '/') { - char dupDrive = BodyStr[i + 1]; - if (std::tolower((unsigned char)dupDrive) == - std::tolower((unsigned char)mntDrive)) { - // Replace the whole prefix up to the duplicated drive so the - // body becomes "/C:/..." (prefer the embedded Windows path). This - // avoids duplicating path segments when later mapping. - BodyStr = std::string("/") + BodyStr.substr(i + 1); - break; - } + for (size_t i = 0; i + 2 < BodyStr.size(); ++i) { + if (std::isalpha((unsigned char)BodyStr[i]) && BodyStr[i + 1] == ':' && + BodyStr[i + 2] == '/') { + // Found an embedded drive at BodyStr[i]. Normalize to start at that + // drive (prepend a leading '/') so the body becomes "/C:/...". + BodyStr = std::string("/") + BodyStr.substr(i); + break; } } } >From 4017775ca9f5684d8f430baf52e99df61b31510b Mon Sep 17 00:00:00 2001 From: alexandre-charlot_qonto <alexandre.char...@qonto.com> Date: Sun, 24 Aug 2025 21:04:27 +0200 Subject: [PATCH 10/14] PathMapping: for ClientToServer, normalize embedded Windows drive and return normalized URI without additional mapping If client sends /mnt/.../C:/..., prefer and return /C:/... directly. Co-authored-by: openhands <openha...@all-hands.dev> --- clang-tools-extra/clangd/PathMapping.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index afae00ee2d7f1..860aab7bf0cda 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -59,6 +59,17 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, } } + // If we've normalized to a Windows-style drive (e.g. "/C:/...") for an incoming + // client->server URI, return the normalized Windows path directly to avoid + // trying to map from the original /mnt/ prefix which no longer applies. + if (Dir == PathMapping::Direction::ClientToServer) { + if (BodyStr.size() >= 3 && BodyStr[0] == '/' && + std::isalpha((unsigned char)BodyStr[1]) && BodyStr[2] == ':') { + vlog("PathMapping: normalized client->server Windows body={0}", BodyStr); + 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 >From b7ab8c9d43f97c54437ff13072118bb3c996724c Mon Sep 17 00:00:00 2001 From: alexandre-charlot_qonto <alexandre.char...@qonto.com> Date: Sun, 24 Aug 2025 21:29:54 +0200 Subject: [PATCH 11/14] PathMapping: only short-circuit returning normalized /C:/... for bodies that we explicitly preferred from /mnt/ embedded drive This preserves mapping behavior for ordinary incoming Windows URIs. Co-authored-by: openhands <openha...@all-hands.dev> --- clang-tools-extra/clangd/PathMapping.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index 860aab7bf0cda..3db9a0c622abe 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -47,6 +47,7 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, // "/mnt/c/.../C:/Users/..." to "/C:/Users/..." by chopping everything // before the embedded drive. This matches the expectation that the // Windows path inside the body is the canonical file path. + 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] == ':' && @@ -54,15 +55,18 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, // Found an embedded drive at BodyStr[i]. Normalize to start at that // drive (prepend a leading '/') so the body becomes "/C:/...". BodyStr = std::string("/") + BodyStr.substr(i); + DidPreferEmbeddedDrive = true; break; } } } // If we've normalized to a Windows-style drive (e.g. "/C:/...") for an incoming - // client->server URI, return the normalized Windows path directly to avoid - // trying to map from the original /mnt/ prefix which no longer applies. - if (Dir == PathMapping::Direction::ClientToServer) { + // client->server URI that came from an original /mnt/... path, return the + // normalized Windows path directly to avoid trying to map from the original + // /mnt/ prefix which no longer applies. Do not short-circuit normal incoming + // Windows URIs that should still be subject to mappings. + if (Dir == PathMapping::Direction::ClientToServer && DidPreferEmbeddedDrive) { if (BodyStr.size() >= 3 && BodyStr[0] == '/' && std::isalpha((unsigned char)BodyStr[1]) && BodyStr[2] == ':') { vlog("PathMapping: normalized client->server Windows body={0}", BodyStr); >From fdba14f99b78f9d1bf0ae8f4ac6ae72260908b9b Mon Sep 17 00:00:00 2001 From: openhands <openha...@all-hands.dev> Date: Sun, 24 Aug 2025 22:12:19 +0200 Subject: [PATCH 12/14] PathMapping: remove verbose vlog instrumentation and inline comments for cleaner output before PR --- clang-tools-extra/clangd/PathMapping.cpp | 26 ------------------------ 1 file changed, 26 deletions(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index 3db9a0c622abe..12eb08a9cdd05 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -37,9 +37,6 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, // Convert backslashes to forward slashes. std::replace(BodyStr.begin(), BodyStr.end(), '\\', '/'); - // Diagnostic logging to help debug mapping failures. This will show the - // normalized body and each mapping attempted. - vlog("PathMapping: doPathMapping S={0} normalized_body={1}", S, BodyStr); // If path starts with /mnt/<drive>/ and later contains an embedded // Windows drive fragment like "/C:/", prefer the embedded Windows path. @@ -69,7 +66,6 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, if (Dir == PathMapping::Direction::ClientToServer && DidPreferEmbeddedDrive) { if (BodyStr.size() >= 3 && BodyStr[0] == '/' && std::isalpha((unsigned char)BodyStr[1]) && BodyStr[2] == ':') { - vlog("PathMapping: normalized client->server Windows body={0}", BodyStr); return URI((*Uri).scheme(), (*Uri).authority(), BodyStr).toString(); } } @@ -82,7 +78,6 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, const std::string &To = Dir == PathMapping::Direction::ClientToServer ? Mapping.ServerPath : Mapping.ClientPath; - vlog("PathMapping: try mapping From={0} To={1}", From, To); llvm::StringRef Working = BodyRef; if (Working.consume_front(From)) { // Accept the match if either the mapping 'From' ends with a slash (explicit @@ -113,7 +108,6 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, } } std::string MappedBody = (To + Adjusted).str(); - vlog("PathMapping: matched From={0} To={1} => MappedBody={2}", From, To, MappedBody); return URI((*Uri).scheme(), (*Uri).authority(), MappedBody).toString(); } } @@ -135,14 +129,12 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, // If Rest begins with '/' or is empty, this is a reasonable match. if (Rest.empty() || Rest.front() == '/') { std::string MappedBody = (To + Rest).str(); - vlog("PathMapping: fallback matched From={0} To={1} => MappedBody={2}", From, To, MappedBody); return URI((*Uri).scheme(), (*Uri).authority(), MappedBody).toString(); } } } } - vlog("PathMapping: no mapping matched for body={0}", BodyStr); return std::nullopt; } @@ -185,30 +177,21 @@ class PathMappingMessageHandler : public Transport::MessageHandler { : WrappedHandler(Handler), Mappings(Mappings) {} bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override { - vlog("PathMapping: inbound onNotify Method={0}", Method); - vlog("PathMapping: inbound Params before={0}", Params); applyPathMappings(Params, PathMapping::Direction::ClientToServer, Mappings); - vlog("PathMapping: inbound Params after={0}", Params); return WrappedHandler.onNotify(Method, std::move(Params)); } bool onCall(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override { - vlog("PathMapping: inbound onCall Method={0}", Method); - vlog("PathMapping: inbound Params before={0}", Params); applyPathMappings(Params, PathMapping::Direction::ClientToServer, Mappings); - vlog("PathMapping: inbound Params after={0}", Params); return WrappedHandler.onCall(Method, std::move(Params), std::move(ID)); } bool onReply(llvm::json::Value ID, llvm::Expected<llvm::json::Value> Result) override { - vlog("PathMapping: inbound onReply ID={0}", ID); if (Result) { - vlog("PathMapping: inbound Result before={0}", *Result); applyPathMappings(*Result, PathMapping::Direction::ClientToServer, Mappings); - vlog("PathMapping: inbound Result after={0}", *Result); } return WrappedHandler.onReply(std::move(ID), std::move(Result)); } @@ -226,30 +209,21 @@ class PathMappingTransport : public Transport { : WrappedTransport(std::move(Transp)), Mappings(std::move(Mappings)) {} void notify(llvm::StringRef Method, llvm::json::Value Params) override { - vlog("PathMapping: outbound notify Method={0}", Method); - vlog("PathMapping: outbound Params before={0}", Params); applyPathMappings(Params, PathMapping::Direction::ServerToClient, Mappings); - vlog("PathMapping: outbound Params after={0}", Params); WrappedTransport->notify(Method, std::move(Params)); } void call(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override { - vlog("PathMapping: outbound call Method={0}", Method); - vlog("PathMapping: outbound Params before={0}", Params); applyPathMappings(Params, PathMapping::Direction::ServerToClient, Mappings); - vlog("PathMapping: outbound Params after={0}", Params); WrappedTransport->call(Method, std::move(Params), std::move(ID)); } void reply(llvm::json::Value ID, llvm::Expected<llvm::json::Value> Result) override { - vlog("PathMapping: outbound reply ID={0}", ID); if (Result) { - vlog("PathMapping: outbound Result before={0}", *Result); applyPathMappings(*Result, PathMapping::Direction::ServerToClient, Mappings); - vlog("PathMapping: outbound Result after={0}", *Result); } WrappedTransport->reply(std::move(ID), std::move(Result)); } >From 5a39f260bd15269f71e2189307917ff529962fe3 Mon Sep 17 00:00:00 2001 From: openhands <openha...@all-hands.dev> Date: Sun, 24 Aug 2025 22:15:27 +0200 Subject: [PATCH 13/14] PathMapping: remove assistant-added comments --- clang-tools-extra/clangd/PathMapping.cpp | 88 ++++++++++++------------ 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index 12eb08a9cdd05..a0242122a1578 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -21,8 +21,8 @@ namespace clangd { std::optional<std::string> doPathMapping(llvm::StringRef S, PathMapping::Direction Dir, const PathMappings &Mappings) { - // Return early to optimize for the common case, wherein S is not a file URI - if (!S.starts_with("file://")) + + if (!S.starts_with("file: return std::nullopt; auto Uri = URI::parse(S); if (!Uri) { @@ -30,27 +30,27 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, return std::nullopt; } - // Normalize the URI body to handle cases where a client mixes WSL and - // Windows paths (e.g. "/mnt/c/.../C:/..."), or uses backslashes. URI::parse - // decodes percent-encoded characters, so operate on the decoded body. + + + std::string BodyStr = (*Uri).body().str(); - // Convert backslashes to forward slashes. + std::replace(BodyStr.begin(), BodyStr.end(), '\\', '/'); - // If path starts with /mnt/<drive>/ and later contains an embedded - // Windows drive fragment like "/C:/", prefer the embedded Windows path. - // For incoming client->server URIs, normalize bodies like - // "/mnt/c/.../C:/Users/..." to "/C:/Users/..." by chopping everything - // before the embedded drive. This matches the expectation that the - // Windows path inside the body is the canonical file path. + + + + + + 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] == '/') { - // Found an embedded drive at BodyStr[i]. Normalize to start at that - // drive (prepend a leading '/') so the body becomes "/C:/...". + + BodyStr = std::string("/") + BodyStr.substr(i); DidPreferEmbeddedDrive = true; break; @@ -58,11 +58,11 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, } } - // If we've normalized to a Windows-style drive (e.g. "/C:/...") for an incoming - // client->server URI that came from an original /mnt/... path, return the - // normalized Windows path directly to avoid trying to map from the original - // /mnt/ prefix which no longer applies. Do not short-circuit normal incoming - // Windows URIs that should still be subject to mappings. + + + + + if (Dir == PathMapping::Direction::ClientToServer && DidPreferEmbeddedDrive) { if (BodyStr.size() >= 3 && BodyStr[0] == '/' && std::isalpha((unsigned char)BodyStr[1]) && BodyStr[2] == ':') { @@ -80,17 +80,17 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, : Mapping.ClientPath; llvm::StringRef Working = BodyRef; if (Working.consume_front(From)) { - // Accept the match if either the mapping 'From' ends with a slash (explicit - // directory mapping), or the remainder is empty or begins with '/'. This - // handles cases like From="/C:/" matching Body="/C:/Users/..." where - // consuming From leaves "Users/..." (which does not start with '/'). + + + + if (From.empty() || From.back() == '/' || Working.empty() || Working.front() == '/') { - // If the remainder contains a duplicated Windows drive fragment (e.g. - // "projects/.../C:/Users/..."), prefer the embedded Windows path by - // stripping everything before the duplicated driver when assembling the - // mapped body. This avoids producing "/C:/projects/.../C:/...". + + + + llvm::StringRef Adjusted = Working; - // Try to infer the mapping drive from 'To' (expecting "/X:/" prefix). + char MappingDrive = 0; if (To.size() >= 3 && To[0] == '/' && std::isalpha((unsigned char)To[1]) && To[2] == ':') MappingDrive = To[1]; @@ -99,8 +99,8 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, 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)) { - // Strip everything up to and including the duplicated "X:/" so - // that appending to To yields "/X:/<rest>" without duplication. + + Adjusted = Working.substr(i + 3); break; } @@ -113,9 +113,9 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, } } - // Fallback: sometimes the body starts with a Server-style drive like "/C:/..." - // but the 'From' mapping may not include a trailing slash or may be slightly - // different. Try a relaxed matching pass for Server->Client direction. + + + if (Dir == PathMapping::Direction::ServerToClient) { for (const auto &Mapping : Mappings) { const std::string &From = Mapping.ServerPath; @@ -123,10 +123,10 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, if (From.empty()) continue; llvm::StringRef W = BodyRef; - // If the body starts with From (allowing missing trailing slash), accept it. + if (W.starts_with(From)) { llvm::StringRef Rest = W.substr(From.size()); - // If Rest begins with '/' or is empty, this is a reasonable match. + if (Rest.empty() || Rest.front() == '/') { std::string MappedBody = (To + Rest).str(); return URI((*Uri).scheme(), (*Uri).authority(), MappedBody).toString(); @@ -145,7 +145,7 @@ void applyPathMappings(llvm::json::Value &V, PathMapping::Direction Dir, if (K == Kind::Object) { llvm::json::Object *Obj = V.getAsObject(); llvm::json::Object MappedObj; - // 1. Map all the Keys + for (auto &KV : *Obj) { if (std::optional<std::string> MappedKey = doPathMapping(KV.first.str(), Dir, Mappings)) { @@ -155,7 +155,7 @@ void applyPathMappings(llvm::json::Value &V, PathMapping::Direction Dir, } } *Obj = std::move(MappedObj); - // 2. Map all the values + for (auto &KV : *Obj) applyPathMappings(KV.second, Dir, Mappings); } else if (K == Kind::Array) { @@ -201,8 +201,8 @@ class PathMappingMessageHandler : public Transport::MessageHandler { const PathMappings &Mappings; }; -// Apply path mappings to all LSP messages by intercepting all params/results -// and then delegating to the normal transport + + class PathMappingTransport : public Transport { public: PathMappingTransport(std::unique_ptr<Transport> Transp, PathMappings Mappings) @@ -238,8 +238,8 @@ class PathMappingTransport : public Transport { PathMappings Mappings; }; -// Converts a unix/windows path to the path portion of a file URI -// e.g. "C:\foo" -> "/C:/foo" + + llvm::Expected<std::string> parsePath(llvm::StringRef Path) { namespace path = llvm::sys::path; if (path::is_absolute(Path, path::Style::posix)) { @@ -254,7 +254,7 @@ llvm::Expected<std::string> parsePath(llvm::StringRef Path) { return error("Path not absolute: {0}", Path); } -} // namespace +} llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PathMapping &M) { return OS << M.ClientPath << "=" << M.ServerPath; @@ -287,5 +287,5 @@ createPathMappingTransport(std::unique_ptr<Transport> Transp, return std::make_unique<PathMappingTransport>(std::move(Transp), Mappings); } -} // namespace clangd -} // namespace clang +} +} >From 14bd5780aec547761e6111709b4484709703ec26 Mon Sep 17 00:00:00 2001 From: openhands <openha...@all-hands.dev> Date: Sun, 24 Aug 2025 22:21:18 +0200 Subject: [PATCH 14/14] Restore PathMapping.cpp to pre-comment-strip state --- clang-tools-extra/clangd/PathMapping.cpp | 80 ++++++++---------------- 1 file changed, 25 insertions(+), 55 deletions(-) diff --git a/clang-tools-extra/clangd/PathMapping.cpp b/clang-tools-extra/clangd/PathMapping.cpp index a0242122a1578..68cdf0e3f037e 100644 --- a/clang-tools-extra/clangd/PathMapping.cpp +++ b/clang-tools-extra/clangd/PathMapping.cpp @@ -12,17 +12,17 @@ #include "llvm/Support/Error.h" #include "llvm/Support/Path.h" #include <algorithm> +#include <cctype> #include <optional> #include <tuple> -#include <cctype> namespace clang { namespace clangd { std::optional<std::string> doPathMapping(llvm::StringRef S, PathMapping::Direction Dir, const PathMappings &Mappings) { - - if (!S.starts_with("file: + // Return early to optimize for the common case, wherein S is not a file URI + if (!S.starts_with("file://")) return std::nullopt; auto Uri = URI::parse(S); if (!Uri) { @@ -30,27 +30,14 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, 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; @@ -58,11 +45,6 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, } } - - - - - if (Dir == PathMapping::Direction::ClientToServer && DidPreferEmbeddedDrive) { if (BodyStr.size() >= 3 && BodyStr[0] == '/' && std::isalpha((unsigned char)BodyStr[1]) && BodyStr[2] == ':') { @@ -80,27 +62,21 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, : Mapping.ClientPath; llvm::StringRef Working = BodyRef; if (Working.consume_front(From)) { - - - - - if (From.empty() || From.back() == '/' || Working.empty() || Working.front() == '/') { - - - - + 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] == ':') + 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)) { - - + 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; } @@ -113,9 +89,6 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, } } - - - if (Dir == PathMapping::Direction::ServerToClient) { for (const auto &Mapping : Mappings) { const std::string &From = Mapping.ServerPath; @@ -123,13 +96,12 @@ std::optional<std::string> doPathMapping(llvm::StringRef S, 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 URI((*Uri).scheme(), (*Uri).authority(), MappedBody) + .toString(); } } } @@ -145,7 +117,7 @@ void applyPathMappings(llvm::json::Value &V, PathMapping::Direction Dir, if (K == Kind::Object) { llvm::json::Object *Obj = V.getAsObject(); llvm::json::Object MappedObj; - + // 1. Map all the Keys for (auto &KV : *Obj) { if (std::optional<std::string> MappedKey = doPathMapping(KV.first.str(), Dir, Mappings)) { @@ -155,7 +127,7 @@ void applyPathMappings(llvm::json::Value &V, PathMapping::Direction Dir, } } *Obj = std::move(MappedObj); - + // 2. Map all the values for (auto &KV : *Obj) applyPathMappings(KV.second, Dir, Mappings); } else if (K == Kind::Array) { @@ -189,10 +161,9 @@ class PathMappingMessageHandler : public Transport::MessageHandler { bool onReply(llvm::json::Value ID, llvm::Expected<llvm::json::Value> Result) override { - if (Result) { + if (Result) applyPathMappings(*Result, PathMapping::Direction::ClientToServer, Mappings); - } return WrappedHandler.onReply(std::move(ID), std::move(Result)); } @@ -201,8 +172,8 @@ class PathMappingMessageHandler : public Transport::MessageHandler { const PathMappings &Mappings; }; - - +// Apply path mappings to all LSP messages by intercepting all params/results +// and then delegating to the normal transport class PathMappingTransport : public Transport { public: PathMappingTransport(std::unique_ptr<Transport> Transp, PathMappings Mappings) @@ -221,10 +192,9 @@ class PathMappingTransport : public Transport { void reply(llvm::json::Value ID, llvm::Expected<llvm::json::Value> Result) override { - if (Result) { + if (Result) applyPathMappings(*Result, PathMapping::Direction::ServerToClient, Mappings); - } WrappedTransport->reply(std::move(ID), std::move(Result)); } @@ -238,8 +208,8 @@ class PathMappingTransport : public Transport { PathMappings Mappings; }; - - +// Converts a unix/windows path to the path portion of a file URI +// e.g. "C:\foo" -> "/C:/foo" llvm::Expected<std::string> parsePath(llvm::StringRef Path) { namespace path = llvm::sys::path; if (path::is_absolute(Path, path::Style::posix)) { @@ -254,7 +224,7 @@ llvm::Expected<std::string> parsePath(llvm::StringRef Path) { return error("Path not absolute: {0}", Path); } -} +} // namespace llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PathMapping &M) { return OS << M.ClientPath << "=" << M.ServerPath; @@ -287,5 +257,5 @@ createPathMappingTransport(std::unique_ptr<Transport> Transp, return std::make_unique<PathMappingTransport>(std::move(Transp), Mappings); } -} -} +} // namespace clangd +} // namespace clang _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits