[clang-tools-extra] [clangd] Turn `Path` and `PathRef` into classes (PR #136439)

via cfe-commits cfe-commits at lists.llvm.org
Sat Apr 19 10:50:40 PDT 2025


https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/136439

>From 9fc8237e136176ed60981e3c28a991bf00fe4dbc Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Sat, 19 Apr 2025 18:30:38 +0200
Subject: [PATCH] [clangd] Turn `Path` and `PathRef` into classes

---
 clang-tools-extra/clangd/ASTSignals.cpp       |   2 +-
 clang-tools-extra/clangd/ClangdLSPServer.cpp  |   6 +-
 clang-tools-extra/clangd/ClangdServer.cpp     |  72 ++++---
 clang-tools-extra/clangd/CodeComplete.cpp     |  32 +--
 clang-tools-extra/clangd/ConfigCompile.cpp    |   5 +-
 clang-tools-extra/clangd/ConfigProvider.cpp   |   9 +-
 clang-tools-extra/clangd/DraftStore.cpp       |   6 +-
 clang-tools-extra/clangd/FS.cpp               |   6 -
 clang-tools-extra/clangd/FS.h                 |   8 -
 .../clangd/GlobalCompilationDatabase.cpp      |  50 ++---
 .../clangd/HeaderSourceSwitch.cpp             |  24 ++-
 clang-tools-extra/clangd/Headers.cpp          |   6 +-
 clang-tools-extra/clangd/Hover.cpp            |   2 +-
 clang-tools-extra/clangd/IncludeCleaner.cpp   |  16 +-
 clang-tools-extra/clangd/ModulesBuilder.cpp   |  14 +-
 clang-tools-extra/clangd/ParsedAST.cpp        |   5 +-
 clang-tools-extra/clangd/Preamble.cpp         |   8 +-
 clang-tools-extra/clangd/Protocol.cpp         |  10 +-
 clang-tools-extra/clangd/Protocol.h           |   4 +-
 .../clangd/ScanningProjectModules.cpp         |   8 +-
 clang-tools-extra/clangd/TUScheduler.cpp      |  60 +++---
 clang-tools-extra/clangd/TidyProvider.cpp     |  14 +-
 clang-tools-extra/clangd/XRefs.cpp            |  29 +--
 clang-tools-extra/clangd/index/Background.cpp |  14 +-
 clang-tools-extra/clangd/index/Background.h   |   1 +
 .../clangd/index/BackgroundIndexLoader.cpp    |  16 +-
 .../clangd/index/BackgroundIndexLoader.h      |   2 +-
 .../clangd/index/BackgroundIndexStorage.cpp   |   4 +-
 clang-tools-extra/clangd/index/FileIndex.cpp  |   4 +-
 clang-tools-extra/clangd/refactor/Rename.cpp  |   5 +-
 clang-tools-extra/clangd/refactor/Tweak.cpp   |   2 +-
 clang-tools-extra/clangd/refactor/Tweak.h     |   3 +-
 .../clangd/refactor/tweaks/DefineInline.cpp   |   5 +-
 .../clangd/refactor/tweaks/DefineOutline.cpp  |  17 +-
 .../clangd/support/FileCache.cpp              |   6 +-
 clang-tools-extra/clangd/support/Path.cpp     | 146 +++++++++++--
 clang-tools-extra/clangd/support/Path.h       | 202 ++++++++++++++++--
 .../clangd/support/ThreadsafeFS.cpp           |   2 +-
 clang-tools-extra/clangd/tool/ClangdMain.cpp  |  16 +-
 .../clangd/unittests/ASTTests.cpp             |   2 +-
 .../clangd/unittests/BackgroundIndexTests.cpp |  43 ++--
 .../clangd/unittests/ClangdTests.cpp          |  16 +-
 .../clangd/unittests/CodeCompleteTests.cpp    |   2 +-
 .../GlobalCompilationDatabaseTests.cpp        |  10 +-
 .../unittests/HeaderSourceSwitchTests.cpp     |  14 +-
 .../clangd/unittests/HeadersTests.cpp         |   2 +-
 .../clangd/unittests/PreambleTests.cpp        |  10 +-
 .../clangd/unittests/TUSchedulerTests.cpp     |   6 +-
 clang-tools-extra/clangd/unittests/TestFS.cpp |  21 +-
 .../clangd/unittests/support/PathTests.cpp    |  20 +-
 50 files changed, 625 insertions(+), 362 deletions(-)

diff --git a/clang-tools-extra/clangd/ASTSignals.cpp b/clang-tools-extra/clangd/ASTSignals.cpp
index cffadb091d557..21647994d259e 100644
--- a/clang-tools-extra/clangd/ASTSignals.cpp
+++ b/clang-tools-extra/clangd/ASTSignals.cpp
@@ -19,7 +19,7 @@ ASTSignals ASTSignals::derive(const ParsedAST &AST) {
   trace::Span Span("ASTSignals::derive");
   ASTSignals Signals;
   Signals.InsertionDirective = preferredIncludeDirective(
-      AST.tuPath(), AST.getLangOpts(),
+      AST.tuPath().raw(), AST.getLangOpts(),
       AST.getIncludeStructure().MainFileIncludes, AST.getLocalTopLevelDecls());
   const SourceManager &SM = AST.getSourceManager();
   findExplicitReferences(
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index 1e981825c7c15..35ada432cd5dd 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -926,11 +926,11 @@ void ClangdLSPServer::onDocumentDidClose(
 
   {
     std::lock_guard<std::mutex> Lock(DiagRefMutex);
-    DiagRefMap.erase(File);
+    DiagRefMap.erase(File.raw());
   }
   {
     std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
-    LastSemanticTokens.erase(File);
+    LastSemanticTokens.erase(File.raw());
   }
   // clangd will not send updates for this file anymore, so we empty out the
   // list of diagnostics shown on the client (e.g. in the "Problems" pane of
@@ -1816,7 +1816,7 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version,
   // Cache DiagRefMap
   {
     std::lock_guard<std::mutex> Lock(DiagRefMutex);
-    DiagRefMap[File] = LocalDiagMap;
+    DiagRefMap[File.raw()] = LocalDiagMap;
   }
 
   // Send a notification to the LSP client.
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index 52844129834c3..8ae5aa79674f2 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -88,15 +88,15 @@ struct UpdateIndexCallbacks : public ParsingCallbacks {
       indexStdlib(CI, std::move(*Loc));
 
     // FIndex outlives the UpdateIndexCallbacks.
-    auto Task = [FIndex(FIndex), Path(Path.str()), Version(Version.str()),
+    auto Task = [FIndex(FIndex), Path(Path.owned()), Version(Version.str()),
                  ASTCtx(std::move(ASTCtx)), PI(std::move(PI))]() mutable {
       trace::Span Tracer("PreambleIndexing");
-      FIndex->updatePreamble(Path, Version, ASTCtx.getASTContext(),
+      FIndex->updatePreamble(Path.raw(), Version, ASTCtx.getASTContext(),
                              ASTCtx.getPreprocessor(), *PI);
     };
 
     if (Tasks) {
-      Tasks->runAsync("Preamble indexing for:" + Path + Version,
+      Tasks->runAsync("Preamble indexing for:" + Path.raw() + Version,
                       std::move(Task));
     } else
       Task();
@@ -262,7 +262,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
     BackgroundIdx = std::make_unique<BackgroundIndex>(
         TFS, CDB,
         BackgroundIndexStorage::createDiskBackedStorageFactory(
-            [&CDB](llvm::StringRef File) { return CDB.getProjectInfo(File); }),
+            [&CDB](PathRef File) { return CDB.getProjectInfo(File); }),
         std::move(BGOpts));
     AddIndex(BackgroundIdx.get());
   }
@@ -316,14 +316,14 @@ void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents,
   bool NewFile = WorkScheduler->update(File, Inputs, WantDiags);
   // If we loaded Foo.h, we want to make sure Foo.cpp is indexed.
   if (NewFile && BackgroundIdx)
-    BackgroundIdx->boostRelated(File);
+    BackgroundIdx->boostRelated(File.raw());
 }
 
 void ClangdServer::reparseOpenFilesIfNeeded(
     llvm::function_ref<bool(llvm::StringRef File)> Filter) {
   // Reparse only opened files that were modified.
   for (const Path &FilePath : DraftMgr.getActiveFiles())
-    if (Filter(FilePath))
+    if (Filter(FilePath.raw()))
       if (auto Draft = DraftMgr.getDraft(FilePath)) // else disappeared in race?
         addDocument(FilePath, *Draft->Contents, Draft->Version,
                     WantDiagnostics::Auto);
@@ -340,7 +340,7 @@ std::function<Context(PathRef)>
 ClangdServer::createConfiguredContextProvider(const config::Provider *Provider,
                                               Callbacks *Publish) {
   if (!Provider)
-    return [](llvm::StringRef) { return Context::current().clone(); };
+    return [](PathRef) { return Context::current().clone(); };
 
   struct Impl {
     const config::Provider *Provider;
@@ -408,8 +408,8 @@ ClangdServer::createConfiguredContextProvider(const config::Provider *Provider,
   };
 
   // Copyable wrapper.
-  return [I(std::make_shared<Impl>(Provider, Publish))](llvm::StringRef Path) {
-    return (*I)(Path);
+  return [I(std::make_shared<Impl>(Provider, Publish))](PathRef Path) {
+    return (*I)(Path.raw());
   };
 }
 
@@ -426,7 +426,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos,
   if (!CodeCompleteOpts.Index) // Respect overridden index.
     CodeCompleteOpts.Index = Index;
 
-  auto Task = [Pos, CodeCompleteOpts, File = File.str(), CB = std::move(CB),
+  auto Task = [Pos, CodeCompleteOpts, File = File.owned(), CB = std::move(CB),
                this](llvm::Expected<InputsAndPreamble> IP) mutable {
     if (!IP)
       return CB(IP.takeError());
@@ -442,7 +442,8 @@ void ClangdServer::codeComplete(PathRef File, Position Pos,
       SpecFuzzyFind.emplace();
       {
         std::lock_guard<std::mutex> Lock(CachedCompletionFuzzyFindRequestMutex);
-        SpecFuzzyFind->CachedReq = CachedCompletionFuzzyFindRequestByFile[File];
+        SpecFuzzyFind->CachedReq =
+            CachedCompletionFuzzyFindRequestByFile[File.raw()];
       }
     }
     ParseInputs ParseInput{IP->Command, &getHeaderFS(), IP->Contents.str()};
@@ -473,7 +474,8 @@ void ClangdServer::codeComplete(PathRef File, Position Pos,
       return;
     if (SpecFuzzyFind->NewReq) {
       std::lock_guard<std::mutex> Lock(CachedCompletionFuzzyFindRequestMutex);
-      CachedCompletionFuzzyFindRequestByFile[File] = *SpecFuzzyFind->NewReq;
+      CachedCompletionFuzzyFindRequestByFile[File.raw()] =
+          *SpecFuzzyFind->NewReq;
     }
     // Explicitly block until async task completes, this is fine as we've
     // already provided reply to the client and running as a preamble task
@@ -495,7 +497,7 @@ void ClangdServer::signatureHelp(PathRef File, Position Pos,
                                  MarkupKind DocumentationFormat,
                                  Callback<SignatureHelp> CB) {
 
-  auto Action = [Pos, File = File.str(), CB = std::move(CB),
+  auto Action = [Pos, File = File.owned(), CB = std::move(CB),
                  DocumentationFormat,
                  this](llvm::Expected<InputsAndPreamble> IP) mutable {
     if (!IP)
@@ -540,12 +542,13 @@ void ClangdServer::formatFile(PathRef File, std::optional<Range> Rng,
   }
 
   // Call clang-format.
-  auto Action = [File = File.str(), Code = std::move(*Code),
+  auto Action = [File = File.owned(), Code = std::move(*Code),
                  Ranges = std::vector<tooling::Range>{RequestedRange},
                  CB = std::move(CB), this]() mutable {
-    format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS, true);
+    format::FormatStyle Style =
+        getFormatStyleForFile(File.raw(), Code, TFS, true);
     tooling::Replacements IncludeReplaces =
-        format::sortIncludes(Style, Code, Ranges, File);
+        format::sortIncludes(Style, Code, Ranges, File.raw());
     auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces);
     if (!Changed)
       return CB(Changed.takeError());
@@ -553,9 +556,9 @@ void ClangdServer::formatFile(PathRef File, std::optional<Range> Rng,
     CB(IncludeReplaces.merge(format::reformat(
         Style, *Changed,
         tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges),
-        File)));
+        File.raw())));
   };
-  WorkScheduler->runQuick("Format", File, std::move(Action));
+  WorkScheduler->runQuick("Format", File.raw(), std::move(Action));
 }
 
 void ClangdServer::formatOnType(PathRef File, Position Pos,
@@ -568,24 +571,24 @@ void ClangdServer::formatOnType(PathRef File, Position Pos,
   llvm::Expected<size_t> CursorPos = positionToOffset(*Code, Pos);
   if (!CursorPos)
     return CB(CursorPos.takeError());
-  auto Action = [File = File.str(), Code = std::move(*Code),
+  auto Action = [File = File.owned(), Code = std::move(*Code),
                  TriggerText = TriggerText.str(), CursorPos = *CursorPos,
                  CB = std::move(CB), this]() mutable {
-    auto Style = getFormatStyleForFile(File, Code, TFS, false);
+    auto Style = getFormatStyleForFile(File.raw(), Code, TFS, false);
     std::vector<TextEdit> Result;
     for (const tooling::Replacement &R :
          formatIncremental(Code, CursorPos, TriggerText, Style))
       Result.push_back(replacementToEdit(Code, R));
     return CB(Result);
   };
-  WorkScheduler->runQuick("FormatOnType", File, std::move(Action));
+  WorkScheduler->runQuick("FormatOnType", File.raw(), std::move(Action));
 }
 
 void ClangdServer::prepareRename(PathRef File, Position Pos,
                                  std::optional<std::string> NewName,
                                  const RenameOptions &RenameOpts,
                                  Callback<RenameResult> CB) {
-  auto Action = [Pos, File = File.str(), CB = std::move(CB),
+  auto Action = [Pos, File = File.owned(), CB = std::move(CB),
                  NewName = std::move(NewName),
                  RenameOpts](llvm::Expected<InputsAndAST> InpAST) mutable {
     if (!InpAST)
@@ -594,7 +597,7 @@ void ClangdServer::prepareRename(PathRef File, Position Pos,
     // only need main-file references
     auto Results =
         clangd::rename({Pos, NewName.value_or("__clangd_rename_placeholder"),
-                        InpAST->AST, File, /*FS=*/nullptr,
+                        InpAST->AST, File.raw(), /*FS=*/nullptr,
                         /*Index=*/nullptr, RenameOpts});
     if (!Results) {
       // LSP says to return null on failure, but that will result in a generic
@@ -610,7 +613,7 @@ void ClangdServer::prepareRename(PathRef File, Position Pos,
 void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName,
                           const RenameOptions &Opts,
                           Callback<RenameResult> CB) {
-  auto Action = [File = File.str(), NewName = NewName.str(), Pos, Opts,
+  auto Action = [File = File.owned(), NewName = NewName.str(), Pos, Opts,
                  CB = std::move(CB),
                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
     // Tracks number of files edited per invocation.
@@ -618,13 +621,13 @@ void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName,
                                                trace::Metric::Distribution);
     if (!InpAST)
       return CB(InpAST.takeError());
-    auto R = clangd::rename({Pos, NewName, InpAST->AST, File,
+    auto R = clangd::rename({Pos, NewName, InpAST->AST, File.raw(),
                              DirtyFS->view(std::nullopt), Index, Opts});
     if (!R)
       return CB(R.takeError());
 
     if (Opts.WantFormat) {
-      auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents,
+      auto Style = getFormatStyleForFile(File.raw(), InpAST->Inputs.Contents,
                                          *InpAST->Inputs.TFS, false);
       llvm::Error Err = llvm::Error::success();
       for (auto &E : R->GlobalChanges)
@@ -756,7 +759,7 @@ void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID,
   static constexpr trace::Metric TweakFailed(
       "tweak_failed", trace::Metric::Counter, "tweak_id");
   TweakAttempt.record(1, TweakID);
-  auto Action = [File = File.str(), Sel, TweakID = TweakID.str(),
+  auto Action = [File = File.owned(), Sel, TweakID = TweakID.str(),
                  CB = std::move(CB),
                  this](Expected<InputsAndAST> InpAST) mutable {
     if (!InpAST)
@@ -782,7 +785,7 @@ void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID,
       for (auto &It : (*Effect)->ApplyEdits) {
         Edit &E = It.second;
         format::FormatStyle Style =
-            getFormatStyleForFile(File, E.InitialCode, TFS, false);
+            getFormatStyleForFile(File.raw(), E.InitialCode, TFS, false);
         if (llvm::Error Err = reformatEdit(E, Style))
           elog("Failed to format {0}: {1}", It.first(), std::move(Err));
       }
@@ -817,7 +820,7 @@ void ClangdServer::switchSourceHeader(
   if (auto CorrespondingFile =
           getCorrespondingHeaderOrSource(Path, TFS.view(std::nullopt)))
     return CB(std::move(CorrespondingFile));
-  auto Action = [Path = Path.str(), CB = std::move(CB),
+  auto Action = [Path = Path.owned(), CB = std::move(CB),
                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
     if (!InpAST)
       return CB(InpAST.takeError());
@@ -840,12 +843,12 @@ void ClangdServer::findDocumentHighlights(
 
 void ClangdServer::findHover(PathRef File, Position Pos,
                              Callback<std::optional<HoverInfo>> CB) {
-  auto Action = [File = File.str(), Pos, CB = std::move(CB),
+  auto Action = [File = File.owned(), Pos, CB = std::move(CB),
                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
     if (!InpAST)
       return CB(InpAST.takeError());
     format::FormatStyle Style = getFormatStyleForFile(
-        File, InpAST->Inputs.Contents, *InpAST->Inputs.TFS, false);
+        File.raw(), InpAST->Inputs.Contents, *InpAST->Inputs.TFS, false);
     CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index));
   };
 
@@ -855,7 +858,8 @@ void ClangdServer::findHover(PathRef File, Position Pos,
 void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
                                  TypeHierarchyDirection Direction,
                                  Callback<std::vector<TypeHierarchyItem>> CB) {
-  auto Action = [File = File.str(), Pos, Resolve, Direction, CB = std::move(CB),
+  auto Action = [File = File.owned(), Pos, Resolve, Direction,
+                 CB = std::move(CB),
                  this](Expected<InputsAndAST> InpAST) mutable {
     if (!InpAST)
       return CB(InpAST.takeError());
@@ -894,7 +898,7 @@ void ClangdServer::resolveTypeHierarchy(
 
 void ClangdServer::prepareCallHierarchy(
     PathRef File, Position Pos, Callback<std::vector<CallHierarchyItem>> CB) {
-  auto Action = [File = File.str(), Pos,
+  auto Action = [File = File.owned(), Pos,
                  CB = std::move(CB)](Expected<InputsAndAST> InpAST) mutable {
     if (!InpAST)
       return CB(InpAST.takeError());
@@ -976,7 +980,7 @@ void ClangdServer::foldingRanges(llvm::StringRef File,
   WorkScheduler->runQuick("FoldingRanges", File, std::move(Action));
 }
 
-void ClangdServer::findType(llvm::StringRef File, Position Pos,
+void ClangdServer::findType(PathRef File, Position Pos,
                             Callback<std::vector<LocatedSymbol>> CB) {
   auto Action = [Pos, CB = std::move(CB),
                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 0eb196fbad46a..ebbcea5f67bbf 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -1384,14 +1384,14 @@ bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
   CI->getLangOpts().DelayedTemplateParsing = false;
   // Setup code completion.
   FrontendOpts.CodeCompleteOpts = Options;
-  FrontendOpts.CodeCompletionAt.FileName = std::string(Input.FileName);
+  FrontendOpts.CodeCompletionAt.FileName = Input.FileName.owned().raw();
   std::tie(FrontendOpts.CodeCompletionAt.Line,
            FrontendOpts.CodeCompletionAt.Column) =
       offsetToClangLineColumn(Input.ParseInput.Contents, Input.Offset);
 
   std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer =
       llvm::MemoryBuffer::getMemBuffer(Input.ParseInput.Contents,
-                                       Input.FileName);
+                                       Input.FileName.raw());
   // The diagnostic options must be set before creating a CompilerInstance.
   CI->getDiagnosticOpts().IgnoreWarnings = true;
   // We reuse the preamble whether it's valid or not. This is a
@@ -1649,7 +1649,7 @@ class CodeCompleteFlow {
       assert(Recorder && "Recorder is not set");
       CCContextKind = Recorder->CCContext.getKind();
       IsUsingDeclaration = Recorder->CCContext.isUsingDeclaration();
-      auto Style = getFormatStyleForFile(SemaCCInput.FileName,
+      auto Style = getFormatStyleForFile(SemaCCInput.FileName.raw(),
                                          SemaCCInput.ParseInput.Contents,
                                          *SemaCCInput.ParseInput.TFS, false);
       const auto NextToken = findTokenAfterCompletionPoint(
@@ -1660,7 +1660,7 @@ class CodeCompleteFlow {
       // If preprocessor was run, inclusions from preprocessor callback should
       // already be added to Includes.
       Inserter.emplace(
-          SemaCCInput.FileName, SemaCCInput.ParseInput.Contents, Style,
+          SemaCCInput.FileName.raw(), SemaCCInput.ParseInput.Contents, Style,
           SemaCCInput.ParseInput.CompileCommand.Directory,
           &Recorder->CCSema->getPreprocessor().getHeaderSearchInfo(),
           Config::current().Style.QuotedHeaders,
@@ -1741,12 +1741,12 @@ class CodeCompleteFlow {
     ReplacedRange.start.character -= HeuristicPrefix.Name.size();
 
     llvm::StringMap<SourceParams> ProxSources;
-    ProxSources[FileName].Cost = 0;
+    ProxSources[FileName.raw()].Cost = 0;
     FileProximity.emplace(ProxSources);
 
-    auto Style = getFormatStyleForFile(FileName, Content, TFS, false);
+    auto Style = getFormatStyleForFile(FileName.raw(), Content, TFS, false);
     // This will only insert verbatim headers.
-    Inserter.emplace(FileName, Content, Style,
+    Inserter.emplace(FileName.raw(), Content, Style,
                      /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr,
                      Config::current().Style.QuotedHeaders,
                      Config::current().Style.AngledHeaders);
@@ -1917,7 +1917,7 @@ class CodeCompleteFlow {
     Req.Scopes = QueryScopes;
     Req.AnyScope = AllScopes;
     // FIXME: we should send multiple weighted paths here.
-    Req.ProximityPaths.push_back(std::string(FileName));
+    Req.ProximityPaths.push_back(FileName.owned().raw());
     if (PreferredType)
       Req.PreferredTypes.push_back(std::string(PreferredType->raw()));
     vlog("Code complete: fuzzyFind({0:2})", toJSON(Req));
@@ -1972,8 +1972,9 @@ class CodeCompleteFlow {
         assert(IdentifierResult);
         C.Name = IdentifierResult->Name;
       }
-      if (auto OverloadSet = C.overloadSet(
-              Opts, FileName, Inserter ? &*Inserter : nullptr, CCContextKind)) {
+      if (auto OverloadSet =
+              C.overloadSet(Opts, FileName.raw(),
+                            Inserter ? &*Inserter : nullptr, CCContextKind)) {
         auto Ret = BundleLookup.try_emplace(OverloadSet, Bundles.size());
         if (Ret.second)
           Bundles.emplace_back();
@@ -2140,8 +2141,9 @@ class CodeCompleteFlow {
                           : nullptr;
       if (!Builder)
         Builder.emplace(Recorder ? &Recorder->CCSema->getASTContext() : nullptr,
-                        Item, SemaCCS, AccessibleScopes, *Inserter, FileName,
-                        CCContextKind, Opts, IsUsingDeclaration, NextTokenKind);
+                        Item, SemaCCS, AccessibleScopes, *Inserter,
+                        FileName.raw(), CCContextKind, Opts, IsUsingDeclaration,
+                        NextTokenKind);
       else
         Builder->add(Item, SemaCCS, CCContextKind);
     }
@@ -2214,7 +2216,7 @@ CodeCompleteResult codeCompleteComment(PathRef FileName, unsigned Offset,
   semaCodeComplete(
       std::make_unique<ParamNameCollector>(Options, ParamNames), Options,
       {FileName, Offset, *Preamble,
-       PreamblePatch::createFullPatch(FileName, ParseInput, *Preamble),
+       PreamblePatch::createFullPatch(FileName.raw(), ParseInput, *Preamble),
        ParseInput});
   if (ParamNames.empty())
     return CodeCompleteResult();
@@ -2288,7 +2290,7 @@ CodeCompleteResult codeComplete(PathRef FileName, Position Pos,
              : std::move(Flow).run({FileName, *Offset, *Preamble,
                                     /*PreamblePatch=*/
                                     PreamblePatch::createMacroPatch(
-                                        FileName, ParseInput, *Preamble),
+                                        FileName.raw(), ParseInput, *Preamble),
                                     ParseInput});
 }
 
@@ -2312,7 +2314,7 @@ SignatureHelp signatureHelp(PathRef FileName, Position Pos,
                                                ParseInput.Index, Result),
       Options,
       {FileName, *Offset, Preamble,
-       PreamblePatch::createFullPatch(FileName, ParseInput, Preamble),
+       PreamblePatch::createFullPatch(FileName.raw(), ParseInput, Preamble),
        ParseInput});
   return Result;
 }
diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp
index 13c2405e76df7..a2487c957a230 100644
--- a/clang-tools-extra/clangd/ConfigCompile.cpp
+++ b/clang-tools-extra/clangd/ConfigCompile.cpp
@@ -411,8 +411,9 @@ struct FragmentCompiler {
         C.Index.External = Spec;
         return;
       }
-      if (P.Path.empty() || !pathStartsWith(Spec.MountPoint, P.Path,
-                                            llvm::sys::path::Style::posix))
+      if (P.Path.empty() ||
+          !PathRef(Spec.MountPoint)
+               .startsWith(P.Path, llvm::sys::path::Style::posix))
         return;
       C.Index.External = Spec;
       // Disable background indexing for the files under the mountpoint.
diff --git a/clang-tools-extra/clangd/ConfigProvider.cpp b/clang-tools-extra/clangd/ConfigProvider.cpp
index ac437ee8b6eb1..24dacabd82b2a 100644
--- a/clang-tools-extra/clangd/ConfigProvider.cpp
+++ b/clang-tools-extra/clangd/ConfigProvider.cpp
@@ -43,7 +43,8 @@ class FileConfigCache : public FileCache {
         [&](std::optional<llvm::StringRef> Data) {
           CachedValue.clear();
           if (Data)
-            for (auto &Fragment : Fragment::parseYAML(*Data, path(), DC)) {
+            for (auto &Fragment :
+                 Fragment::parseYAML(*Data, path().raw(), DC)) {
               Fragment.Source.Directory = Directory;
               Fragment.Source.Trusted = Trusted;
               CachedValue.push_back(std::move(Fragment).compile(DC));
@@ -103,9 +104,9 @@ Provider::fromAncestorRelativeYAMLFiles(llvm::StringRef RelPath,
 
       // Compute absolute paths to all ancestors (substrings of P.Path).
       llvm::SmallVector<llvm::StringRef, 8> Ancestors;
-      for (auto Ancestor = absoluteParent(P.Path); !Ancestor.empty();
-           Ancestor = absoluteParent(Ancestor)) {
-        Ancestors.emplace_back(Ancestor);
+      for (auto Ancestor = PathRef(P.Path).absoluteParent(); !Ancestor.empty();
+           Ancestor = Ancestor.absoluteParent()) {
+        Ancestors.emplace_back(Ancestor.raw());
       }
       // Ensure corresponding cache entries exist in the map.
       llvm::SmallVector<FileConfigCache *, 8> Caches;
diff --git a/clang-tools-extra/clangd/DraftStore.cpp b/clang-tools-extra/clangd/DraftStore.cpp
index 66e45b0c04ce3..de84b693f449b 100644
--- a/clang-tools-extra/clangd/DraftStore.cpp
+++ b/clang-tools-extra/clangd/DraftStore.cpp
@@ -19,7 +19,7 @@ namespace clangd {
 std::optional<DraftStore::Draft> DraftStore::getDraft(PathRef File) const {
   std::lock_guard<std::mutex> Lock(Mutex);
 
-  auto It = Drafts.find(File);
+  auto It = Drafts.find(File.raw());
   if (It == Drafts.end())
     return std::nullopt;
 
@@ -76,7 +76,7 @@ std::string DraftStore::addDraft(PathRef File, llvm::StringRef Version,
                                  llvm::StringRef Contents) {
   std::lock_guard<std::mutex> Lock(Mutex);
 
-  auto &D = Drafts[File];
+  auto &D = Drafts[File.raw()];
   updateVersion(D.D, Version);
   std::time(&D.MTime);
   D.D.Contents = std::make_shared<std::string>(Contents);
@@ -86,7 +86,7 @@ std::string DraftStore::addDraft(PathRef File, llvm::StringRef Version,
 void DraftStore::removeDraft(PathRef File) {
   std::lock_guard<std::mutex> Lock(Mutex);
 
-  Drafts.erase(File);
+  Drafts.erase(File.raw());
 }
 
 namespace {
diff --git a/clang-tools-extra/clangd/FS.cpp b/clang-tools-extra/clangd/FS.cpp
index 5729b9341d9d4..cdf5fc6aceae0 100644
--- a/clang-tools-extra/clangd/FS.cpp
+++ b/clang-tools-extra/clangd/FS.cpp
@@ -113,11 +113,5 @@ PreambleFileStatusCache::getConsumingFS(
   return llvm::IntrusiveRefCntPtr<CacheVFS>(new CacheVFS(std::move(FS), *this));
 }
 
-Path removeDots(PathRef File) {
-  llvm::SmallString<128> CanonPath(File);
-  llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
-  return CanonPath.str().str();
-}
-
 } // namespace clangd
 } // namespace clang
diff --git a/clang-tools-extra/clangd/FS.h b/clang-tools-extra/clangd/FS.h
index 827b465aed983..4f9845bdf9111 100644
--- a/clang-tools-extra/clangd/FS.h
+++ b/clang-tools-extra/clangd/FS.h
@@ -9,7 +9,6 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H
 
-#include "support/Path.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/Support/VirtualFileSystem.h"
@@ -68,13 +67,6 @@ class PreambleFileStatusCache {
   llvm::StringMap<llvm::vfs::Status> StatCache;
 };
 
-/// Returns a version of \p File that doesn't contain dots and dot dots.
-/// e.g /a/b/../c -> /a/c
-///     /a/b/./c -> /a/b/c
-/// FIXME: We should avoid encountering such paths in clangd internals by
-/// filtering everything we get over LSP, CDB, etc.
-Path removeDots(PathRef File);
-
 } // namespace clangd
 } // namespace clang
 
diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
index 7c0eb9651feaa..8743344845125 100644
--- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
+++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
@@ -47,8 +47,8 @@ namespace {
 // deepest directory and going up to root. Stops whenever action succeeds.
 void actOnAllParentDirectories(PathRef FileName,
                                llvm::function_ref<bool(PathRef)> Action) {
-  for (auto Path = absoluteParent(FileName); !Path.empty() && !Action(Path);
-       Path = absoluteParent(Path))
+  for (auto Path = FileName.absoluteParent(); !Path.empty() && !Action(Path);
+       Path = Path.absoluteParent())
     ;
 }
 
@@ -60,12 +60,12 @@ GlobalCompilationDatabase::getFallbackCommand(PathRef File) const {
   // Clang treats .h files as C by default and files without extension as linker
   // input, resulting in unhelpful diagnostics.
   // Parsing as Objective C++ is friendly to more cases.
-  auto FileExtension = llvm::sys::path::extension(File);
+  auto FileExtension = File.extension();
   if (FileExtension.empty() || FileExtension == ".h")
     Argv.push_back("-xobjective-c++-header");
-  Argv.push_back(std::string(File));
-  tooling::CompileCommand Cmd(llvm::sys::path::parent_path(File),
-                              llvm::sys::path::filename(File), std::move(Argv),
+  Argv.push_back(File.owned().raw());
+  tooling::CompileCommand Cmd(File.parentPath().raw(), File.filename(),
+                              std::move(Argv),
                               /*Output=*/"");
   Cmd.Heuristic = "clangd fallback";
   return Cmd;
@@ -265,7 +265,7 @@ parseJSON(PathRef Path, llvm::StringRef Data, std::string &Error) {
 static std::unique_ptr<tooling::CompilationDatabase>
 parseFixed(PathRef Path, llvm::StringRef Data, std::string &Error) {
   return tooling::FixedCompilationDatabase::loadFromBuffer(
-      llvm::sys::path::parent_path(Path), Data, Error);
+      Path.parentPath().raw(), Data, Error);
 }
 
 bool DirectoryBasedGlobalCompilationDatabase::DirectoryCache::load(
@@ -374,7 +374,7 @@ DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const {
     return std::nullopt;
   }
 
-  auto Candidates = Res->CDB->getCompileCommands(File);
+  auto Candidates = Res->CDB->getCompileCommands(File.raw());
   if (!Candidates.empty())
     return std::move(Candidates.front());
 
@@ -388,10 +388,10 @@ DirectoryBasedGlobalCompilationDatabase::getDirectoryCaches(
   FoldedDirs.reserve(Dirs.size());
   for (const auto &Dir : Dirs) {
 #ifndef NDEBUG
-    if (!llvm::sys::path::is_absolute(Dir))
+    if (!PathRef(Dir).isAbsolute())
       elog("Trying to cache CDB for relative {0}");
 #endif
-    FoldedDirs.push_back(maybeCaseFoldPath(Dir));
+    FoldedDirs.push_back(PathRef(Dir).caseFolded().raw());
   }
 
   std::vector<DirectoryCache *> Ret;
@@ -406,15 +406,15 @@ DirectoryBasedGlobalCompilationDatabase::getDirectoryCaches(
 std::optional<DirectoryBasedGlobalCompilationDatabase::CDBLookupResult>
 DirectoryBasedGlobalCompilationDatabase::lookupCDB(
     CDBLookupRequest Request) const {
-  assert(llvm::sys::path::is_absolute(Request.FileName) &&
-         "path must be absolute");
+  assert(Request.FileName.isAbsolute() && "path must be absolute");
 
   std::string Storage;
   std::vector<llvm::StringRef> SearchDirs;
   if (Opts.CompileCommandsDir) // FIXME: unify this case with config.
-    SearchDirs = {*Opts.CompileCommandsDir};
+    SearchDirs = {Opts.CompileCommandsDir->raw()};
   else {
-    WithContext WithProvidedContext(Opts.ContextProvider(Request.FileName));
+    WithContext WithProvidedContext(
+        Opts.ContextProvider(Request.FileName.raw()));
     const auto &Spec = Config::current().CompileFlags.CDBSearch;
     switch (Spec.Policy) {
     case Config::CDBSearchSpec::NoCDBSearch:
@@ -427,9 +427,9 @@ DirectoryBasedGlobalCompilationDatabase::lookupCDB(
       // Traverse the canonical version to prevent false positives. i.e.:
       // src/build/../a.cc can detect a CDB in /src/build if not
       // canonicalized.
-      Storage = removeDots(Request.FileName);
-      actOnAllParentDirectories(Storage, [&](llvm::StringRef Dir) {
-        SearchDirs.push_back(Dir);
+      Storage = Request.FileName.removeDots().raw();
+      actOnAllParentDirectories(Storage, [&](PathRef Dir) {
+        SearchDirs.push_back(Dir.raw());
         return false;
       });
     }
@@ -581,8 +581,8 @@ class DirectoryBasedGlobalCompilationDatabase::BroadcastThread::Filter {
   DirInfo *addParents(llvm::StringRef FilePath) {
     DirInfo *Leaf = nullptr;
     DirInfo *Child = nullptr;
-    actOnAllParentDirectories(FilePath, [&](llvm::StringRef Dir) {
-      auto &Info = Dirs[Dir];
+    actOnAllParentDirectories(FilePath, [&](PathRef Dir) {
+      auto &Info = Dirs[Dir.raw()];
       // If this is the first iteration, then this node is the overall result.
       if (!Leaf)
         Leaf = &Info;
@@ -677,7 +677,7 @@ class DirectoryBasedGlobalCompilationDatabase::BroadcastThread::Filter {
     std::vector<SearchPath> SearchPaths(AllFiles.size());
     for (unsigned I = 0; I < AllFiles.size(); ++I) {
       if (Parent.Opts.CompileCommandsDir) { // FIXME: unify with config
-        SearchPaths[I].setPointer(&Dirs[*Parent.Opts.CompileCommandsDir]);
+        SearchPaths[I].setPointer(&Dirs[Parent.Opts.CompileCommandsDir->raw()]);
         continue;
       }
       if (ExitEarly()) // loading config may be slow
@@ -768,7 +768,7 @@ OverlayCDB::getCompileCommand(PathRef File) const {
   std::optional<tooling::CompileCommand> Cmd;
   {
     std::lock_guard<std::mutex> Lock(Mutex);
-    auto It = Commands.find(removeDots(File));
+    auto It = Commands.find(File.removeDots().raw());
     if (It != Commands.end())
       Cmd = It->second;
   }
@@ -793,7 +793,7 @@ OverlayCDB::getCompileCommand(PathRef File) const {
   if (!Cmd)
     return std::nullopt;
   if (Mangler)
-    Mangler(*Cmd, File);
+    Mangler(*Cmd, File.raw());
   return Cmd;
 }
 
@@ -803,7 +803,7 @@ tooling::CompileCommand OverlayCDB::getFallbackCommand(PathRef File) const {
   Cmd.CommandLine.insert(Cmd.CommandLine.end(), FallbackFlags.begin(),
                          FallbackFlags.end());
   if (Mangler)
-    Mangler(Cmd, File);
+    Mangler(Cmd, File.raw());
   return Cmd;
 }
 
@@ -812,7 +812,7 @@ bool OverlayCDB::setCompileCommand(PathRef File,
   // We store a canonical version internally to prevent mismatches between set
   // and get compile commands. Also it assures clients listening to broadcasts
   // doesn't receive different names for the same file.
-  std::string CanonPath = removeDots(File);
+  std::string CanonPath = File.removeDots().raw();
   {
     std::unique_lock<std::mutex> Lock(Mutex);
     if (Cmd) {
@@ -835,7 +835,7 @@ OverlayCDB::getProjectModules(PathRef File) const {
   auto MDB = DelegatingCDB::getProjectModules(File);
   MDB->setCommandMangler([&Mangler = Mangler](tooling::CompileCommand &Command,
                                               PathRef CommandPath) {
-    Mangler(Command, CommandPath);
+    Mangler(Command, CommandPath.raw());
   });
   return MDB;
 }
diff --git a/clang-tools-extra/clangd/HeaderSourceSwitch.cpp b/clang-tools-extra/clangd/HeaderSourceSwitch.cpp
index d54c3668570eb..4ea2fccda464e 100644
--- a/clang-tools-extra/clangd/HeaderSourceSwitch.cpp
+++ b/clang-tools-extra/clangd/HeaderSourceSwitch.cpp
@@ -26,16 +26,18 @@ std::optional<Path> getCorrespondingHeaderOrSource(
                                         ".inc",  ".cppm", ".ccm", ".cxxm",
                                         ".c++m", ".ixx"};
 
-  llvm::StringRef PathExt = llvm::sys::path::extension(OriginalFile);
+  llvm::StringRef PathExt = OriginalFile.extension();
 
   // Lookup in a list of known extensions.
-  bool IsSource = llvm::any_of(SourceExtensions, [&PathExt](PathRef SourceExt) {
-    return SourceExt.equals_insensitive(PathExt);
-  });
+  bool IsSource =
+      llvm::any_of(SourceExtensions, [&PathExt](llvm::StringRef SourceExt) {
+        return SourceExt.equals_insensitive(PathExt);
+      });
 
-  bool IsHeader = llvm::any_of(HeaderExtensions, [&PathExt](PathRef HeaderExt) {
-    return HeaderExt.equals_insensitive(PathExt);
-  });
+  bool IsHeader =
+      llvm::any_of(HeaderExtensions, [&PathExt](llvm::StringRef HeaderExt) {
+        return HeaderExt.equals_insensitive(PathExt);
+      });
 
   // We can only switch between the known extensions.
   if (!IsSource && !IsHeader)
@@ -50,7 +52,7 @@ std::optional<Path> getCorrespondingHeaderOrSource(
     NewExts = SourceExtensions;
 
   // Storage for the new path.
-  llvm::SmallString<128> NewPath = OriginalFile;
+  llvm::SmallString<128> NewPath = OriginalFile.raw();
 
   // Loop through switched extension candidates.
   for (llvm::StringRef NewExt : NewExts) {
@@ -81,8 +83,8 @@ std::optional<Path> getCorrespondingHeaderOrSource(PathRef OriginalFile,
   }
   llvm::StringMap<int> Candidates; // Target path => score.
   auto AwardTarget = [&](const char *TargetURI) {
-    if (auto TargetPath = URI::resolve(TargetURI, OriginalFile)) {
-      if (!pathEqual(*TargetPath, OriginalFile)) // exclude the original file.
+    if (auto TargetPath = URI::resolve(TargetURI, OriginalFile.raw())) {
+      if (PathRef(*TargetPath) != OriginalFile) // exclude the original file.
         ++Candidates[*TargetPath];
     } else {
       elog("Failed to resolve URI {0}: {1}", TargetURI, TargetPath.takeError());
@@ -94,7 +96,7 @@ std::optional<Path> getCorrespondingHeaderOrSource(PathRef OriginalFile,
   //
   // For each symbol in the original file, we get its target location (decl or
   // def) from the index, then award that target file.
-  bool IsHeader = isHeaderFile(OriginalFile, AST.getLangOpts());
+  bool IsHeader = isHeaderFile(OriginalFile.raw(), AST.getLangOpts());
   Index->lookup(Request, [&](const Symbol &Sym) {
     if (IsHeader)
       AwardTarget(Sym.Definition.FileURI);
diff --git a/clang-tools-extra/clangd/Headers.cpp b/clang-tools-extra/clangd/Headers.cpp
index 87fd261b906e6..58bf77a1c9285 100644
--- a/clang-tools-extra/clangd/Headers.cpp
+++ b/clang-tools-extra/clangd/Headers.cpp
@@ -263,7 +263,7 @@ IncludeStructure::mainFileIncludesWithSpelling(llvm::StringRef Spelling) const {
 void IncludeInserter::addExisting(const Inclusion &Inc) {
   IncludedHeaders.insert(Inc.Written);
   if (!Inc.Resolved.empty())
-    IncludedHeaders.insert(Inc.Resolved);
+    IncludedHeaders.insert(Inc.Resolved.raw());
 }
 
 /// FIXME(ioeric): we might not want to insert an absolute include path if the
@@ -278,7 +278,7 @@ bool IncludeInserter::shouldInsertInclude(
   auto Included = [&](llvm::StringRef Header) {
     return IncludedHeaders.contains(Header);
   };
-  return !Included(DeclaringHeader) && !Included(InsertedHeader.File);
+  return !Included(DeclaringHeader.raw()) && !Included(InsertedHeader.File);
 }
 
 std::optional<std::string>
@@ -348,7 +348,7 @@ IncludeInserter::insert(llvm::StringRef VerbatimHeader,
 
 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Inclusion &Inc) {
   return OS << Inc.Written << " = "
-            << (!Inc.Resolved.empty() ? Inc.Resolved : "[unresolved]")
+            << (!Inc.Resolved.empty() ? Inc.Resolved.raw() : "[unresolved]")
             << " at line" << Inc.HashLine;
 }
 
diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp
index 3ab3d89030520..c5957afbdc4de 100644
--- a/clang-tools-extra/clangd/Hover.cpp
+++ b/clang-tools-extra/clangd/Hover.cpp
@@ -1302,7 +1302,7 @@ std::optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
       continue;
     HoverCountMetric.record(1, "include");
     HoverInfo HI;
-    HI.Name = std::string(llvm::sys::path::filename(Inc.Resolved));
+    HI.Name = Inc.Resolved.ref().filename().str();
     // FIXME: We don't have a fitting value for Kind.
     HI.Definition =
         URIForFile::canonicalize(Inc.Resolved, AST.tuPath()).file().str();
diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp
index e34706172f0bf..c37adc1f58593 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.cpp
+++ b/clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -100,7 +100,7 @@ bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST,
       // Since most private -> public mappings happen in a verbatim way, we
       // check textually here. This might go wrong in presence of symlinks or
       // header mappings. But that's not different than rest of the places.
-      if (AST.tuPath().ends_with(PHeader))
+      if (AST.tuPath().raw().ends_with(PHeader))
         return false;
     }
   }
@@ -122,9 +122,9 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
   const SourceManager &SM = AST.getSourceManager();
   const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
 
-  auto FileStyle = getFormatStyleForFile(AST.tuPath(), Code, TFS, false);
+  auto FileStyle = getFormatStyleForFile(AST.tuPath().raw(), Code, TFS, false);
 
-  tooling::HeaderIncludes HeaderIncludes(AST.tuPath(), Code,
+  tooling::HeaderIncludes HeaderIncludes(AST.tuPath().raw(), Code,
                                          FileStyle.IncludeStyle);
   for (const auto &SymbolWithMissingInclude : MissingIncludes) {
     llvm::StringRef ResolvedPath =
@@ -157,7 +157,7 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
                       SymbolWithMissingInclude.Symbol.name());
     D.Name = "missing-includes";
     D.Source = Diag::DiagSource::Clangd;
-    D.File = AST.tuPath();
+    D.File = AST.tuPath().raw();
     D.InsideMainFile = true;
     // We avoid the "warning" severity here in favor of LSP's "information".
     //
@@ -194,7 +194,7 @@ std::vector<Diag> generateUnusedIncludeDiagnostics(
     llvm::StringRef Code, HeaderFilter IgnoreHeaders) {
   std::vector<Diag> Result;
   for (const auto *Inc : UnusedIncludes) {
-    if (isIgnored(Inc->Resolved, IgnoreHeaders))
+    if (isIgnored(Inc->Resolved.raw(), IgnoreHeaders))
       continue;
     Diag &D = Result.emplace_back();
     D.Message =
@@ -204,7 +204,7 @@ std::vector<Diag> generateUnusedIncludeDiagnostics(
                           llvm::sys::path::Style::posix));
     D.Name = "unused-includes";
     D.Source = Diag::DiagSource::Clangd;
-    D.File = FileName;
+    D.File = FileName.raw();
     D.InsideMainFile = true;
     D.Severity = DiagnosticsEngine::Warning;
     D.Tags.push_back(Unnecessary);
@@ -343,7 +343,7 @@ include_cleaner::Includes convertIncludes(const ParsedAST &AST) {
     TransformedInc.Angled = WrittenRef.starts_with("<");
     // Inc.Resolved is canonicalized with clangd::getCanonicalPath(),
     // which is based on FileManager::getCanonicalName(ParentDir).
-    auto FE = SM.getFileManager().getFileRef(Inc.Resolved);
+    auto FE = SM.getFileManager().getFileRef(Inc.Resolved.raw());
     if (!FE) {
       elog("IncludeCleaner: Failed to get an entry for resolved path {0}: {1}",
            Inc.Resolved, FE.takeError());
@@ -363,7 +363,7 @@ computeIncludeCleanerFindings(ParsedAST &AST, bool AnalyzeAngledIncludes) {
   const auto &SM = AST.getSourceManager();
   include_cleaner::Includes ConvertedIncludes = convertIncludes(AST);
   const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
-  auto PreamblePatch = PreamblePatch::getPatchEntry(AST.tuPath(), SM);
+  auto PreamblePatch = PreamblePatch::getPatchEntry(AST.tuPath().raw(), SM);
 
   std::vector<include_cleaner::SymbolReference> Macros =
       collectMacroReferences(AST);
diff --git a/clang-tools-extra/clangd/ModulesBuilder.cpp b/clang-tools-extra/clangd/ModulesBuilder.cpp
index c1878f91b5e16..72d5f6568ce51 100644
--- a/clang-tools-extra/clangd/ModulesBuilder.cpp
+++ b/clang-tools-extra/clangd/ModulesBuilder.cpp
@@ -36,10 +36,10 @@ namespace {
 // TODO: Move these module fils out of the temporary directory if the module
 // files are persistent.
 llvm::SmallString<256> getUniqueModuleFilesPath(PathRef MainFile) {
-  llvm::SmallString<128> HashedPrefix = llvm::sys::path::filename(MainFile);
+  llvm::SmallString<128> HashedPrefix = MainFile.filename();
   // There might be multiple files with the same name in a project. So appending
   // the hash value of the full path to make sure they won't conflict.
-  HashedPrefix += std::to_string(llvm::hash_value(MainFile));
+  HashedPrefix += std::to_string(hash_value(MainFile));
 
   llvm::SmallString<256> ResultPattern;
 
@@ -64,7 +64,7 @@ llvm::SmallString<256> getUniqueModuleFilesPath(PathRef MainFile) {
 // Get a unique module file path under \param ModuleFilesPrefix.
 std::string getModuleFilePath(llvm::StringRef ModuleName,
                               PathRef ModuleFilesPrefix) {
-  llvm::SmallString<256> ModuleFilePath(ModuleFilesPrefix);
+  llvm::SmallString<256> ModuleFilePath(ModuleFilesPrefix.raw());
   auto [PrimaryModuleName, PartitionName] = ModuleName.split(':');
   llvm::sys::path::append(ModuleFilePath, PrimaryModuleName);
   if (!PartitionName.empty()) {
@@ -97,7 +97,7 @@ class FailedPrerequisiteModules : public PrerequisiteModules {
 
 struct ModuleFile {
   ModuleFile(StringRef ModuleName, PathRef ModuleFilePath)
-      : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
+      : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.raw()) {}
 
   ModuleFile() = delete;
 
@@ -216,7 +216,7 @@ bool IsModuleFileUpToDate(PathRef ModuleFilePath,
   // listener.
   Reader.setListener(nullptr);
 
-  if (Reader.ReadAST(ModuleFilePath, serialization::MK_MainFile,
+  if (Reader.ReadAST(ModuleFilePath.raw(), serialization::MK_MainFile,
                      SourceLocation(),
                      ASTReader::ARR_None) != ASTReader::Success)
     return false;
@@ -308,7 +308,7 @@ bool ReusablePrerequisiteModules::canReuse(
   if (RequiredModules.empty())
     return true;
 
-  llvm::SmallVector<llvm::StringRef> BMIPaths;
+  llvm::SmallVector<PathRef> BMIPaths;
   for (auto &MF : RequiredModules)
     BMIPaths.push_back(MF->getModuleFilePath());
   return IsModuleFilesUpToDate(BMIPaths, *this, VFS);
@@ -370,7 +370,7 @@ class ModuleNameToSourceCache {
 
   void addEntry(llvm::StringRef ModuleName, PathRef Source) {
     std::lock_guard<std::mutex> Lock(CacheMutex);
-    ModuleNameToSourceCache[ModuleName] = Source.str();
+    ModuleNameToSourceCache[ModuleName] = Source.raw();
   }
 
   void eraseEntry(llvm::StringRef ModuleName) {
diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp
index 3f63daaf400db..2874b2c245bbd 100644
--- a/clang-tools-extra/clangd/ParsedAST.cpp
+++ b/clang-tools-extra/clangd/ParsedAST.cpp
@@ -196,8 +196,9 @@ class ReplayPreamble : private PPCallbacks {
   void replay() {
     for (const auto &Inc : Includes) {
       OptionalFileEntryRef File;
-      if (Inc.Resolved != "")
-        File = expectedToOptional(SM.getFileManager().getFileRef(Inc.Resolved));
+      if (!Inc.Resolved.empty())
+        File = expectedToOptional(
+            SM.getFileManager().getFileRef(Inc.Resolved.ref().raw()));
 
       // Re-lex the #include directive to find its interesting parts.
       auto HashLoc = SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset);
diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp
index 6fab3e2191426..1e2fcccf78c71 100644
--- a/clang-tools-extra/clangd/Preamble.cpp
+++ b/clang-tools-extra/clangd/Preamble.cpp
@@ -595,11 +595,11 @@ buildPreamble(PathRef FileName, CompilerInvocation CI,
   // Note that we don't need to copy the input contents, preamble can live
   // without those.
   auto ContentsBuffer =
-      llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
+      llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName.raw());
   auto Bounds = ComputePreambleBounds(CI.getLangOpts(), *ContentsBuffer, 0);
 
   trace::Span Tracer("BuildPreamble");
-  SPAN_ATTACH(Tracer, "File", FileName);
+  SPAN_ATTACH(Tracer, "File", FileName.raw());
   std::vector<std::unique_ptr<FeatureModule::ASTListener>> ASTListeners;
   if (Inputs.FeatureModules) {
     for (auto &M : *Inputs.FeatureModules) {
@@ -650,7 +650,7 @@ buildPreamble(PathRef FileName, CompilerInvocation CI,
         for (const auto &L : ASTListeners)
           L->beforeExecute(CI);
       });
-  llvm::SmallString<32> AbsFileName(FileName);
+  llvm::SmallString<32> AbsFileName(FileName.raw());
   VFS->makeAbsolute(AbsFileName);
   auto StatCache = std::make_shared<PreambleFileStatusCache>(AbsFileName);
   auto StatCacheFS = StatCache->getProducingFS(VFS);
@@ -743,7 +743,7 @@ bool isPreambleCompatible(const PreambleData &Preamble,
                           const ParseInputs &Inputs, PathRef FileName,
                           const CompilerInvocation &CI) {
   auto ContentsBuffer =
-      llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
+      llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName.raw());
   auto Bounds = ComputePreambleBounds(CI.getLangOpts(), *ContentsBuffer, 0);
   auto VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
   return compileCommandsAreEqual(Inputs.CompileCommand,
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index 05c8041df7de7..dc220daba1c82 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -13,6 +13,7 @@
 #include "Protocol.h"
 #include "URI.h"
 #include "support/Logger.h"
+#include "support/Path.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/StringExtras.h"
@@ -43,15 +44,14 @@ bool mapOptOrNull(const llvm::json::Value &Params, llvm::StringLiteral Prop,
 
 char LSPError::ID;
 
-URIForFile URIForFile::canonicalize(llvm::StringRef AbsPath,
-                                    llvm::StringRef TUPath) {
-  assert(llvm::sys::path::is_absolute(AbsPath) && "the path is relative");
-  auto Resolved = URI::resolvePath(AbsPath, TUPath);
+URIForFile URIForFile::canonicalize(PathRef AbsPath, PathRef TUPath) {
+  assert(AbsPath.isAbsolute() && "the path is relative");
+  auto Resolved = URI::resolvePath(AbsPath.raw(), TUPath.raw());
   if (!Resolved) {
     elog("URIForFile: failed to resolve path {0} with TU path {1}: "
          "{2}.\nUsing unresolved path.",
          AbsPath, TUPath, Resolved.takeError());
-    return URIForFile(std::string(AbsPath));
+    return URIForFile(AbsPath.owned().raw());
   }
   return URIForFile(std::move(*Resolved));
 }
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index c7ef1a13e6e39..e89880527ca1c 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -26,6 +26,7 @@
 #include "URI.h"
 #include "index/SymbolID.h"
 #include "support/MemoryTree.h"
+#include "support/Path.h"
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/JSON.h"
@@ -94,8 +95,7 @@ struct URIForFile {
   /// Files can be referred to by several paths (e.g. in the presence of links).
   /// Which one we prefer may depend on where we're coming from. \p TUPath is a
   /// hint, and should usually be the main entrypoint file we're processing.
-  static URIForFile canonicalize(llvm::StringRef AbsPath,
-                                 llvm::StringRef TUPath);
+  static URIForFile canonicalize(PathRef AbsPath, PathRef TUPath);
 
   static llvm::Expected<URIForFile> fromURI(const URI &U,
                                             llvm::StringRef HintPath);
diff --git a/clang-tools-extra/clangd/ScanningProjectModules.cpp b/clang-tools-extra/clangd/ScanningProjectModules.cpp
index e561513fe687f..b509d7cee838d 100644
--- a/clang-tools-extra/clangd/ScanningProjectModules.cpp
+++ b/clang-tools-extra/clangd/ScanningProjectModules.cpp
@@ -92,7 +92,7 @@ class ModuleDependencyScanner {
 std::optional<ModuleDependencyScanner::ModuleDependencyInfo>
 ModuleDependencyScanner::scan(PathRef FilePath,
                               const ProjectModules::CommandMangler &Mangler) {
-  auto Candidates = CDB->getCompileCommands(FilePath);
+  auto Candidates = CDB->getCompileCommands(FilePath.raw());
   if (Candidates.empty())
     return std::nullopt;
 
@@ -106,7 +106,7 @@ ModuleDependencyScanner::scan(PathRef FilePath,
 
   using namespace clang::tooling::dependencies;
 
-  llvm::SmallString<128> FilePathDir(FilePath);
+  llvm::SmallString<128> FilePathDir(FilePath.raw());
   llvm::sys::path::remove_filename(FilePathDir);
   DependencyScanningTool ScanningTool(Service, TFS.view(FilePathDir));
 
@@ -122,7 +122,7 @@ ModuleDependencyScanner::scan(PathRef FilePath,
   ModuleDependencyInfo Result;
 
   if (ScanningResult->Provides) {
-    ModuleNameToSource[ScanningResult->Provides->ModuleName] = FilePath;
+    ModuleNameToSource[ScanningResult->Provides->ModuleName] = FilePath.raw();
     Result.ModuleName = ScanningResult->Provides->ModuleName;
   }
 
@@ -195,7 +195,7 @@ class ScanningAllProjectModules : public ProjectModules {
   std::string getSourceForModuleName(llvm::StringRef ModuleName,
                                      PathRef RequiredSourceFile) override {
     Scanner.globalScan(Mangler);
-    return Scanner.getSourceForModuleName(ModuleName).str();
+    return Scanner.getSourceForModuleName(ModuleName).owned().raw();
   }
 
   std::string getModuleNameForSource(PathRef File) override {
diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp
index 035e5e63d8fbb..c7e76d9d70e96 100644
--- a/clang-tools-extra/clangd/TUScheduler.cpp
+++ b/clang-tools-extra/clangd/TUScheduler.cpp
@@ -310,7 +310,7 @@ class TUScheduler::HeaderIncluderCache {
   // Headers not in the list will have their associations removed.
   void update(PathRef MainFile, llvm::ArrayRef<std::string> Headers) {
     std::lock_guard<std::mutex> Lock(Mu);
-    auto It = MainToFirst.try_emplace(MainFile, nullptr);
+    auto It = MainToFirst.try_emplace(MainFile.raw(), nullptr);
     Association *&First = It.first->second;
     if (First)
       invalidate(First);
@@ -323,7 +323,7 @@ class TUScheduler::HeaderIncluderCache {
   // will be eligible for association with other files that get update()d.
   void remove(PathRef MainFile) {
     std::lock_guard<std::mutex> Lock(Mu);
-    Association *&First = MainToFirst[MainFile];
+    Association *&First = MainToFirst[MainFile.raw()];
     if (First) {
       invalidate(First);
       First = nullptr;
@@ -335,7 +335,7 @@ class TUScheduler::HeaderIncluderCache {
   /// Get the mainfile associated with Header, or the empty string if none.
   std::string get(PathRef Header) const {
     std::lock_guard<std::mutex> Lock(Mu);
-    return HeaderToMain.lookup(Header).MainFile.str();
+    return HeaderToMain.lookup(Header.raw()).MainFile.str();
   }
 
   size_t getUsedBytes() const {
@@ -483,7 +483,7 @@ class PreambleThread {
           break;
 
         {
-          Throttle.emplace(FileName, Throttler, ReqCV);
+          Throttle.emplace(FileName.raw(), Throttler, ReqCV);
           std::optional<trace::Span> Tracer;
           // If acquire succeeded synchronously, avoid status jitter.
           if (!Throttle->satisfied()) {
@@ -822,9 +822,9 @@ ASTWorker::create(PathRef FileName, const GlobalCompilationDatabase &CDB,
       new ASTWorker(FileName, CDB, IdleASTs, HeaderIncluders, Barrier,
                     /*RunSync=*/!Tasks, Opts, Callbacks));
   if (Tasks) {
-    Tasks->runAsync("ASTWorker:" + llvm::sys::path::filename(FileName),
+    Tasks->runAsync("ASTWorker:" + FileName.filename(),
                     [Worker]() { Worker->run(); });
-    Tasks->runAsync("PreambleWorker:" + llvm::sys::path::filename(FileName),
+    Tasks->runAsync("PreambleWorker:" + FileName.filename(),
                     [Worker]() { Worker->PreamblePeer.run(); });
   }
 
@@ -841,8 +841,9 @@ ASTWorker::ASTWorker(PathRef FileName, const GlobalCompilationDatabase &CDB,
       UpdateDebounce(Opts.UpdateDebounce), FileName(FileName),
       ContextProvider(Opts.ContextProvider), CDB(CDB), Callbacks(Callbacks),
       Barrier(Barrier), Done(false), Status(FileName, Callbacks),
-      PreamblePeer(FileName, Callbacks, Opts.StorePreamblesInMemory, RunSync,
-                   Opts.PreambleThrottler, Status, HeaderIncluders, *this) {
+      PreamblePeer(FileName.raw(), Callbacks, Opts.StorePreamblesInMemory,
+                   RunSync, Opts.PreambleThrottler, Status, HeaderIncluders,
+                   *this) {
   // Set a fallback command because compile command can be accessed before
   // `Inputs` is initialized. Other fields are only used after initialization
   // from client inputs.
@@ -881,7 +882,8 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags,
           HeaderIncluders.remove(ProxyFile);
         } else {
           // We have a reliable command for an including file, use it.
-          Cmd = tooling::transferCompileCommand(std::move(*ProxyCmd), FileName);
+          Cmd = tooling::transferCompileCommand(std::move(*ProxyCmd),
+                                                FileName.raw());
         }
       }
     }
@@ -995,9 +997,9 @@ void ASTWorker::runWithAST(
       // return a compatible preamble as ASTWorker::update blocks.
       std::optional<ParsedAST> NewAST;
       if (Invocation) {
-        NewAST = ParsedAST::build(FileName, FileInputs, std::move(Invocation),
-                                  CompilerInvocationDiagConsumer.take(),
-                                  getPossiblyStalePreamble());
+        NewAST = ParsedAST::build(
+            FileName.raw(), FileInputs, std::move(Invocation),
+            CompilerInvocationDiagConsumer.take(), getPossiblyStalePreamble());
         ++ASTBuildCount;
       }
       AST = NewAST ? std::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;
@@ -1211,8 +1213,9 @@ void ASTWorker::generateDiagnostics(
       IdleASTs.take(this, &ASTAccessForDiag);
   if (!AST || !InputsAreLatest) {
     auto RebuildStartTime = DebouncePolicy::clock::now();
-    std::optional<ParsedAST> NewAST = ParsedAST::build(
-        FileName, Inputs, std::move(Invocation), CIDiags, *LatestPreamble);
+    std::optional<ParsedAST> NewAST =
+        ParsedAST::build(FileName.raw(), Inputs, std::move(Invocation), CIDiags,
+                         *LatestPreamble);
     auto RebuildDuration = DebouncePolicy::clock::now() - RebuildStartTime;
     ++ASTBuildCount;
     // Try to record the AST-build time, to inform future update debouncing.
@@ -1324,7 +1327,7 @@ void ASTWorker::runTask(llvm::StringRef Name, llvm::function_ref<void()> Task) {
     crashDumpParseInputs(llvm::errs(), FileInputs);
   });
   trace::Span Tracer(Name);
-  WithContext WithProvidedContext(ContextProvider(FileName));
+  WithContext WithProvidedContext(ContextProvider(FileName.raw()));
   Task();
 }
 
@@ -1352,7 +1355,7 @@ void ASTWorker::startTask(llvm::StringRef Name,
     }
 
     // Allow this request to be cancelled if invalidated.
-    Context Ctx = Context::current().derive(FileBeingProcessed, FileName);
+    Context Ctx = Context::current().derive(FileBeingProcessed, FileName.raw());
     Canceler Invalidate = nullptr;
     if (Invalidation) {
       WithContext WC(std::move(Ctx));
@@ -1640,7 +1643,7 @@ TUScheduler::TUScheduler(const GlobalCompilationDatabase &CDB,
       HeaderIncluders(std::make_unique<HeaderIncluderCache>()) {
   // Avoid null checks everywhere.
   if (!Opts.ContextProvider) {
-    this->Opts.ContextProvider = [](llvm::StringRef) {
+    this->Opts.ContextProvider = [](PathRef) {
       return Context::current().clone();
     };
   }
@@ -1673,7 +1676,7 @@ bool TUScheduler::blockUntilIdle(Deadline D) const {
 
 bool TUScheduler::update(PathRef File, ParseInputs Inputs,
                          WantDiagnostics WantDiags) {
-  std::unique_ptr<FileData> &FD = Files[File];
+  std::unique_ptr<FileData> &FD = Files[File.raw()];
   bool NewFile = FD == nullptr;
   bool ContentChanged = false;
   if (!FD) {
@@ -1692,12 +1695,12 @@ bool TUScheduler::update(PathRef File, ParseInputs Inputs,
   // There might be synthetic update requests, don't change the LastActiveFile
   // in such cases.
   if (ContentChanged)
-    LastActiveFile = File.str();
+    LastActiveFile = File.owned().raw();
   return NewFile;
 }
 
 void TUScheduler::remove(PathRef File) {
-  bool Removed = Files.erase(File);
+  bool Removed = Files.erase(File.raw());
   if (!Removed)
     elog("Trying to remove file from TUScheduler that is not tracked: {0}",
          File);
@@ -1744,13 +1747,13 @@ void TUScheduler::runWithAST(
     llvm::StringRef Name, PathRef File,
     llvm::unique_function<void(llvm::Expected<InputsAndAST>)> Action,
     TUScheduler::ASTActionInvalidation Invalidation) {
-  auto It = Files.find(File);
+  auto It = Files.find(File.raw());
   if (It == Files.end()) {
     Action(llvm::make_error<LSPError>(
         "trying to get AST for non-added document", ErrorCode::InvalidParams));
     return;
   }
-  LastActiveFile = File.str();
+  LastActiveFile = File.owned().raw();
 
   It->second->Worker->runWithAST(Name, std::move(Action), Invalidation);
 }
@@ -1758,18 +1761,18 @@ void TUScheduler::runWithAST(
 void TUScheduler::runWithPreamble(llvm::StringRef Name, PathRef File,
                                   PreambleConsistency Consistency,
                                   Callback<InputsAndPreamble> Action) {
-  auto It = Files.find(File);
+  auto It = Files.find(File.raw());
   if (It == Files.end()) {
     Action(llvm::make_error<LSPError>(
         "trying to get preamble for non-added document",
         ErrorCode::InvalidParams));
     return;
   }
-  LastActiveFile = File.str();
+  LastActiveFile = File.owned().raw();
 
   if (!PreambleTasks) {
     trace::Span Tracer(Name);
-    SPAN_ATTACH(Tracer, "file", File);
+    SPAN_ATTACH(Tracer, "file", File.raw());
     std::shared_ptr<const ASTSignals> Signals;
     std::shared_ptr<const PreambleData> Preamble =
         It->second->Worker->getPossiblyStalePreamble(&Signals);
@@ -1781,11 +1784,11 @@ void TUScheduler::runWithPreamble(llvm::StringRef Name, PathRef File,
   }
 
   std::shared_ptr<const ASTWorker> Worker = It->second->Worker.lock();
-  auto Task = [Worker, Consistency, Name = Name.str(), File = File.str(),
+  auto Task = [Worker, Consistency, Name = Name.str(), File = File.owned(),
                Contents = It->second->Contents,
                Command = Worker->getCurrentCompileCommand(),
                Ctx = Context::current().derive(FileBeingProcessed,
-                                               std::string(File)),
+                                               File.owned().raw()),
                Action = std::move(Action), this]() mutable {
     clang::noteBottomOfStack();
     ThreadCrashReporter ScopedReporter([&Name, &Contents, &Command]() {
@@ -1811,8 +1814,7 @@ void TUScheduler::runWithPreamble(llvm::StringRef Name, PathRef File,
     Action(InputsAndPreamble{Contents, Command, Preamble.get(), Signals.get()});
   };
 
-  PreambleTasks->runAsync("task:" + llvm::sys::path::filename(File),
-                          std::move(Task));
+  PreambleTasks->runAsync("task:" + File.filename(), std::move(Task));
 }
 
 llvm::StringMap<TUScheduler::FileStats> TUScheduler::fileStats() const {
diff --git a/clang-tools-extra/clangd/TidyProvider.cpp b/clang-tools-extra/clangd/TidyProvider.cpp
index 1d79a7a7399ec..d2be8fa03b057 100644
--- a/clang-tools-extra/clangd/TidyProvider.cpp
+++ b/clang-tools-extra/clangd/TidyProvider.cpp
@@ -64,7 +64,7 @@ class DotClangTidyCache : private FileCache {
               }
             };
             if (auto Parsed = tidy::parseConfigurationWithDiags(
-                    llvm::MemoryBufferRef(*Data, path()), Diagnostics))
+                    llvm::MemoryBufferRef(*Data, path().raw()), Diagnostics))
               Value = std::make_shared<const tidy::ClangTidyOptions>(
                   std::move(*Parsed));
             else
@@ -99,21 +99,21 @@ class DotClangTidyTree {
 
   void apply(tidy::ClangTidyOptions &Result, PathRef AbsPath) {
     namespace path = llvm::sys::path;
-    assert(path::is_absolute(AbsPath));
+    assert(AbsPath.isAbsolute());
 
     // Compute absolute paths to all ancestors (substrings of P.Path).
     // Ensure cache entries for each ancestor exist in the map.
     llvm::SmallVector<DotClangTidyCache *> Caches;
     {
       std::lock_guard<std::mutex> Lock(Mu);
-      for (auto Ancestor = absoluteParent(AbsPath); !Ancestor.empty();
-           Ancestor = absoluteParent(Ancestor)) {
-        auto It = Cache.find(Ancestor);
+      for (auto Ancestor = AbsPath.absoluteParent(); !Ancestor.empty();
+           Ancestor = Ancestor.absoluteParent()) {
+        auto It = Cache.find(Ancestor.raw());
         // Assemble the actual config file path only if needed.
         if (It == Cache.end()) {
-          llvm::SmallString<256> ConfigPath = Ancestor;
+          llvm::SmallString<256> ConfigPath = Ancestor.raw();
           path::append(ConfigPath, RelPath);
-          It = Cache.try_emplace(Ancestor, ConfigPath.str()).first;
+          It = Cache.try_emplace(Ancestor.raw(), ConfigPath.str()).first;
         }
         Caches.push_back(&It->second);
       }
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index 8b9fffa3f64cd..af63e15bcea14 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -226,7 +226,7 @@ std::optional<LocatedSymbol> locateFileReferent(const Position &Pos,
   for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) {
     if (!Inc.Resolved.empty() && Inc.HashLine == Pos.line) {
       LocatedSymbol File;
-      File.Name = std::string(llvm::sys::path::filename(Inc.Resolved));
+      File.Name = Inc.Resolved.ref().filename().str();
       File.PreferredDeclaration = {
           URIForFile::canonicalize(Inc.Resolved, MainFilePath), Range{}};
       File.Definition = File.PreferredDeclaration;
@@ -514,7 +514,7 @@ std::vector<LocatedSymbol> locateSymbolForType(const ParsedAST &AST,
                                                const QualType &Type,
                                                const SymbolIndex *Index) {
   const auto &SM = AST.getSourceManager();
-  auto MainFilePath = AST.tuPath();
+  auto MainFilePath = AST.tuPath().raw();
 
   // FIXME: this sends unique_ptr<Foo> to unique_ptr<T>.
   // Likely it would be better to send it to Foo (heuristically) or to both.
@@ -776,7 +776,7 @@ const syntax::Token *findNearbyIdentifier(const SpelledWord &Word,
 std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
                                           const SymbolIndex *Index) {
   const auto &SM = AST.getSourceManager();
-  auto MainFilePath = AST.tuPath();
+  auto MainFilePath = AST.tuPath().raw();
 
   if (auto File = locateFileReferent(Pos, AST, MainFilePath))
     return {std::move(*File)};
@@ -1320,7 +1320,7 @@ std::vector<LocatedSymbol> findImplementations(ParsedAST &AST, Position Pos,
       QueryKind = RelationKind::BaseOf;
     }
   }
-  return findImplementors(std::move(IDs), QueryKind, Index, AST.tuPath());
+  return findImplementors(std::move(IDs), QueryKind, Index, AST.tuPath().raw());
 }
 
 namespace {
@@ -1538,8 +1538,9 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
           return;
         }
         const auto LSPLocDecl =
-            toLSPLocation(Object.CanonicalDeclaration, MainFilePath);
-        const auto LSPLocDef = toLSPLocation(Object.Definition, MainFilePath);
+            toLSPLocation(Object.CanonicalDeclaration, MainFilePath.raw());
+        const auto LSPLocDef =
+            toLSPLocation(Object.Definition, MainFilePath.raw());
         if (LSPLocDecl && LSPLocDecl != LSPLocDef) {
           ReferencesResult::Reference Result;
           Result.Loc = {std::move(*LSPLocDecl), std::nullopt};
@@ -1590,7 +1591,7 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
     LookupRequest ContainerLookup;
     llvm::DenseMap<SymbolID, std::vector<size_t>> RefIndicesForContainer;
     Results.HasMore |= Index->refs(Req, [&](const Ref &R) {
-      auto LSPLoc = toLSPLocation(R.Location, MainFilePath);
+      auto LSPLoc = toLSPLocation(R.Location, MainFilePath.raw());
       // Avoid indexed results for the main file - the AST is authoritative.
       if (!LSPLoc ||
           (!AllowMainFileSymbols && LSPLoc->uri.file() == MainFilePath))
@@ -1668,9 +1669,9 @@ std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos) {
     }
     if (const NamedDecl *Def = getDefinition(D))
       NewSymbol.definitionRange = makeLocation(
-          AST.getASTContext(), nameLocation(*Def, SM), MainFilePath);
-    NewSymbol.declarationRange =
-        makeLocation(AST.getASTContext(), nameLocation(*D, SM), MainFilePath);
+          AST.getASTContext(), nameLocation(*Def, SM), MainFilePath.raw());
+    NewSymbol.declarationRange = makeLocation(
+        AST.getASTContext(), nameLocation(*D, SM), MainFilePath.raw());
 
     Results.push_back(std::move(NewSymbol));
   }
@@ -1789,7 +1790,7 @@ declToCallHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
 template <typename HierarchyItem>
 static std::optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S,
                                                           PathRef TUPath) {
-  auto Loc = symbolToLocation(S, TUPath);
+  auto Loc = symbolToLocation(S, TUPath.raw());
   if (!Loc) {
     elog("Failed to convert symbol to hierarchy item: {0}", Loc.takeError());
     return std::nullopt;
@@ -2204,12 +2205,12 @@ getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
     }
 
     std::optional<TypeHierarchyItem> Result =
-        declToTypeHierarchyItem(*CXXRD, AST.tuPath());
+        declToTypeHierarchyItem(*CXXRD, AST.tuPath().raw());
     if (!Result)
       continue;
 
     RecursionProtectionSet RPSet;
-    fillSuperTypes(*CXXRD, AST.tuPath(), *Result, RPSet);
+    fillSuperTypes(*CXXRD, AST.tuPath().raw(), *Result, RPSet);
 
     if (WantChildren && ResolveLevels > 0) {
       Result->children.emplace();
@@ -2289,7 +2290,7 @@ prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath) {
           !cast<VarDecl>(Decl)->isLocalVarDecl()) &&
         Decl->getKind() != Decl::Kind::Field)
       continue;
-    if (auto CHI = declToCallHierarchyItem(*Decl, AST.tuPath()))
+    if (auto CHI = declToCallHierarchyItem(*Decl, AST.tuPath().raw()))
       Result.emplace_back(std::move(*CHI));
   }
   return Result;
diff --git a/clang-tools-extra/clangd/index/Background.cpp b/clang-tools-extra/clangd/index/Background.cpp
index 496d1455def4b..fad6e3cbe3e46 100644
--- a/clang-tools-extra/clangd/index/Background.cpp
+++ b/clang-tools-extra/clangd/index/Background.cpp
@@ -78,7 +78,7 @@ llvm::SmallString<128> getAbsolutePath(const tooling::CompileCommand &Cmd) {
 }
 
 bool shardIsStale(const LoadedShard &LS, llvm::vfs::FileSystem *FS) {
-  auto Buf = FS->getBufferForFile(LS.AbsolutePath);
+  auto Buf = FS->getBufferForFile(LS.AbsolutePath.raw());
   if (!Buf) {
     vlog("Background-index: Couldn't read {0} to validate stored index: {1}",
          LS.AbsolutePath, Buf.getError().message());
@@ -224,14 +224,14 @@ void BackgroundIndex::update(
     // We need to store shards before updating the index, since the latter
     // consumes slabs.
     // FIXME: Also skip serializing the shard if it is already up-to-date.
-    if (auto Error = IndexStorageFactory(Path)->storeShard(Path, *IF))
+    if (auto Error = IndexStorageFactory(Path)->storeShard(Path.raw(), *IF))
       elog("Failed to write background-index shard for file {0}: {1}", Path,
            std::move(Error));
 
     {
       std::lock_guard<std::mutex> Lock(ShardVersionsMu);
       const auto &Hash = FileIt.getValue().second;
-      auto DigestIt = ShardVersions.try_emplace(Path);
+      auto DigestIt = ShardVersions.try_emplace(Path.raw());
       ShardVersion &SV = DigestIt.first->second;
       // Skip if file is already up to date, unless previous index was broken
       // and this one is not.
@@ -386,12 +386,12 @@ BackgroundIndex::loadProject(std::vector<std::string> MainFiles) {
           LS.Shard->Relations
               ? std::make_unique<RelationSlab>(std::move(*LS.Shard->Relations))
               : nullptr;
-      ShardVersion &SV = ShardVersions[LS.AbsolutePath];
+      ShardVersion &SV = ShardVersions[LS.AbsolutePath.raw()];
       SV.Digest = LS.Digest;
       SV.HadErrors = LS.HadErrors;
       ++LoadedShards;
 
-      IndexedSymbols.update(URI::create(LS.AbsolutePath).toString(),
+      IndexedSymbols.update(URI::create(LS.AbsolutePath.raw()).toString(),
                             std::move(SS), std::move(RS), std::move(RelS),
                             LS.CountReferences);
     }
@@ -417,7 +417,9 @@ BackgroundIndex::loadProject(std::vector<std::string> MainFiles) {
     TUsToIndex.insert(TUForFile);
   }
 
-  return {TUsToIndex.begin(), TUsToIndex.end()};
+  auto ToRaw = [](PathRef R) { return R.raw(); };
+  return {llvm::map_iterator(TUsToIndex.begin(), ToRaw),
+          llvm::map_iterator(TUsToIndex.end(), ToRaw)};
 }
 
 void BackgroundIndex::profile(MemoryTree &MT) const {
diff --git a/clang-tools-extra/clangd/index/Background.h b/clang-tools-extra/clangd/index/Background.h
index 448e911201575..1babc3f85edbb 100644
--- a/clang-tools-extra/clangd/index/Background.h
+++ b/clang-tools-extra/clangd/index/Background.h
@@ -211,6 +211,7 @@ class BackgroundIndex : public SwapIndex {
   std::mutex ShardVersionsMu;
 
   BackgroundIndexStorage::Factory IndexStorageFactory;
+  // XXX: `MainFiles` should be a vector of `Path`s
   // Tries to load shards for the MainFiles and their dependencies.
   std::vector<std::string> loadProject(std::vector<std::string> MainFiles);
 
diff --git a/clang-tools-extra/clangd/index/BackgroundIndexLoader.cpp b/clang-tools-extra/clangd/index/BackgroundIndexLoader.cpp
index c09a5c3a3aeb8..4abe594e50e6f 100644
--- a/clang-tools-extra/clangd/index/BackgroundIndexLoader.cpp
+++ b/clang-tools-extra/clangd/index/BackgroundIndexLoader.cpp
@@ -48,17 +48,17 @@ class BackgroundIndexLoader {
 
 std::pair<const LoadedShard &, std::vector<Path>>
 BackgroundIndexLoader::loadShard(PathRef StartSourceFile, PathRef DependentTU) {
-  auto It = LoadedShards.try_emplace(StartSourceFile);
+  auto It = LoadedShards.try_emplace(StartSourceFile.raw());
   LoadedShard &LS = It.first->getValue();
   std::vector<Path> Edges = {};
   // Return the cached shard.
   if (!It.second)
     return {LS, Edges};
 
-  LS.AbsolutePath = StartSourceFile.str();
-  LS.DependentTU = std::string(DependentTU);
+  LS.AbsolutePath = StartSourceFile.owned();
+  LS.DependentTU = DependentTU.owned();
   BackgroundIndexStorage *Storage = IndexStorageFactory(LS.AbsolutePath);
-  auto Shard = Storage->loadShard(StartSourceFile);
+  auto Shard = Storage->loadShard(StartSourceFile.raw());
   if (!Shard || !Shard->Sources) {
     vlog("Failed to load shard: {0}", StartSourceFile);
     return {LS, Edges};
@@ -66,7 +66,7 @@ BackgroundIndexLoader::loadShard(PathRef StartSourceFile, PathRef DependentTU) {
 
   LS.Shard = std::move(Shard);
   for (const auto &It : *LS.Shard->Sources) {
-    auto AbsPath = URI::resolve(It.getKey(), StartSourceFile);
+    auto AbsPath = URI::resolve(It.getKey(), StartSourceFile.raw());
     if (!AbsPath) {
       elog("Failed to resolve URI: {0}", AbsPath.takeError());
       continue;
@@ -91,7 +91,7 @@ void BackgroundIndexLoader::load(PathRef MainFile) {
   llvm::StringSet<> InQueue;
   // Following containers points to strings inside InQueue.
   std::queue<PathRef> ToVisit;
-  InQueue.insert(MainFile);
+  InQueue.insert(MainFile.raw());
   ToVisit.push(MainFile);
 
   while (!ToVisit.empty()) {
@@ -100,7 +100,7 @@ void BackgroundIndexLoader::load(PathRef MainFile) {
 
     auto ShardAndEdges = loadShard(SourceFile, MainFile);
     for (PathRef Edge : ShardAndEdges.second) {
-      auto It = InQueue.insert(Edge);
+      auto It = InQueue.insert(Edge.raw());
       if (It.second)
         ToVisit.push(It.first->getKey());
     }
@@ -117,7 +117,7 @@ std::vector<LoadedShard> BackgroundIndexLoader::takeResult() && {
 } // namespace
 
 std::vector<LoadedShard>
-loadIndexShards(llvm::ArrayRef<Path> MainFiles,
+loadIndexShards(llvm::ArrayRef<std::string> MainFiles,
                 BackgroundIndexStorage::Factory &IndexStorageFactory,
                 const GlobalCompilationDatabase &CDB) {
   BackgroundIndexLoader Loader(IndexStorageFactory);
diff --git a/clang-tools-extra/clangd/index/BackgroundIndexLoader.h b/clang-tools-extra/clangd/index/BackgroundIndexLoader.h
index 81033646b6a4e..bee5ce6c6974b 100644
--- a/clang-tools-extra/clangd/index/BackgroundIndexLoader.h
+++ b/clang-tools-extra/clangd/index/BackgroundIndexLoader.h
@@ -39,7 +39,7 @@ struct LoadedShard {
 
 /// Loads all shards for the TU \p MainFile from \p Storage.
 std::vector<LoadedShard>
-loadIndexShards(llvm::ArrayRef<Path> MainFiles,
+loadIndexShards(llvm::ArrayRef<std::string> MainFiles,
                 BackgroundIndexStorage::Factory &IndexStorageFactory,
                 const GlobalCompilationDatabase &CDB);
 
diff --git a/clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp b/clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp
index d887b09482a95..5c077c4ed9471 100644
--- a/clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp
+++ b/clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp
@@ -109,7 +109,7 @@ class DiskBackedIndexStorageManager {
   // Creates or fetches to storage from cache for the specified project.
   BackgroundIndexStorage *operator()(PathRef File) {
     std::lock_guard<std::mutex> Lock(*IndexStorageMapMu);
-    llvm::SmallString<128> StorageDir(FallbackDir);
+    llvm::SmallString<128> StorageDir(FallbackDir.raw());
     if (auto PI = GetProjectInfo(File)) {
       StorageDir = PI->SourceRoot;
       llvm::sys::path::append(StorageDir, ".cache", "clangd", "index");
@@ -126,7 +126,7 @@ class DiskBackedIndexStorageManager {
       elog("Tried to create storage for empty directory!");
       return std::make_unique<NullStorage>();
     }
-    return std::make_unique<DiskBackedIndexStorage>(CDBDirectory);
+    return std::make_unique<DiskBackedIndexStorage>(CDBDirectory.raw());
   }
 
   Path FallbackDir;
diff --git a/clang-tools-extra/clangd/index/FileIndex.cpp b/clang-tools-extra/clangd/index/FileIndex.cpp
index aa573e312a756..7714fdbdebe65 100644
--- a/clang-tools-extra/clangd/index/FileIndex.cpp
+++ b/clang-tools-extra/clangd/index/FileIndex.cpp
@@ -182,7 +182,7 @@ FileShardedIndex::FileShardedIndex(IndexFileIn Input)
 std::vector<llvm::StringRef> FileShardedIndex::getAllSources() const {
   // It should be enough to construct a vector with {Shards.keys().begin(),
   // Shards.keys().end()} but MSVC fails to compile that.
-  std::vector<PathRef> Result;
+  std::vector<llvm::StringRef> Result;
   Result.reserve(Shards.size());
   for (auto Key : Shards.keys())
     Result.push_back(Key);
@@ -470,7 +470,7 @@ void FileIndex::updatePreamble(PathRef Path, llvm::StringRef Version,
 void FileIndex::updateMain(PathRef Path, ParsedAST &AST) {
   auto Contents = indexMainDecls(AST);
   MainFileSymbols.update(
-      URI::create(Path).toString(),
+      URI::create(Path.raw()).toString(),
       std::make_unique<SymbolSlab>(std::move(std::get<0>(Contents))),
       std::make_unique<RefSlab>(std::move(std::get<1>(Contents))),
       std::make_unique<RelationSlab>(std::move(std::get<2>(Contents))),
diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp
index 26059167208aa..436b6be40dbf6 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -784,7 +784,8 @@ renameObjCMethodWithinFile(ParsedAST &AST, const ObjCMethodDecl *MD,
   auto FilePath = AST.tuPath();
   auto RenameRanges = collectRenameIdentifierRanges(
       RenameSymbolName(MD->getDeclName()), Code, LangOpts);
-  auto RenameEdit = buildRenameEdit(FilePath, Code, RenameRanges, NewNames);
+  auto RenameEdit =
+      buildRenameEdit(FilePath.raw(), Code, RenameRanges, NewNames);
   if (!RenameEdit)
     return error("failed to rename in file {0}: {1}", FilePath,
                  RenameEdit.takeError());
@@ -891,7 +892,7 @@ findOccurrencesOutsideFile(const NamedDecl &RenameDecl,
     if ((R.Kind & RefKind::Spelled) == RefKind::Unknown)
       return;
     if (auto RefFilePath = filePath(R.Location, /*HintFilePath=*/MainFile)) {
-      if (!pathEqual(*RefFilePath, MainFile))
+      if (PathRef(*RefFilePath) != MainFile)
         AffectedFiles[*RefFilePath].push_back(toRange(R.Location));
     }
   });
diff --git a/clang-tools-extra/clangd/refactor/Tweak.cpp b/clang-tools-extra/clangd/refactor/Tweak.cpp
index 840843d1bfc4b..683b8c5710448 100644
--- a/clang-tools-extra/clangd/refactor/Tweak.cpp
+++ b/clang-tools-extra/clangd/refactor/Tweak.cpp
@@ -100,7 +100,7 @@ prepareTweak(StringRef ID, const Tweak::Selection &S,
   return error("tweak ID {0} is invalid", ID);
 }
 
-llvm::Expected<std::pair<Path, Edit>>
+llvm::Expected<std::pair<std::string, Edit>>
 Tweak::Effect::fileEdit(const SourceManager &SM, FileID FID,
                         tooling::Replacements Replacements) {
   Edit Ed(SM.getBufferData(FID), std::move(Replacements));
diff --git a/clang-tools-extra/clangd/refactor/Tweak.h b/clang-tools-extra/clangd/refactor/Tweak.h
index 257f44a285f88..4e8269138eda9 100644
--- a/clang-tools-extra/clangd/refactor/Tweak.h
+++ b/clang-tools-extra/clangd/refactor/Tweak.h
@@ -85,10 +85,11 @@ class Tweak {
       return E;
     }
 
+    /// XXX: This should return Path instead of std::string
     /// Path is the absolute, symlink-resolved path for the file pointed by FID
     /// in SM. Edit is generated from Replacements.
     /// Fails if cannot figure out absolute path for FID.
-    static llvm::Expected<std::pair<Path, Edit>>
+    static llvm::Expected<std::pair<std::string, Edit>>
     fileEdit(const SourceManager &SM, FileID FID,
              tooling::Replacements Replacements);
 
diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
index d6556bba14725..52e8c9315de73 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
@@ -474,7 +474,7 @@ class DefineInline : public Tweak {
     const tooling::Replacement DeleteFuncBody(SM, DefRange->getBegin(),
                                               SourceLen, "");
 
-    llvm::SmallVector<std::pair<std::string, Edit>> Edits;
+    llvm::SmallVector<std::pair<Path, Edit>> Edits;
     // Edit for Target.
     auto FE = Effect::fileEdit(SM, SM.getFileID(*Semicolon),
                                std::move(TargetFileReplacements));
@@ -499,7 +499,8 @@ class DefineInline : public Tweak {
 
     Effect E;
     for (auto &Pair : Edits)
-      E.ApplyEdits.try_emplace(std::move(Pair.first), std::move(Pair.second));
+      E.ApplyEdits.try_emplace(std::move(Pair.first).raw(),
+                               std::move(Pair.second));
     return E;
   }
 
diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
index e4eef228b6b99..6e87cc3ecfab1 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
@@ -428,7 +428,7 @@ class DefineOutline : public Tweak {
   }
 
   bool prepare(const Selection &Sel) override {
-    SameFile = !isHeaderFile(Sel.AST->tuPath(), Sel.AST->getLangOpts());
+    SameFile = !isHeaderFile(Sel.AST->tuPath().raw(), Sel.AST->getLangOpts());
     Source = getSelectedFunction(Sel.ASTSelection.commonAncestor());
 
     // Bail out if the selection is not a in-line function definition.
@@ -489,12 +489,12 @@ class DefineOutline : public Tweak {
 
   Expected<Effect> apply(const Selection &Sel) override {
     const SourceManager &SM = Sel.AST->getSourceManager();
-    auto CCFile = SameFile ? Sel.AST->tuPath().str()
-                           : getSourceFile(Sel.AST->tuPath(), Sel);
+    auto CCFile = SameFile ? Sel.AST->tuPath().owned()
+                           : getSourceFile(Sel.AST->tuPath().raw(), Sel);
     if (!CCFile)
       return error("Couldn't find a suitable implementation file.");
     assert(Sel.FS && "FS Must be set in apply");
-    auto Buffer = Sel.FS->getBufferForFile(*CCFile);
+    auto Buffer = Sel.FS->getBufferForFile(CCFile->raw());
     // FIXME: Maybe we should consider creating the implementation file if it
     // doesn't exist?
     if (!Buffer)
@@ -507,13 +507,14 @@ class DefineOutline : public Tweak {
     auto FuncDef = getFunctionSourceCode(
         Source, InsertionPoint->EnclosingNamespace, Sel.AST->getTokens(),
         Sel.AST->getHeuristicResolver(),
-        SameFile && isHeaderFile(Sel.AST->tuPath(), Sel.AST->getLangOpts()));
+        SameFile &&
+            isHeaderFile(Sel.AST->tuPath().raw(), Sel.AST->getLangOpts()));
     if (!FuncDef)
       return FuncDef.takeError();
 
-    SourceManagerForFile SMFF(*CCFile, Contents);
+    SourceManagerForFile SMFF(CCFile->raw(), Contents);
     const tooling::Replacement InsertFunctionDef(
-        *CCFile, InsertionPoint->Offset, 0, *FuncDef);
+        CCFile->raw(), InsertionPoint->Offset, 0, *FuncDef);
     auto Effect = Effect::mainFileEdit(
         SMFF.get(), tooling::Replacements(InsertFunctionDef));
     if (!Effect)
@@ -536,7 +537,7 @@ class DefineOutline : public Tweak {
     }
 
     if (SameFile) {
-      tooling::Replacements &R = Effect->ApplyEdits[*CCFile].Replacements;
+      tooling::Replacements &R = Effect->ApplyEdits[CCFile->raw()].Replacements;
       R = R.merge(HeaderUpdates);
     } else {
       auto HeaderFE = Effect::fileEdit(SM, SM.getMainFileID(), HeaderUpdates);
diff --git a/clang-tools-extra/clangd/support/FileCache.cpp b/clang-tools-extra/clangd/support/FileCache.cpp
index cc59c648b062a..9c55c0499f427 100644
--- a/clang-tools-extra/clangd/support/FileCache.cpp
+++ b/clang-tools-extra/clangd/support/FileCache.cpp
@@ -22,10 +22,10 @@ static constexpr uint64_t CacheDiskMismatch =
 // The cached value reflects that the file doesn't exist.
 static constexpr uint64_t FileNotFound = CacheDiskMismatch - 1;
 
-FileCache::FileCache(llvm::StringRef Path)
-    : Path(Path), ValidTime(std::chrono::steady_clock::time_point::min()),
+FileCache::FileCache(PathRef Path)
+    : Path(Path.raw()), ValidTime(std::chrono::steady_clock::time_point::min()),
       ModifiedTime(), Size(CacheDiskMismatch) {
-  assert(llvm::sys::path::is_absolute(Path));
+  assert(Path.isAbsolute());
 }
 
 void FileCache::read(
diff --git a/clang-tools-extra/clangd/support/Path.cpp b/clang-tools-extra/clangd/support/Path.cpp
index 1dd107cc6abef..9e9a1c308e36c 100644
--- a/clang-tools-extra/clangd/support/Path.cpp
+++ b/clang-tools-extra/clangd/support/Path.cpp
@@ -7,47 +7,151 @@
 //===----------------------------------------------------------------------===//
 
 #include "support/Path.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
+
+#include <ostream>
+
 namespace clang {
 namespace clangd {
 
-#ifdef CLANGD_PATH_CASE_INSENSITIVE
-std::string maybeCaseFoldPath(PathRef Path) { return Path.lower(); }
-bool pathEqual(PathRef A, PathRef B) { return A.equals_insensitive(B); }
-#else  // NOT CLANGD_PATH_CASE_INSENSITIVE
-std::string maybeCaseFoldPath(PathRef Path) { return Path.str(); }
-bool pathEqual(PathRef A, PathRef B) { return A == B; }
-#endif // CLANGD_PATH_CASE_INSENSITIVE
-
-PathRef absoluteParent(PathRef Path) {
-  assert(llvm::sys::path::is_absolute(Path));
+PathRef PathRef::absoluteParent() const {
+  assert(llvm::sys::path::is_absolute(Data));
 #if defined(_WIN32)
   // llvm::sys says "C:\" is absolute, and its parent is "C:" which is relative.
   // This unhelpful behavior seems to have been inherited from boost.
-  if (llvm::sys::path::relative_path(Path).empty()) {
+  if (llvm::sys::path::relative_path(Data).empty()) {
     return PathRef();
   }
 #endif
-  PathRef Result = llvm::sys::path::parent_path(Path);
+  llvm::StringRef Result = llvm::sys::path::parent_path(Data);
   assert(Result.empty() || llvm::sys::path::is_absolute(Result));
   return Result;
 }
 
-bool pathStartsWith(PathRef Ancestor, PathRef Path,
-                    llvm::sys::path::Style Style) {
-  assert(llvm::sys::path::is_absolute(Ancestor) &&
-         llvm::sys::path::is_absolute(Path));
+PathRef PathRef::parentPath(Style Style) const {
+  return llvm::sys::path::parent_path(Data, Style);
+}
+
+bool PathRef::startsWith(PathRef Path, Style Style) const {
+  assert(isAbsolute() && Path.isAbsolute()); // XXX: this doesn't pass the Style
+
   // If ancestor ends with a separator drop that, so that we can match /foo/ as
   // a parent of /foo.
-  if (llvm::sys::path::is_separator(Ancestor.back(), Style))
-    Ancestor = Ancestor.drop_back();
+  PathRef Ancestor = withoutTrailingSeparator(Style);
   // Ensure Path starts with Ancestor.
-  if (!pathEqual(Ancestor, Path.take_front(Ancestor.size())))
+  if (Ancestor != PathRef(Path.raw().take_front(Ancestor.size())))
     return false;
-  Path = Path.drop_front(Ancestor.size());
+  Path = Path.Data.drop_front(Ancestor.size());
   // Then make sure either two paths are equal or Path has a separator
   // afterwards.
-  return Path.empty() || llvm::sys::path::is_separator(Path.front(), Style);
+  return Path.empty() ||
+         llvm::sys::path::is_separator(Path.raw().front(), Style);
+}
+
+llvm::StringRef PathRef::filename(Style Style) const {
+  return llvm::sys::path::filename(raw(), Style);
+}
+
+llvm::StringRef PathRef::extension(Style Style) const {
+  return llvm::sys::path::extension(raw(), Style);
+}
+
+PathRef PathRef::stem(Style Style) const {
+  return llvm::sys::path::stem(raw(), Style);
+}
+
+PathRef PathRef::withoutTrailingSeparator(Style Style) const {
+  if (llvm::sys::path::is_separator(Data.back(), Style))
+    return Data.drop_back();
+  return Data;
+}
+
+Path PathRef::removeDots() const {
+  llvm::SmallString<128> CanonPath(Data);
+  llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
+  return CanonPath.str().str();
+}
+
+Path PathRef::caseFolded() const {
+#ifdef CLANGD_PATH_CASE_INSENSITIVE
+  return Data.lower();
+#else
+  return Data.str();
+#endif
+}
+
+bool PathRef::isAbsolute(Style Style) const {
+  return llvm::sys::path::is_absolute(Data, Style);
+}
+
+bool PathRef::isRelative(Style Style) const {
+  return llvm::sys::path::is_relative(Data, Style);
+}
+
+bool PathRef::exists() const { return llvm::sys::fs::exists(Data); }
+
+std::ostream &operator<<(std::ostream &OS, PathRef Path) {
+  OS << std::string_view(Path.raw());
+  return OS;
+}
+
+bool operator==(PathRef LHS, PathRef RHS) {
+#ifdef CLANGD_PATH_CASE_INSENSITIVE
+  return LHS.raw().equals_insensitive(RHS.raw());
+#else
+  return LHS.raw() == RHS.raw();
+#endif
+}
+
+llvm::hash_code hash_value(PathRef P) {
+#ifdef CLANGD_PATH_CASE_INSENSITIVE
+  return hash_combine_range(llvm::map_iterator(P.raw().begin(), llvm::toLower),
+                            llvm::map_iterator(P.raw().end(), llvm::toLower));
+#else
+  return hash_value(P.raw());
+#endif
 }
+
 } // namespace clangd
 } // namespace clang
+
+namespace llvm {
+
+unsigned DenseMapInfo<clang::clangd::PathRef, void>::getHashValue(
+    clang::clangd::PathRef Val) {
+  assert(Val.Data.data() != getEmptyKey().Data.data() &&
+         "Cannot hash the empty key!");
+  assert(Val.Data.data() != getTombstoneKey().Data.data() &&
+         "Cannot hash the tombstone key!");
+  return (unsigned)(hash_value(Val));
+}
+
+namespace cl {
+
+void parser<clang::clangd::Path>::printOptionDiff(const Option &O,
+                                                  clang::clangd::PathRef V,
+                                                  const OptVal &Default,
+                                                  size_t GlobalWidth) const {
+  constexpr static const size_t MaxOptWidth = 8;
+
+  printOptionName(O, GlobalWidth);
+  outs() << "= " << V.raw();
+  size_t NumSpaces = MaxOptWidth > V.size() ? MaxOptWidth - V.size() : 0;
+  outs().indent(NumSpaces) << " (default: ";
+  if (Default.hasValue())
+    outs() << Default.getValue().raw();
+  else
+    outs() << "*no default*";
+  outs() << ")\n";
+}
+
+void parser<clang::clangd::Path>::anchor() {}
+
+} // namespace cl
+
+} // namespace llvm
diff --git a/clang-tools-extra/clangd/support/Path.h b/clang-tools-extra/clangd/support/Path.h
index ff45a436e5f08..a6c3a8e345434 100644
--- a/clang-tools-extra/clangd/support/Path.h
+++ b/clang-tools-extra/clangd/support/Path.h
@@ -9,8 +9,15 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_PATH_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_PATH_H
 
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FormatProviders.h"
+#include "llvm/Support/JSON.h"
 #include "llvm/Support/Path.h"
+
 #include <string>
 
 /// Whether current platform treats paths case insensitively.
@@ -21,30 +28,179 @@
 namespace clang {
 namespace clangd {
 
-/// A typedef to represent a file path. Used solely for more descriptive
-/// signatures.
-using Path = std::string;
-/// A typedef to represent a ref to file path. Used solely for more descriptive
-/// signatures.
-using PathRef = llvm::StringRef;
-
-// For platforms where paths are case-insensitive (but case-preserving),
-// we need to do case-insensitive comparisons and use lowercase keys.
-// FIXME: Make Path a real class with desired semantics instead.
-std::string maybeCaseFoldPath(PathRef Path);
-bool pathEqual(PathRef, PathRef);
-
-/// Checks if \p Ancestor is a proper ancestor of \p Path. This is just a
-/// smarter lexical prefix match, e.g: foo/bar/baz doesn't start with foo/./bar.
-/// Both \p Ancestor and \p Path must be absolute.
-bool pathStartsWith(
-    PathRef Ancestor, PathRef Path,
-    llvm::sys::path::Style Style = llvm::sys::path::Style::native);
-
-/// Variant of parent_path that operates only on absolute paths.
-/// Unlike parent_path doesn't consider C: a parent of C:\.
-PathRef absoluteParent(PathRef Path);
+class PathRef;
+class Path {
+public:
+  Path() = default;
+  Path(std::string Data) : Data(std::move(Data)) {}
+  Path(const char *Data) : Data(Data) {}
+
+  explicit Path(PathRef Ref);
+
+  operator PathRef() const;
+  PathRef ref() const;
+
+  [[nodiscard]] const std::string &raw() const & { return Data; }
+  [[nodiscard]] std::string &&raw() && { return std::move(Data); }
+
+  [[nodiscard]] size_t size() const { return Data.size(); }
+  [[nodiscard]] bool empty() const { return Data.empty(); }
+
+private:
+  std::string Data;
+
+  friend llvm::json::Value toJSON(const Path &Path) { return Path.Data; }
+
+  friend bool fromJSON(const llvm::json::Value &Value, Path &Path,
+                       llvm::json::Path Cursor) {
+    return fromJSON(Value, Path.Data, Cursor);
+  }
+};
+
+class LLVM_GSL_POINTER PathRef {
+public:
+  using Style = llvm::sys::path::Style;
+
+  PathRef() = default;
+  PathRef(llvm::StringRef Ref) : Data(Ref) {}
+  PathRef(const std::string &Str) : Data(Str) {}
+  PathRef(const char *Str) : Data(Str) {}
+  template <unsigned int N>
+  PathRef(const llvm::SmallString<N> &Str) : Data(Str) {}
+
+  /// Variant of parent_path that operates only on absolute paths.
+  /// Unlike parent_path doesn't consider C: a parent of C:\.
+  [[nodiscard]] PathRef absoluteParent() const;
+
+  [[nodiscard]] PathRef parentPath(Style Style = Style::native) const;
+
+  /// Checks if \p this is a proper ancestor of \p Path. This is just a
+  /// smarter lexical prefix match, e.g: foo/bar/baz doesn't start with
+  /// foo/./bar. Both \p this and \p Path must be absolute.
+  [[nodiscard]] bool startsWith(PathRef Path,
+                                Style Style = Style::native) const;
+
+  [[nodiscard]] llvm::StringRef filename(Style Style = Style::native) const;
+
+  [[nodiscard]] llvm::StringRef extension(Style Style = Style::native) const;
+
+  [[nodiscard]] PathRef stem(Style Style = Style::native) const;
+
+  /// Returns a version of \p File that doesn't contain dots and dot dots.
+  /// e.g /a/b/../c -> /a/c
+  ///     /a/b/./c -> /a/b/c
+  /// FIXME: We should avoid encountering such paths in clangd internals by
+  /// filtering everything we get over LSP, CDB, etc.
+  [[nodiscard]] Path removeDots() const;
+
+  [[nodiscard]] Path caseFolded() const;
+
+  [[nodiscard]] Path owned() const { return Path(*this); }
+  [[nodiscard]] llvm::StringRef raw() const { return Data; }
+
+  [[nodiscard]] PathRef
+  withoutTrailingSeparator(Style Style = Style::native) const;
+
+  [[nodiscard]] size_t size() const { return Data.size(); }
+
+  [[nodiscard]] bool empty() const { return Data.empty(); }
+
+  [[nodiscard]] bool isAbsolute(Style Style = Style::native) const;
+  [[nodiscard]] bool isRelative(Style Style = Style::native) const;
+
+  [[nodiscard]] bool exists() const;
+
+private:
+  llvm::StringRef Data;
+
+  friend llvm::DenseMapInfo<clang::clangd::PathRef, void>;
+};
+
+inline Path::Path(PathRef Ref) : Data(Ref.raw().str()) {}
+
+// for gtest
+std::ostream &operator<<(std::ostream &OS, PathRef Path);
+
+inline Path::operator PathRef() const { return PathRef(Data); }
+inline PathRef Path::ref() const { return PathRef(Data); }
+
+bool operator==(PathRef LHS, PathRef RHS);
+inline bool operator!=(PathRef LHS, PathRef RHS) { return !(LHS == RHS); }
+
+inline bool operator==(Path LHS, Path RHS) {
+  return clang::clangd::PathRef(LHS) == clang::clangd::PathRef(RHS);
+}
+inline bool operator!=(Path LHS, Path RHS) { return !(LHS == RHS); }
+
+[[nodiscard]] llvm::hash_code hash_value(PathRef P);
+[[nodiscard]] inline llvm::hash_code hash_value(const Path &P) {
+  return hash_value(PathRef(P));
+}
+
+inline llvm::json::Value toJson(PathRef Path) { return Path.raw(); }
+
 } // namespace clangd
 } // namespace clang
 
+namespace llvm {
+
+template <> struct format_provider<clang::clangd::PathRef> {
+  static void format(const clang::clangd::PathRef &V, llvm::raw_ostream &Stream,
+                     StringRef Style) {
+    format_provider<llvm::StringRef>::format(V.raw(), Stream, Style);
+  }
+};
+
+template <> struct format_provider<clang::clangd::Path> {
+  static void format(const clang::clangd::Path &V, llvm::raw_ostream &Stream,
+                     StringRef Style) {
+    format_provider<clang::clangd::PathRef>::format(V, Stream, Style);
+  }
+};
+
+template <> struct DenseMapInfo<clang::clangd::PathRef, void> {
+  static inline clang::clangd::PathRef getEmptyKey() {
+    return DenseMapInfo<llvm::StringRef>::getEmptyKey();
+  }
+
+  static inline clang::clangd::PathRef getTombstoneKey() {
+    return DenseMapInfo<llvm::StringRef>::getTombstoneKey();
+  }
+
+  static unsigned getHashValue(clang::clangd::PathRef Val);
+
+  static bool isEqual(clang::clangd::PathRef LHS, clang::clangd::PathRef RHS) {
+    if (RHS.Data.data() == getEmptyKey().Data.data())
+      return LHS.Data.data() == getEmptyKey().Data.data();
+    if (RHS.Data.data() == getTombstoneKey().Data.data())
+      return LHS.Data.data() == getTombstoneKey().Data.data();
+    return LHS == RHS;
+  }
+};
+
+namespace cl {
+
+template <>
+struct parser<clang::clangd::Path> : public basic_parser<clang::clangd::Path> {
+public:
+  parser(Option &O) : basic_parser(O) {}
+
+  bool parse(cl::Option &O, StringRef ArgName, StringRef ArgValue,
+             clang::clangd::Path &Val) {
+    Val = ArgValue.str();
+    return false;
+  }
+
+  StringRef getValueName() const override { return "path"; }
+
+  void printOptionDiff(const Option &O, clang::clangd::PathRef V,
+                       const OptVal &Default, size_t GlobalWidth) const;
+
+  void anchor() override;
+};
+
+} // namespace cl
+
+} // namespace llvm
+
 #endif
diff --git a/clang-tools-extra/clangd/support/ThreadsafeFS.cpp b/clang-tools-extra/clangd/support/ThreadsafeFS.cpp
index 7398e4258527b..8c78ad0fe088e 100644
--- a/clang-tools-extra/clangd/support/ThreadsafeFS.cpp
+++ b/clang-tools-extra/clangd/support/ThreadsafeFS.cpp
@@ -74,7 +74,7 @@ class VolatileFileSystem : public llvm::vfs::ProxyFileSystem {
 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
 ThreadsafeFS::view(PathRef CWD) const {
   auto FS = view(std::nullopt);
-  if (auto EC = FS->setCurrentWorkingDirectory(CWD))
+  if (auto EC = FS->setCurrentWorkingDirectory(CWD.raw()))
     elog("VFS: failed to set CWD to {0}: {1}", CWD, EC.message());
   return FS;
 }
diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp
index 4bd256d6be22b..26b34e8dee3fc 100644
--- a/clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -673,12 +673,12 @@ class FlagsConfigProvider : public config::Provider {
     // If --compile-commands-dir arg was invoked, check value and override
     // default path.
     if (!CompileCommandsDir.empty()) {
-      if (llvm::sys::fs::exists(CompileCommandsDir)) {
+      if (CompileCommandsDir.ref().exists()) {
         // We support passing both relative and absolute paths to the
         // --compile-commands-dir argument, but we assume the path is absolute
         // in the rest of clangd so we make sure the path is absolute before
         // continuing.
-        llvm::SmallString<128> Path(CompileCommandsDir);
+        llvm::SmallString<128> Path(CompileCommandsDir.raw());
         if (std::error_code EC = llvm::sys::fs::make_absolute(Path)) {
           elog("Error while converting the relative path specified by "
                "--compile-commands-dir to an absolute path: {0}. The argument "
@@ -695,7 +695,7 @@ class FlagsConfigProvider : public config::Provider {
     if (!IndexFile.empty()) {
       Config::ExternalIndexSpec Spec;
       Spec.Kind = Spec.File;
-      Spec.Location = IndexFile;
+      Spec.Location = IndexFile.raw();
       IndexSpec = std::move(Spec);
     }
 #if CLANGD_ENABLE_REMOTE
@@ -826,7 +826,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
   std::optional<llvm::raw_fd_ostream> InputMirrorStream;
   if (!InputMirrorFile.empty()) {
     std::error_code EC;
-    InputMirrorStream.emplace(InputMirrorFile, /*ref*/ EC,
+    InputMirrorStream.emplace(InputMirrorFile.raw(), /*ref*/ EC,
                               llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
     if (EC) {
       InputMirrorStream.reset();
@@ -911,7 +911,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
     break;
   }
   if (!ResourceDir.empty())
-    Opts.ResourceDir = ResourceDir;
+    Opts.ResourceDir = ResourceDir.raw();
   Opts.BuildDynamicSymbolIndex = true;
   std::vector<std::unique_ptr<SymbolIndex>> IdxStack;
 #if CLANGD_ENABLE_REMOTE
@@ -1013,9 +1013,9 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
 
   if (CheckFile.getNumOccurrences()) {
     llvm::SmallString<256> Path;
-    if (auto Error =
-            llvm::sys::fs::real_path(CheckFile, Path, /*expand_tilde=*/true)) {
-      elog("Failed to resolve path {0}: {1}", CheckFile, Error.message());
+    if (auto Error = llvm::sys::fs::real_path(CheckFile.raw(), Path,
+                                              /*expand_tilde=*/true)) {
+      elog("Failed to resolve path {0}: {1}", CheckFile.raw(), Error.message());
       return 1;
     }
     log("Entering check mode (no LSP server)");
diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp
index d0bc3c4d7db98..3eee66c02b851 100644
--- a/clang-tools-extra/clangd/unittests/ASTTests.cpp
+++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp
@@ -622,7 +622,7 @@ TEST(ClangdAST, HasReservedName) {
 TEST(ClangdAST, PreferredIncludeDirective) {
   auto ComputePreferredDirective = [](TestTU &TU) {
     auto AST = TU.build();
-    return preferredIncludeDirective(AST.tuPath(), AST.getLangOpts(),
+    return preferredIncludeDirective(AST.tuPath().raw(), AST.getLangOpts(),
                                      AST.getIncludeStructure().MainFileIncludes,
                                      AST.getLocalTopLevelDecls());
   };
diff --git a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
index ada14c9939318..2d26e1ac5656c 100644
--- a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
+++ b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
@@ -98,7 +98,7 @@ TEST_F(BackgroundIndexTest, NoCrashOnErrorFile) {
   size_t CacheHits = 0;
   MemoryShardStorage MSS(Storage, CacheHits);
   OverlayCDB CDB(/*Base=*/nullptr);
-  BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+  BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                       /*Opts=*/{});
 
   tooling::CompileCommand Cmd;
@@ -132,11 +132,11 @@ TEST_F(BackgroundIndexTest, Config) {
   BackgroundIndex::Options Opts;
   Opts.ContextProvider = [](PathRef P) {
     Config C;
-    if (P.ends_with("foo.cpp"))
+    if (P.raw().ends_with("foo.cpp"))
       C.CompileFlags.Edits.push_back([](std::vector<std::string> &Argv) {
         Argv = tooling::getInsertArgumentAdjuster("-Done=two")(Argv, "");
       });
-    if (P.ends_with("baz.cpp"))
+    if (P.raw().ends_with("baz.cpp"))
       C.Index.Background = Config::BackgroundPolicy::Skip;
     return Context::current().derive(Config::Key, std::move(C));
   };
@@ -148,8 +148,7 @@ TEST_F(BackgroundIndexTest, Config) {
   OverlayCDB CDB(/*Base=*/nullptr, /*FallbackFlags=*/{},
                  CommandMangler::forTests());
 
-  BackgroundIndex Idx(
-      FS, CDB, [&](llvm::StringRef) { return &MSS; }, std::move(Opts));
+  BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; }, std::move(Opts));
   // Index the two files.
   for (auto &Cmd : Cmds) {
     std::string FullPath = testPath(Cmd.Filename);
@@ -191,8 +190,7 @@ TEST_F(BackgroundIndexTest, IndexTwoFiles) {
   MemoryShardStorage MSS(Storage, CacheHits);
   OverlayCDB CDB(/*Base=*/nullptr);
   BackgroundIndex::Options Opts;
-  BackgroundIndex Idx(
-      FS, CDB, [&](llvm::StringRef) { return &MSS; }, Opts);
+  BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; }, Opts);
 
   tooling::CompileCommand Cmd;
   Cmd.Filename = testPath("root/A.cc");
@@ -246,8 +244,7 @@ TEST_F(BackgroundIndexTest, MainFileRefs) {
   MemoryShardStorage MSS(Storage, CacheHits);
   OverlayCDB CDB(/*Base=*/nullptr);
   BackgroundIndex::Options Opts;
-  BackgroundIndex Idx(
-      FS, CDB, [&](llvm::StringRef) { return &MSS; }, Opts);
+  BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; }, Opts);
 
   tooling::CompileCommand Cmd;
   Cmd.Filename = testPath("root/A.cc");
@@ -286,7 +283,7 @@ TEST_F(BackgroundIndexTest, ShardStorageTest) {
   // Check nothing is loaded from Storage, but A.cc and A.h has been stored.
   {
     OverlayCDB CDB(/*Base=*/nullptr);
-    BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+    BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                         /*Opts=*/{});
     CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
     ASSERT_TRUE(Idx.blockUntilIdleForTest());
@@ -296,7 +293,7 @@ TEST_F(BackgroundIndexTest, ShardStorageTest) {
 
   {
     OverlayCDB CDB(/*Base=*/nullptr);
-    BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+    BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                         /*Opts=*/{});
     CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
     ASSERT_TRUE(Idx.blockUntilIdleForTest());
@@ -355,7 +352,7 @@ TEST_F(BackgroundIndexTest, DirectIncludesTest) {
   Cmd.CommandLine = {"clang++", testPath("root/A.cc")};
   {
     OverlayCDB CDB(/*Base=*/nullptr);
-    BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+    BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                         /*Opts=*/{});
     CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
     ASSERT_TRUE(Idx.blockUntilIdleForTest());
@@ -405,7 +402,7 @@ TEST_F(BackgroundIndexTest, ShardStorageLoad) {
   // Check nothing is loaded from Storage, but A.cc and A.h has been stored.
   {
     OverlayCDB CDB(/*Base=*/nullptr);
-    BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+    BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                         /*Opts=*/{});
     CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
     ASSERT_TRUE(Idx.blockUntilIdleForTest());
@@ -420,7 +417,7 @@ TEST_F(BackgroundIndexTest, ShardStorageLoad) {
       )cpp";
   {
     OverlayCDB CDB(/*Base=*/nullptr);
-    BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+    BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                         /*Opts=*/{});
     CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
     ASSERT_TRUE(Idx.blockUntilIdleForTest());
@@ -438,7 +435,7 @@ TEST_F(BackgroundIndexTest, ShardStorageLoad) {
   {
     CacheHits = 0;
     OverlayCDB CDB(/*Base=*/nullptr);
-    BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+    BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                         /*Opts=*/{});
     CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
     ASSERT_TRUE(Idx.blockUntilIdleForTest());
@@ -479,7 +476,7 @@ TEST_F(BackgroundIndexTest, ShardStorageEmptyFile) {
   // Check that A.cc, A.h and B.h has been stored.
   {
     OverlayCDB CDB(/*Base=*/nullptr);
-    BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+    BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                         /*Opts=*/{});
     CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
     ASSERT_TRUE(Idx.blockUntilIdleForTest());
@@ -495,7 +492,7 @@ TEST_F(BackgroundIndexTest, ShardStorageEmptyFile) {
   {
     CacheHits = 0;
     OverlayCDB CDB(/*Base=*/nullptr);
-    BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+    BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                         /*Opts=*/{});
     CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
     ASSERT_TRUE(Idx.blockUntilIdleForTest());
@@ -511,7 +508,7 @@ TEST_F(BackgroundIndexTest, ShardStorageEmptyFile) {
   {
     CacheHits = 0;
     OverlayCDB CDB(/*Base=*/nullptr);
-    BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+    BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                         /*Opts=*/{});
     CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
     ASSERT_TRUE(Idx.blockUntilIdleForTest());
@@ -529,7 +526,7 @@ TEST_F(BackgroundIndexTest, NoDotsInAbsPath) {
   size_t CacheHits = 0;
   MemoryShardStorage MSS(Storage, CacheHits);
   OverlayCDB CDB(/*Base=*/nullptr);
-  BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+  BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                       /*Opts=*/{});
   ASSERT_TRUE(Idx.blockUntilIdleForTest());
 
@@ -560,7 +557,7 @@ TEST_F(BackgroundIndexTest, UncompilableFiles) {
   size_t CacheHits = 0;
   MemoryShardStorage MSS(Storage, CacheHits);
   OverlayCDB CDB(/*Base=*/nullptr);
-  BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+  BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                       /*Opts=*/{});
 
   tooling::CompileCommand Cmd;
@@ -624,7 +621,7 @@ TEST_F(BackgroundIndexTest, CmdLineHash) {
   size_t CacheHits = 0;
   MemoryShardStorage MSS(Storage, CacheHits);
   OverlayCDB CDB(/*Base=*/nullptr);
-  BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+  BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                       /*Opts=*/{});
 
   tooling::CompileCommand Cmd;
@@ -652,7 +649,7 @@ TEST_F(BackgroundIndexTest, Reindex) {
   size_t CacheHits = 0;
   MemoryShardStorage MSS(Storage, CacheHits);
   OverlayCDB CDB(/*Base=*/nullptr);
-  BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
+  BackgroundIndex Idx(FS, CDB, [&](PathRef) { return &MSS; },
                       /*Opts=*/{});
 
   // Index a file.
@@ -901,7 +898,7 @@ TEST(BackgroundQueueTest, Progress) {
 TEST(BackgroundIndex, Profile) {
   MockFS FS;
   MockCompilationDatabase CDB;
-  BackgroundIndex Idx(FS, CDB, [](llvm::StringRef) { return nullptr; },
+  BackgroundIndex Idx(FS, CDB, [](PathRef) { return nullptr; },
                       /*Opts=*/{});
 
   llvm::BumpPtrAllocator Alloc;
diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp
index 643b8e9f12d75..fce04fe711680 100644
--- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp
+++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp
@@ -102,7 +102,7 @@ class MultipleErrorCheckingCallbacks : public ClangdServer::Callbacks {
     bool HadError = diagsContainErrors(Diagnostics);
 
     std::lock_guard<std::mutex> Lock(Mutex);
-    LastDiagsHadError[File] = HadError;
+    LastDiagsHadError[File.raw()] = HadError;
   }
 
   /// Exposes all files consumed by onDiagnosticsReady in an unspecified order.
@@ -591,8 +591,8 @@ struct Something {
 )cpp";
   Path BarCpp = testPath("bar.cpp");
 
-  FS.Files[FooCpp] = "";
-  FS.Files[BarCpp] = "";
+  FS.Files[FooCpp.raw()] = "";
+  FS.Files[BarCpp.raw()] = "";
 
   EXPECT_THAT(Server.fileStats(), IsEmpty());
 
@@ -691,7 +691,7 @@ int d;
 
     void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
                             llvm::ArrayRef<Diag> Diagnostics) override {
-      StringRef FileIndexStr = llvm::sys::path::stem(File);
+      StringRef FileIndexStr = File.stem().raw();
       ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
 
       unsigned long FileIndex = std::stoul(FileIndexStr.str());
@@ -1117,11 +1117,11 @@ TEST(ClangdServerTest, FallbackWhenWaitingForCompileCommand) {
       // FIXME: make this timeout and fail instead of waiting forever in case
       // something goes wrong.
       CanReturnCommand.wait();
-      auto FileName = llvm::sys::path::filename(File);
+      auto FileName = File.filename();
       std::vector<std::string> CommandLine = {"clangd", "-ffreestanding",
-                                              std::string(File)};
-      return {tooling::CompileCommand(llvm::sys::path::parent_path(File),
-                                      FileName, std::move(CommandLine), "")};
+                                              File.owned().raw()};
+      return {tooling::CompileCommand(File.parentPath().raw(), FileName,
+                                      std::move(CommandLine), "")};
     }
 
     std::vector<std::string> ExtraClangFlags;
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 718bee2e40b11..c197c288a0f3d 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -147,7 +147,7 @@ CodeCompleteResult completions(llvm::StringRef Text,
   Annotations Test(Text);
   auto TU = TestTU::withCode(Test.code());
   // To make sure our tests for completiopns inside templates work on Windows.
-  TU.Filename = FilePath.str();
+  TU.Filename = FilePath.owned().raw();
   return completions(TU, Test.point(), std::move(IndexSymbols),
                      std::move(Opts));
 }
diff --git a/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp b/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp
index c9e01e52dac1f..1fc72fb36a9ee 100644
--- a/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp
+++ b/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp
@@ -55,23 +55,23 @@ TEST(GlobalCompilationDatabaseTest, FallbackCommand) {
                                            testPath("foo/bar")));
 }
 
-static tooling::CompileCommand cmd(llvm::StringRef File, llvm::StringRef Arg) {
+static tooling::CompileCommand cmd(PathRef File, llvm::StringRef Arg) {
   return tooling::CompileCommand(
-      testRoot(), File, {"clang", std::string(Arg), std::string(File)}, "");
+      testRoot(), File.owned().raw(),
+      {"clang", std::string(Arg), File.owned().raw()}, "");
 }
 
 class OverlayCDBTest : public ::testing::Test {
   class BaseCDB : public GlobalCompilationDatabase {
   public:
     std::optional<tooling::CompileCommand>
-    getCompileCommand(llvm::StringRef File) const override {
+    getCompileCommand(PathRef File) const override {
       if (File == testPath("foo.cc"))
         return cmd(File, "-DA=1");
       return std::nullopt;
     }
 
-    tooling::CompileCommand
-    getFallbackCommand(llvm::StringRef File) const override {
+    tooling::CompileCommand getFallbackCommand(PathRef File) const override {
       return cmd(File, "-DA=2");
     }
 
diff --git a/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp b/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp
index e600207de458a..c9f95db7c923c 100644
--- a/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp
@@ -334,17 +334,17 @@ TEST(HeaderSourceSwitchTest, CaseSensitivity) {
   // index, check if we can still find the source file, which defines less
   // symbols than the header.
   auto HeaderAbsPath = testPath("HEADER.H");
-  // We expect the heuristics to pick:
-  // - header on case sensitive file systems, because the HeaderAbsPath doesn't
-  //   match what we've seen through index.
-  // - source on case insensitive file systems, as the HeaderAbsPath would match
-  //   the filename in index.
+// We expect the heuristics to pick:
+// - header on case sensitive file systems, because the HeaderAbsPath doesn't
+//   match what we've seen through index.
+// - source on case insensitive file systems, as the HeaderAbsPath would match
+//   the filename in index.
 #ifdef CLANGD_PATH_CASE_INSENSITIVE
   EXPECT_THAT(getCorrespondingHeaderOrSource(HeaderAbsPath, AST, Index.get()),
-              llvm::ValueIs(testing::StrCaseEq(testPath(TU.Filename))));
+              llvm::ValueIs(Path(testPath(TU.Filename))));
 #else
   EXPECT_THAT(getCorrespondingHeaderOrSource(HeaderAbsPath, AST, Index.get()),
-              llvm::ValueIs(testing::StrCaseEq(testPath(TU.HeaderFilename))));
+              llvm::ValueIs(Path(testPath(TU.HeaderFilename))));
 #endif
 }
 
diff --git a/clang-tools-extra/clangd/unittests/HeadersTests.cpp b/clang-tools-extra/clangd/unittests/HeadersTests.cpp
index 751383e3b4650..fd62c72b9340b 100644
--- a/clang-tools-extra/clangd/unittests/HeadersTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HeadersTests.cpp
@@ -111,7 +111,7 @@ class HeadersTest : public ::testing::Test {
                              QuotedHeaders, AngledHeaders);
     for (const auto &Inc : Inclusions)
       Inserter.addExisting(Inc);
-    auto Inserted = ToHeaderFile(Preferred);
+    auto Inserted = ToHeaderFile(Preferred.raw());
     if (!Inserter.shouldInsertInclude(Original, Inserted))
       return "";
     auto Path = Inserter.calculateIncludePath(Inserted, MainFile);
diff --git a/clang-tools-extra/clangd/unittests/PreambleTests.cpp b/clang-tools-extra/clangd/unittests/PreambleTests.cpp
index 16a2f9448b1ec..330bed8d08a5c 100644
--- a/clang-tools-extra/clangd/unittests/PreambleTests.cpp
+++ b/clang-tools-extra/clangd/unittests/PreambleTests.cpp
@@ -883,14 +883,14 @@ TEST(PreamblePatch, PatchFileEntry) {
 #define FOO)cpp");
   {
     auto AST = createPatchedAST(Code.code(), Code.code());
-    EXPECT_EQ(
-        PreamblePatch::getPatchEntry(AST->tuPath(), AST->getSourceManager()),
-        nullptr);
+    EXPECT_EQ(PreamblePatch::getPatchEntry(AST->tuPath().raw(),
+                                           AST->getSourceManager()),
+              nullptr);
   }
   {
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    auto FE =
-        PreamblePatch::getPatchEntry(AST->tuPath(), AST->getSourceManager());
+    auto FE = PreamblePatch::getPatchEntry(AST->tuPath().raw(),
+                                           AST->getSourceManager());
     ASSERT_NE(FE, std::nullopt);
     EXPECT_THAT(FE->getName().str(),
                 testing::EndsWith(PreamblePatch::HeaderName.str()));
diff --git a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
index 43f38e39c8952..27c39955035b4 100644
--- a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
@@ -79,7 +79,7 @@ MATCHER_P2(TUState, PreambleActivity, ASTActivity, "") {
 // Simple ContextProvider to verify the provider is invoked & contexts are used.
 static Key<std::string> BoundPath;
 Context bindPath(PathRef F) {
-  return Context::current().derive(BoundPath, F.str());
+  return Context::current().derive(BoundPath, F.owned().raw());
 }
 llvm::StringRef boundPath() {
   const std::string *V = Context::current().get(BoundPath);
@@ -155,7 +155,7 @@ class TUSchedulerTests : public ::testing::Test {
   void updateWithDiags(TUScheduler &S, PathRef File, ParseInputs Inputs,
                        WantDiagnostics WD,
                        llvm::unique_function<void(std::vector<Diag>)> CB) {
-    Path OrigFile = File.str();
+    Path OrigFile = File.owned();
     WithContextValue Ctx(DiagsCallbackKey,
                          [OrigFile, CB = std::move(CB)](
                              PathRef File, std::vector<Diag> Diags) mutable {
@@ -1569,7 +1569,7 @@ TEST_F(TUSchedulerTests, PreambleThrottle) {
       // Deliberately no synchronization.
       // The PreambleThrottler should serialize these calls, if not then tsan
       // will find a bug here.
-      Filenames.emplace_back(Path);
+      Filenames.emplace_back(Path.owned().raw());
     }
   };
 
diff --git a/clang-tools-extra/clangd/unittests/TestFS.cpp b/clang-tools-extra/clangd/unittests/TestFS.cpp
index bb309609eda20..816d5e364d99b 100644
--- a/clang-tools-extra/clangd/unittests/TestFS.cpp
+++ b/clang-tools-extra/clangd/unittests/TestFS.cpp
@@ -21,8 +21,8 @@ namespace {
 
 // Tries to strip \p Prefix from beginning of \p Path. Returns true on success.
 // If \p Prefix doesn't match, leaves \p Path untouched and returns false.
-bool pathConsumeFront(PathRef &Path, PathRef Prefix) {
-  if (!pathStartsWith(Prefix, Path))
+bool pathConsumeFront(llvm::StringRef &Path, PathRef Prefix) {
+  if (!Prefix.startsWith(Path))
     return false;
   Path = Path.drop_front(Prefix.size());
   return true;
@@ -61,14 +61,14 @@ MockCompilationDatabase::getCompileCommand(PathRef File) const {
   if (ExtraClangFlags.empty())
     return std::nullopt;
 
-  auto FileName = llvm::sys::path::filename(File);
+  auto FileName = File.filename();
 
   // Build the compile command.
   auto CommandLine = ExtraClangFlags;
   CommandLine.insert(CommandLine.begin(), "clang");
   if (RelPathPrefix.empty()) {
     // Use the absolute path in the compile command.
-    CommandLine.push_back(std::string(File));
+    CommandLine.push_back(File.owned().raw());
   } else {
     // Build a relative path using RelPathPrefix.
     llvm::SmallString<32> RelativeFilePath(RelPathPrefix);
@@ -76,10 +76,9 @@ MockCompilationDatabase::getCompileCommand(PathRef File) const {
     CommandLine.push_back(std::string(RelativeFilePath.str()));
   }
 
-  return {tooling::CompileCommand(Directory != llvm::StringRef()
-                                      ? Directory
-                                      : llvm::sys::path::parent_path(File),
-                                  FileName, std::move(CommandLine), "")};
+  return {tooling::CompileCommand(
+      Directory != llvm::StringRef() ? Directory : File.parentPath().raw(),
+      FileName, std::move(CommandLine), "")};
 }
 
 const char *testRoot() {
@@ -91,9 +90,9 @@ const char *testRoot() {
 }
 
 std::string testPath(PathRef File, llvm::sys::path::Style Style) {
-  assert(llvm::sys::path::is_relative(File) && "FileName should be relative");
+  assert(File.isRelative() && "FileName should be relative");
 
-  llvm::SmallString<32> NativeFile = File;
+  llvm::SmallString<32> NativeFile = File.raw();
   llvm::sys::path::native(NativeFile, Style);
   llvm::SmallString<32> Path;
   llvm::sys::path::append(Path, Style, testRoot(), NativeFile);
@@ -110,7 +109,7 @@ class TestScheme : public URIScheme {
   llvm::Expected<std::string>
   getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body,
                   llvm::StringRef HintPath) const override {
-    if (!HintPath.empty() && !pathStartsWith(testRoot(), HintPath))
+    if (!HintPath.empty() && !PathRef(testRoot()).startsWith(HintPath))
       return error("Hint path is not empty and doesn't start with {0}: {1}",
                    testRoot(), HintPath);
     if (!Body.consume_front("/"))
diff --git a/clang-tools-extra/clangd/unittests/support/PathTests.cpp b/clang-tools-extra/clangd/unittests/support/PathTests.cpp
index 599c76926d30d..98f3d6a2cc6d7 100644
--- a/clang-tools-extra/clangd/unittests/support/PathTests.cpp
+++ b/clang-tools-extra/clangd/unittests/support/PathTests.cpp
@@ -15,21 +15,21 @@ namespace clang {
 namespace clangd {
 namespace {
 TEST(PathTests, IsAncestor) {
-  EXPECT_TRUE(pathStartsWith(testPath("foo"), testPath("foo")));
-  EXPECT_TRUE(pathStartsWith(testPath("foo/"), testPath("foo")));
+  EXPECT_TRUE(PathRef(testPath("foo")).startsWith(testPath("foo")));
+  EXPECT_TRUE(PathRef(testPath("foo/")).startsWith(testPath("foo")));
 
-  EXPECT_FALSE(pathStartsWith(testPath("foo"), testPath("fooz")));
-  EXPECT_FALSE(pathStartsWith(testPath("foo/"), testPath("fooz")));
+  EXPECT_FALSE(PathRef(testPath("foo")).startsWith(testPath("fooz")));
+  EXPECT_FALSE(PathRef(testPath("foo/")).startsWith(testPath("fooz")));
 
-  EXPECT_TRUE(pathStartsWith(testPath("foo"), testPath("foo/bar")));
-  EXPECT_TRUE(pathStartsWith(testPath("foo/"), testPath("foo/bar")));
+  EXPECT_TRUE(PathRef(testPath("foo")).startsWith(testPath("foo/bar")));
+  EXPECT_TRUE(PathRef(testPath("foo/")).startsWith(testPath("foo/bar")));
 
 #ifdef CLANGD_PATH_CASE_INSENSITIVE
-  EXPECT_TRUE(pathStartsWith(testPath("fOo"), testPath("foo/bar")));
-  EXPECT_TRUE(pathStartsWith(testPath("foo"), testPath("fOo/bar")));
+  EXPECT_TRUE(PathRef(testPath("fOo")).startsWith(testPath("foo/bar")));
+  EXPECT_TRUE(PathRef(testPath("foo")).startsWith(testPath("fOo/bar")));
 #else
-  EXPECT_FALSE(pathStartsWith(testPath("fOo"), testPath("foo/bar")));
-  EXPECT_FALSE(pathStartsWith(testPath("foo"), testPath("fOo/bar")));
+  EXPECT_FALSE(PathRef(testPath("fOo")).startsWith(testPath("foo/bar")));
+  EXPECT_FALSE(PathRef(testPath("foo")).startsWith(testPath("fOo/bar")));
 #endif
 }
 } // namespace



More information about the cfe-commits mailing list