https://github.com/jansvoboda11 updated 
https://github.com/llvm/llvm-project/pull/198333

>From 51544680c5ec667cf32139258e0557d6fb230dd5 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <[email protected]>
Date: Mon, 18 May 2026 08:50:36 -0700
Subject: [PATCH 1/7] [llvm][support] Implement `sys::path::starts_with()`

This PR adds a new function that checks whether a path starts with a specified 
prefix. Unlike `StringRef::starts_with()`, this requires an exact match of the 
last component, and ignores case and separator differences on Windows.

This is adopted by Clang's dependency scanner, where the iterator-based prefix 
matching showed up in the traces (when the `InProcessModuleCache` was already 
populated).
---
 .../DependencyScanning/ModuleDepCollector.cpp | 12 +------
 llvm/include/llvm/Support/Path.h              | 11 ++++++
 llvm/lib/Support/Path.cpp                     | 34 +++++++++++++++++--
 llvm/unittests/Support/Path.cpp               | 10 ++++++
 4 files changed, 53 insertions(+), 14 deletions(-)

diff --git a/clang/lib/DependencyScanning/ModuleDepCollector.cpp 
b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
index f9fe50564a5ae3..cf23b1f6ec0130 100644
--- a/clang/lib/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
@@ -200,18 +200,8 @@ bool dependencies::isPathInStableDir(const 
ArrayRef<StringRef> Directories,
   if (!path::is_absolute(Input))
     return false;
 
-  auto PathStartsWith = [](StringRef Prefix, StringRef Path) {
-    auto PrefixIt = path::begin(Prefix), PrefixEnd = path::end(Prefix);
-    for (auto PathIt = path::begin(Path), PathEnd = path::end(Path);
-         PrefixIt != PrefixEnd && PathIt != PathEnd; ++PrefixIt, ++PathIt) {
-      if (*PrefixIt != *PathIt)
-        return false;
-    }
-    return PrefixIt == PrefixEnd;
-  };
-
   return any_of(Directories, [&](StringRef Dir) {
-    return !Dir.empty() && PathStartsWith(Dir, Input);
+    return !Dir.empty() && path::starts_with(Input, Dir);
   });
 }
 
diff --git a/llvm/include/llvm/Support/Path.h b/llvm/include/llvm/Support/Path.h
index 28e1e8ba3ae36c..a8a3c49500a1c5 100644
--- a/llvm/include/llvm/Support/Path.h
+++ b/llvm/include/llvm/Support/Path.h
@@ -174,6 +174,17 @@ LLVM_ABI void replace_extension(SmallVectorImpl<char> 
&path,
                                 const Twine &extension,
                                 Style style = Style::native);
 
+/// Check whether \a Path starts with \a Prefix.
+///
+/// Unlike \c StringRef::starts_with, this takes care to check for exact match
+/// of the last component in order to correctly return false for path 
"/foo/bar"
+/// and prefix "/foo/b", which are likely different file system entities.
+///
+/// On Windows, this also ignores path separator differences and upper/lower
+/// case differences.
+LLVM_ABI bool starts_with(StringRef Path, StringRef Prefix,
+                          Style style = Style::native);
+
 /// Replace matching path prefix with another path.
 ///
 /// @code
diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp
index 457499b93362df..a15be5a4fc5bd0 100644
--- a/llvm/lib/Support/Path.cpp
+++ b/llvm/lib/Support/Path.cpp
@@ -497,8 +497,36 @@ void replace_extension(SmallVectorImpl<char> &path, const 
Twine &extension,
   path.append(ext.begin(), ext.end());
 }
 
-static bool starts_with(StringRef Path, StringRef Prefix,
-                        Style style = Style::native) {
+bool starts_with(StringRef Path, StringRef Prefix, Style style) {
+  auto ConsumeFront = [&]() {
+    if (Path.consume_front(Prefix))
+      return true;
+    // Windows prefix matching : case and separator insensitive
+    if (is_style_windows(style)) {
+      if (Path.size() < Prefix.size())
+        return false;
+      for (size_t I = 0, E = Prefix.size(); I != E; ++I) {
+        bool SepPath = is_separator(Path[I], style);
+        bool SepPrefix = is_separator(Prefix[I], style);
+        if (SepPath != SepPrefix)
+          return false;
+        if (!SepPath && toLower(Path[I]) != toLower(Prefix[I]))
+          return false;
+      }
+      Path = Path.drop_front(Prefix.size());
+      return true;
+    }
+    return false;
+  };
+  // The path must start with the prefix and last matching component must match
+  // in its entirety, not just the prefix.
+  return ConsumeFront() && (Path.empty() || is_separator(Path[0], style));
+}
+
+// Unlike \c starts_with, this returns true even when the last component is 
just
+// a prefix match and not an exact match.
+static bool starts_with_relaxed(StringRef Path, StringRef Prefix,
+                                Style style = Style::native) {
   // Windows prefix matching : case and separator insensitive
   if (is_style_windows(style)) {
     if (Path.size() < Prefix.size())
@@ -522,7 +550,7 @@ bool replace_path_prefix(SmallVectorImpl<char> &Path, 
StringRef OldPrefix,
     return false;
 
   StringRef OrigPath(Path.begin(), Path.size());
-  if (!starts_with(OrigPath, OldPrefix, style))
+  if (!starts_with_relaxed(OrigPath, OldPrefix, style))
     return false;
 
   // If prefixes have the same size we can simply copy the new one over.
diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp
index bb825a1b1e65ce..f9d10466d67943 100644
--- a/llvm/unittests/Support/Path.cpp
+++ b/llvm/unittests/Support/Path.cpp
@@ -441,6 +441,16 @@ TEST(Support, AbsolutePathIteratorEnd) {
   }
 }
 
+TEST(Support, PathStartsWith) {
+  EXPECT_TRUE(path::starts_with("/foo", "/foo"));
+  EXPECT_TRUE(path::starts_with("/foo/", "/foo"));
+  // FIXME: This should probably work too.
+  // EXPECT_TRUE(path::starts_with("/foo", "/foo/"));
+  EXPECT_TRUE(path::starts_with("/foo/", "/foo/"));
+  EXPECT_FALSE(path::starts_with("/foo", "/fooo"));
+  EXPECT_FALSE(path::starts_with("/fooo", "/foo"));
+}
+
 #ifdef _WIN32
 std::string getEnvWin(const wchar_t *Var) {
   std::string expected;

>From 8b326411aa6fcd9572cee1b1a1a86e26a237f1fa Mon Sep 17 00:00:00 2001
From: Jan Svoboda <[email protected]>
Date: Mon, 18 May 2026 11:29:08 -0700
Subject: [PATCH 2/7] Only handle normalized paths

---
 .../DependencyScannerImpl.h                   |  6 ++--
 .../DependencyScanning/ModuleDepCollector.h   |  8 ++---
 .../clang/Tooling/DependencyScanningTool.h    |  2 +-
 .../DependencyScannerImpl.cpp                 | 33 ++++++++++++-------
 .../DependencyScanning/ModuleDepCollector.cpp | 11 ++++---
 llvm/include/llvm/Support/Path.h              |  4 +--
 llvm/lib/Support/Path.cpp                     | 28 ++++------------
 7 files changed, 44 insertions(+), 48 deletions(-)

diff --git a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h 
b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
index 893017e68233b2..96cfd50a9d4f1a 100644
--- a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
+++ b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
@@ -107,12 +107,12 @@ void initializeScanCompilerInstance(
     DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service,
     IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS);
 
-SmallVector<StringRef>
+SmallVector<SmallString<0>, 2>
 getInitialStableDirs(const CompilerInstance &ScanInstance);
 
 std::optional<PrebuiltModulesAttrsMap>
 computePrebuiltModulesASTMap(CompilerInstance &ScanInstance,
-                             SmallVector<StringRef> &StableDirs);
+                             ArrayRef<SmallString<0>> StableDirs);
 
 /// Create the dependency collector that will collect the produced
 /// dependencies. May return the created ModuleDepCollector depending
@@ -123,7 +123,7 @@ std::shared_ptr<ModuleDepCollector> 
initializeScanInstanceDependencyCollector(
     DependencyScanningService &Service, CompilerInvocation &Inv,
     DependencyActionController &Controller,
     PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
-    SmallVector<StringRef> &StableDirs);
+    SmallVector<SmallString<0>, 2> StableDirs);
 } // namespace dependencies
 } // namespace clang
 
diff --git a/clang/include/clang/DependencyScanning/ModuleDepCollector.h 
b/clang/include/clang/DependencyScanning/ModuleDepCollector.h
index 4713cbd9387ecc..f54b9ccf61b378 100644
--- a/clang/include/clang/DependencyScanning/ModuleDepCollector.h
+++ b/clang/include/clang/DependencyScanning/ModuleDepCollector.h
@@ -86,7 +86,7 @@ class ModuleDepCollector final : public DependencyCollector {
                      DependencyActionController &Controller,
                      CompilerInvocation OriginalCI,
                      const PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
-                     const ArrayRef<StringRef> StableDirs);
+                     SmallVector<SmallString<0>, 2> StableDirs);
 
   /// Processes the accumulated dependency information and reports it to the
   /// \c Consumer.
@@ -115,7 +115,7 @@ class ModuleDepCollector final : public DependencyCollector 
{
   const PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
   /// Directory paths known to be stable through an active development and 
build
   /// cycle.
-  const ArrayRef<StringRef> StableDirs;
+  SmallVector<SmallString<0>, 2> StableDirs;
   /// Path to the main source file.
   std::string MainFile;
   /// Non-modular file dependencies. This includes the main source file and
@@ -210,7 +210,7 @@ void resetBenignCodeGenOptions(frontend::ActionKind 
ProgramAction,
 ///
 /// \param Directories Paths known to be in a stable location. e.g. Sysroot.
 /// \param Input Path to evaluate.
-bool isPathInStableDir(const ArrayRef<StringRef> Directories,
+bool isPathInStableDir(const ArrayRef<SmallString<0>> Directories,
                        const StringRef Input);
 
 /// Determine if options collected from a module's
@@ -218,7 +218,7 @@ bool isPathInStableDir(const ArrayRef<StringRef> 
Directories,
 ///
 /// \param Directories Paths known to be in a stable location. e.g. Sysroot.
 /// \param HSOpts Header search options derived from the compiler invocation.
-bool areOptionsInStableDir(const ArrayRef<StringRef> Directories,
+bool areOptionsInStableDir(const ArrayRef<SmallString<0>> Directories,
                            const HeaderSearchOptions &HSOpts);
 
 } // end namespace dependencies
diff --git a/clang/include/clang/Tooling/DependencyScanningTool.h 
b/clang/include/clang/Tooling/DependencyScanningTool.h
index 90216d8f1da82f..38e7f8aea9d38c 100644
--- a/clang/include/clang/Tooling/DependencyScanningTool.h
+++ b/clang/include/clang/Tooling/DependencyScanningTool.h
@@ -162,7 +162,7 @@ class CompilerInstanceWithContext {
   std::unique_ptr<DependencyOutputOptions> OutputOpts;
 
   // Context - stable directory handling
-  llvm::SmallVector<StringRef> StableDirs;
+  SmallVector<SmallString<0>, 2> StableDirs;
   dependencies::PrebuiltModulesAttrsMap PrebuiltModuleASTMap;
 
   // Compiler Instance
diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp 
b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
index 42f87adba84c9f..2686d8dc47893f 100644
--- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
+++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
@@ -67,7 +67,7 @@ class PrebuiltModuleListener : public ASTReaderListener {
                          PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
                          const HeaderSearchOptions &HSOpts,
                          const LangOptions &LangOpts, DiagnosticsEngine &Diags,
-                         const ArrayRef<StringRef> StableDirs)
+                         const ArrayRef<SmallString<0>> StableDirs)
       : PrebuiltModuleFiles(PrebuiltModuleFiles),
         NewModuleFiles(NewModuleFiles),
         PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts),
@@ -166,7 +166,7 @@ class PrebuiltModuleListener : public ASTReaderListener {
   const LangOptions &ExistingLangOpts;
   DiagnosticsEngine &Diags;
   std::string CurrentFile;
-  const ArrayRef<StringRef> StableDirs;
+  const ArrayRef<SmallString<0>> StableDirs;
 };
 
 /// Visit the given prebuilt module and collect all of the modules it
@@ -176,7 +176,7 @@ static bool visitPrebuiltModule(StringRef 
PrebuiltModuleFilename,
                                 PrebuiltModuleFilesT &ModuleFiles,
                                 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
                                 DiagnosticsEngine &Diags,
-                                const ArrayRef<StringRef> StableDirs) {
+                                const ArrayRef<SmallString<0>> StableDirs) {
   // List of module files to be processed.
   llvm::SmallVector<std::string> Worklist;
 
@@ -459,21 +459,29 @@ std::shared_ptr<CompilerInvocation> 
dependencies::createScanCompilerInvocation(
   return ScanInvocation;
 }
 
-llvm::SmallVector<StringRef>
+SmallVector<SmallString<0>, 2>
 dependencies::getInitialStableDirs(const CompilerInstance &ScanInstance) {
   // Create a collection of stable directories derived from the ScanInstance
   // for determining whether module dependencies would fully resolve from
   // those directories.
-  llvm::SmallVector<StringRef> StableDirs;
+  SmallVector<SmallString<0>, 2> StableDirs;
   const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot;
-  if (!Sysroot.empty() && (llvm::sys::path::root_directory(Sysroot) != 
Sysroot))
-    StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir};
+  if (!Sysroot.empty() && llvm::sys::path::root_directory(Sysroot) != Sysroot) 
{
+    SmallString<0> SysrootBuf = Sysroot;
+    llvm::sys::path::remove_dots(SysrootBuf);
+    StableDirs.emplace_back(std::move(SysrootBuf));
+
+    SmallString<0> ResourceDirBuf{
+        ScanInstance.getHeaderSearchOpts().ResourceDir};
+    llvm::sys::path::remove_dots(ResourceDirBuf);
+    StableDirs.emplace_back(std::move(ResourceDirBuf));
+  }
   return StableDirs;
 }
 
 std::optional<PrebuiltModulesAttrsMap>
 dependencies::computePrebuiltModulesASTMap(
-    CompilerInstance &ScanInstance, llvm::SmallVector<StringRef> &StableDirs) {
+    CompilerInstance &ScanInstance, ArrayRef<SmallString<0>> StableDirs) {
   // Store a mapping of prebuilt module files and their properties like header
   // search options. This will prevent the implicit build to create duplicate
   // modules and will force reuse of the existing prebuilt module files
@@ -512,10 +520,10 @@ dependencies::initializeScanInstanceDependencyCollector(
     DependencyScanningService &Service, CompilerInvocation &Inv,
     DependencyActionController &Controller,
     PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
-    SmallVector<StringRef> &StableDirs) {
+    SmallVector<SmallString<0>, 2> StableDirs) {
   auto MDC = std::make_shared<ModuleDepCollector>(
       Service, std::move(DepOutputOpts), ScanInstance, Controller, Inv,
-      std::move(PrebuiltModulesASTMap), StableDirs);
+      std::move(PrebuiltModulesASTMap), std::move(StableDirs));
   ScanInstance.addDependencyCollector(MDC);
   return MDC;
 }
@@ -737,7 +745,7 @@ bool DependencyScanningAction::runInvocation(
                                    DepFS);
 
     // FIXME: Do this only once.
-    SmallVector<StringRef> StableDirs = getInitialStableDirs(ScanInstance);
+    auto StableDirs = getInitialStableDirs(ScanInstance);
     auto MaybePrebuiltModulesASTMap =
         computePrebuiltModulesASTMap(ScanInstance, StableDirs);
     if (!MaybePrebuiltModulesASTMap)
@@ -760,7 +768,8 @@ bool DependencyScanningAction::runInvocation(
   initializeScanCompilerInstance(ScanInstance, FS, DiagConsumer, Service,
                                  DepFS);
 
-  llvm::SmallVector<StringRef> StableDirs = getInitialStableDirs(ScanInstance);
+  SmallVector<SmallString<0>, 2> StableDirs =
+      getInitialStableDirs(ScanInstance);
   auto MaybePrebuiltModulesASTMap =
       computePrebuiltModulesASTMap(ScanInstance, StableDirs);
   if (!MaybePrebuiltModulesASTMap)
diff --git a/clang/lib/DependencyScanning/ModuleDepCollector.cpp 
b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
index cf23b1f6ec0130..9712803c1abbb8 100644
--- a/clang/lib/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
@@ -193,7 +193,7 @@ void 
dependencies::resetBenignCodeGenOptions(frontend::ActionKind ProgramAction,
   }
 }
 
-bool dependencies::isPathInStableDir(const ArrayRef<StringRef> Directories,
+bool dependencies::isPathInStableDir(const ArrayRef<SmallString<0>> 
Directories,
                                      const StringRef Input) {
   using namespace llvm::sys;
 
@@ -205,8 +205,9 @@ bool dependencies::isPathInStableDir(const 
ArrayRef<StringRef> Directories,
   });
 }
 
-bool dependencies::areOptionsInStableDir(const ArrayRef<StringRef> Directories,
-                                         const HeaderSearchOptions &HSOpts) {
+bool dependencies::areOptionsInStableDir(
+    const ArrayRef<SmallString<0>> Directories,
+    const HeaderSearchOptions &HSOpts) {
   assert(isPathInStableDir(Directories, HSOpts.Sysroot) &&
          "Sysroots differ between module dependencies and current TU");
 
@@ -849,10 +850,10 @@ ModuleDepCollector::ModuleDepCollector(
     CompilerInstance &ScanInstance, DependencyActionController &Controller,
     CompilerInvocation OriginalCI,
     const PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
-    const ArrayRef<StringRef> StableDirs)
+    SmallVector<SmallString<0>, 2> StableDirs)
     : Service(Service), ScanInstance(ScanInstance), Controller(Controller),
       PrebuiltModulesASTMap(std::move(PrebuiltModulesASTMap)),
-      StableDirs(StableDirs), Opts(std::move(Opts)),
+      StableDirs(std::move(StableDirs)), Opts(std::move(Opts)),
       CommonInvocation(
           makeCommonInvocationForModuleBuild(std::move(OriginalCI))) {}
 
diff --git a/llvm/include/llvm/Support/Path.h b/llvm/include/llvm/Support/Path.h
index a8a3c49500a1c5..75edd1a9489643 100644
--- a/llvm/include/llvm/Support/Path.h
+++ b/llvm/include/llvm/Support/Path.h
@@ -180,8 +180,8 @@ LLVM_ABI void replace_extension(SmallVectorImpl<char> &path,
 /// of the last component in order to correctly return false for path 
"/foo/bar"
 /// and prefix "/foo/b", which are likely different file system entities.
 ///
-/// On Windows, this also ignores path separator differences and upper/lower
-/// case differences.
+/// Both \a Path and \a Prefix need to be normalized - without './' components
+/// and with consistent separators.
 LLVM_ABI bool starts_with(StringRef Path, StringRef Prefix,
                           Style style = Style::native);
 
diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp
index a15be5a4fc5bd0..d051904152bab6 100644
--- a/llvm/lib/Support/Path.cpp
+++ b/llvm/lib/Support/Path.cpp
@@ -497,30 +497,16 @@ void replace_extension(SmallVectorImpl<char> &path, const 
Twine &extension,
   path.append(ext.begin(), ext.end());
 }
 
+template <typename T> static T &asLValue(T &&RValue) { return RValue; }
+
 bool starts_with(StringRef Path, StringRef Prefix, Style style) {
-  auto ConsumeFront = [&]() {
-    if (Path.consume_front(Prefix))
-      return true;
-    // Windows prefix matching : case and separator insensitive
-    if (is_style_windows(style)) {
-      if (Path.size() < Prefix.size())
-        return false;
-      for (size_t I = 0, E = Prefix.size(); I != E; ++I) {
-        bool SepPath = is_separator(Path[I], style);
-        bool SepPrefix = is_separator(Prefix[I], style);
-        if (SepPath != SepPrefix)
-          return false;
-        if (!SepPath && toLower(Path[I]) != toLower(Prefix[I]))
-          return false;
-      }
-      Path = Path.drop_front(Prefix.size());
-      return true;
-    }
-    return false;
-  };
+  assert(!remove_dots(asLValue(SmallString<128>{Path}), false, style));
+  assert(!remove_dots(asLValue(SmallString<128>{Prefix}), false, style));
+
   // The path must start with the prefix and last matching component must match
   // in its entirety, not just the prefix.
-  return ConsumeFront() && (Path.empty() || is_separator(Path[0], style));
+  return Path.consume_front(Prefix) &&
+         (Path.empty() || is_separator(Path[0], style));
 }
 
 // Unlike \c starts_with, this returns true even when the last component is 
just

>From be4efd6ad2f329bd7c6b015a872c0de2d147161b Mon Sep 17 00:00:00 2001
From: Jan Svoboda <[email protected]>
Date: Mon, 18 May 2026 13:27:27 -0700
Subject: [PATCH 3/7] Prevent assertion of trailing separators, adjust
 InMemoryFileSystem

---
 llvm/lib/Support/Path.cpp              | 3 ---
 llvm/lib/Support/VirtualFileSystem.cpp | 3 +++
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp
index d051904152bab6..26746d5dff80ac 100644
--- a/llvm/lib/Support/Path.cpp
+++ b/llvm/lib/Support/Path.cpp
@@ -806,9 +806,6 @@ bool remove_dots(SmallVectorImpl<char> &the_path, bool 
remove_dot_dot,
     if (!remaining.empty()) {
       needs_change |= remaining.front() != preferred_separator(style);
       remaining = remaining.drop_front();
-      // The path needs to be rewritten if it has a trailing slash.
-      // FIXME: This is emergent behavior that could be removed.
-      needs_change |= remaining.empty();
     }
 
     // Check for path traversal components or double separators.
diff --git a/llvm/lib/Support/VirtualFileSystem.cpp 
b/llvm/lib/Support/VirtualFileSystem.cpp
index 69f3bb8582b87c..02778f28f90447 100644
--- a/llvm/lib/Support/VirtualFileSystem.cpp
+++ b/llvm/lib/Support/VirtualFileSystem.cpp
@@ -1018,6 +1018,9 @@ InMemoryFileSystem::lookupNode(const Twine &P, bool 
FollowFinalSymlink,
 
   auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
   while (true) {
+    if (*I == ".")
+      return detail::NamedNodeOrError(Path, Dir);
+
     detail::InMemoryNode *Node = Dir->getChild(*I);
     ++I;
     if (!Node)

>From a49be930c427c669f7ca953a45fa3ac3a67c3709 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <[email protected]>
Date: Tue, 19 May 2026 11:17:44 -0700
Subject: [PATCH 4/7] Undo Clang change

---
 .../DependencyScannerImpl.h                   |  9 ++---
 .../DependencyScanning/ModuleDepCollector.h   | 10 +++---
 .../clang/Tooling/DependencyScanningTool.h    |  4 ++-
 .../DependencyScannerImpl.cpp                 | 34 +++++++++++--------
 .../DependencyScanning/ModuleDepCollector.cpp | 11 +++---
 clang/lib/Tooling/DependencyScanningTool.cpp  |  2 +-
 6 files changed, 39 insertions(+), 31 deletions(-)

diff --git a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h 
b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
index 96cfd50a9d4f1a..daa01a28853797 100644
--- a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
+++ b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
@@ -107,12 +107,13 @@ void initializeScanCompilerInstance(
     DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service,
     IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS);
 
-SmallVector<SmallString<0>, 2>
-getInitialStableDirs(const CompilerInstance &ScanInstance);
+SmallVector<StringRef>
+getInitialStableDirs(const CompilerInstance &ScanInstance,
+                     llvm::StringSaver &SS);
 
 std::optional<PrebuiltModulesAttrsMap>
 computePrebuiltModulesASTMap(CompilerInstance &ScanInstance,
-                             ArrayRef<SmallString<0>> StableDirs);
+                             ArrayRef<StringRef> StableDirs);
 
 /// Create the dependency collector that will collect the produced
 /// dependencies. May return the created ModuleDepCollector depending
@@ -123,7 +124,7 @@ std::shared_ptr<ModuleDepCollector> 
initializeScanInstanceDependencyCollector(
     DependencyScanningService &Service, CompilerInvocation &Inv,
     DependencyActionController &Controller,
     PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
-    SmallVector<SmallString<0>, 2> StableDirs);
+    ArrayRef<StringRef> StableDirs);
 } // namespace dependencies
 } // namespace clang
 
diff --git a/clang/include/clang/DependencyScanning/ModuleDepCollector.h 
b/clang/include/clang/DependencyScanning/ModuleDepCollector.h
index f54b9ccf61b378..7e5a292f393ef1 100644
--- a/clang/include/clang/DependencyScanning/ModuleDepCollector.h
+++ b/clang/include/clang/DependencyScanning/ModuleDepCollector.h
@@ -86,7 +86,7 @@ class ModuleDepCollector final : public DependencyCollector {
                      DependencyActionController &Controller,
                      CompilerInvocation OriginalCI,
                      const PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
-                     SmallVector<SmallString<0>, 2> StableDirs);
+                     const ArrayRef<StringRef> StableDirs);
 
   /// Processes the accumulated dependency information and reports it to the
   /// \c Consumer.
@@ -115,7 +115,9 @@ class ModuleDepCollector final : public DependencyCollector 
{
   const PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
   /// Directory paths known to be stable through an active development and 
build
   /// cycle.
-  SmallVector<SmallString<0>, 2> StableDirs;
+  llvm::BumpPtrAllocator Alloc;
+  llvm::StringSaver StableDirsStrings{Alloc};
+  const ArrayRef<StringRef> StableDirs;
   /// Path to the main source file.
   std::string MainFile;
   /// Non-modular file dependencies. This includes the main source file and
@@ -210,7 +212,7 @@ void resetBenignCodeGenOptions(frontend::ActionKind 
ProgramAction,
 ///
 /// \param Directories Paths known to be in a stable location. e.g. Sysroot.
 /// \param Input Path to evaluate.
-bool isPathInStableDir(const ArrayRef<SmallString<0>> Directories,
+bool isPathInStableDir(const ArrayRef<StringRef> Directories,
                        const StringRef Input);
 
 /// Determine if options collected from a module's
@@ -218,7 +220,7 @@ bool isPathInStableDir(const ArrayRef<SmallString<0>> 
Directories,
 ///
 /// \param Directories Paths known to be in a stable location. e.g. Sysroot.
 /// \param HSOpts Header search options derived from the compiler invocation.
-bool areOptionsInStableDir(const ArrayRef<SmallString<0>> Directories,
+bool areOptionsInStableDir(const ArrayRef<StringRef> Directories,
                            const HeaderSearchOptions &HSOpts);
 
 } // end namespace dependencies
diff --git a/clang/include/clang/Tooling/DependencyScanningTool.h 
b/clang/include/clang/Tooling/DependencyScanningTool.h
index 38e7f8aea9d38c..a49c63cf592f49 100644
--- a/clang/include/clang/Tooling/DependencyScanningTool.h
+++ b/clang/include/clang/Tooling/DependencyScanningTool.h
@@ -162,7 +162,9 @@ class CompilerInstanceWithContext {
   std::unique_ptr<DependencyOutputOptions> OutputOpts;
 
   // Context - stable directory handling
-  SmallVector<SmallString<0>, 2> StableDirs;
+  llvm::BumpPtrAllocator Alloc;
+  llvm::StringSaver StableDirsStrings{Alloc};
+  llvm::SmallVector<StringRef> StableDirs;
   dependencies::PrebuiltModulesAttrsMap PrebuiltModuleASTMap;
 
   // Compiler Instance
diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp 
b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
index 2686d8dc47893f..fd25553719c979 100644
--- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
+++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
@@ -67,7 +67,7 @@ class PrebuiltModuleListener : public ASTReaderListener {
                          PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
                          const HeaderSearchOptions &HSOpts,
                          const LangOptions &LangOpts, DiagnosticsEngine &Diags,
-                         const ArrayRef<SmallString<0>> StableDirs)
+                         const ArrayRef<StringRef> StableDirs)
       : PrebuiltModuleFiles(PrebuiltModuleFiles),
         NewModuleFiles(NewModuleFiles),
         PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts),
@@ -166,7 +166,7 @@ class PrebuiltModuleListener : public ASTReaderListener {
   const LangOptions &ExistingLangOpts;
   DiagnosticsEngine &Diags;
   std::string CurrentFile;
-  const ArrayRef<SmallString<0>> StableDirs;
+  const ArrayRef<StringRef> StableDirs;
 };
 
 /// Visit the given prebuilt module and collect all of the modules it
@@ -176,7 +176,7 @@ static bool visitPrebuiltModule(StringRef 
PrebuiltModuleFilename,
                                 PrebuiltModuleFilesT &ModuleFiles,
                                 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
                                 DiagnosticsEngine &Diags,
-                                const ArrayRef<SmallString<0>> StableDirs) {
+                                const ArrayRef<StringRef> StableDirs) {
   // List of module files to be processed.
   llvm::SmallVector<std::string> Worklist;
 
@@ -459,29 +459,30 @@ std::shared_ptr<CompilerInvocation> 
dependencies::createScanCompilerInvocation(
   return ScanInvocation;
 }
 
-SmallVector<SmallString<0>, 2>
-dependencies::getInitialStableDirs(const CompilerInstance &ScanInstance) {
+SmallVector<StringRef>
+dependencies::getInitialStableDirs(const CompilerInstance &ScanInstance,
+                                   llvm::StringSaver &SS) {
   // Create a collection of stable directories derived from the ScanInstance
   // for determining whether module dependencies would fully resolve from
   // those directories.
-  SmallVector<SmallString<0>, 2> StableDirs;
+  SmallVector<StringRef> StableDirs;
   const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot;
   if (!Sysroot.empty() && llvm::sys::path::root_directory(Sysroot) != Sysroot) 
{
     SmallString<0> SysrootBuf = Sysroot;
     llvm::sys::path::remove_dots(SysrootBuf);
-    StableDirs.emplace_back(std::move(SysrootBuf));
+    StableDirs.emplace_back(SS.save(Twine(SysrootBuf)));
 
     SmallString<0> ResourceDirBuf{
         ScanInstance.getHeaderSearchOpts().ResourceDir};
     llvm::sys::path::remove_dots(ResourceDirBuf);
-    StableDirs.emplace_back(std::move(ResourceDirBuf));
+    StableDirs.emplace_back(SS.save(Twine(ResourceDirBuf)));
   }
   return StableDirs;
 }
 
 std::optional<PrebuiltModulesAttrsMap>
-dependencies::computePrebuiltModulesASTMap(
-    CompilerInstance &ScanInstance, ArrayRef<SmallString<0>> StableDirs) {
+dependencies::computePrebuiltModulesASTMap(CompilerInstance &ScanInstance,
+                                           ArrayRef<StringRef> StableDirs) {
   // Store a mapping of prebuilt module files and their properties like header
   // search options. This will prevent the implicit build to create duplicate
   // modules and will force reuse of the existing prebuilt module files
@@ -520,10 +521,10 @@ dependencies::initializeScanInstanceDependencyCollector(
     DependencyScanningService &Service, CompilerInvocation &Inv,
     DependencyActionController &Controller,
     PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
-    SmallVector<SmallString<0>, 2> StableDirs) {
+    ArrayRef<StringRef> StableDirs) {
   auto MDC = std::make_shared<ModuleDepCollector>(
       Service, std::move(DepOutputOpts), ScanInstance, Controller, Inv,
-      std::move(PrebuiltModulesASTMap), std::move(StableDirs));
+      std::move(PrebuiltModulesASTMap), StableDirs);
   ScanInstance.addDependencyCollector(MDC);
   return MDC;
 }
@@ -745,7 +746,9 @@ bool DependencyScanningAction::runInvocation(
                                    DepFS);
 
     // FIXME: Do this only once.
-    auto StableDirs = getInitialStableDirs(ScanInstance);
+    llvm::BumpPtrAllocator Alloc;
+    llvm::StringSaver SS(Alloc);
+    auto StableDirs = getInitialStableDirs(ScanInstance, SS);
     auto MaybePrebuiltModulesASTMap =
         computePrebuiltModulesASTMap(ScanInstance, StableDirs);
     if (!MaybePrebuiltModulesASTMap)
@@ -768,8 +771,9 @@ bool DependencyScanningAction::runInvocation(
   initializeScanCompilerInstance(ScanInstance, FS, DiagConsumer, Service,
                                  DepFS);
 
-  SmallVector<SmallString<0>, 2> StableDirs =
-      getInitialStableDirs(ScanInstance);
+  llvm::BumpPtrAllocator Alloc;
+  llvm::StringSaver SS(Alloc);
+  SmallVector<StringRef> StableDirs = getInitialStableDirs(ScanInstance, SS);
   auto MaybePrebuiltModulesASTMap =
       computePrebuiltModulesASTMap(ScanInstance, StableDirs);
   if (!MaybePrebuiltModulesASTMap)
diff --git a/clang/lib/DependencyScanning/ModuleDepCollector.cpp 
b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
index 9712803c1abbb8..cf23b1f6ec0130 100644
--- a/clang/lib/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
@@ -193,7 +193,7 @@ void 
dependencies::resetBenignCodeGenOptions(frontend::ActionKind ProgramAction,
   }
 }
 
-bool dependencies::isPathInStableDir(const ArrayRef<SmallString<0>> 
Directories,
+bool dependencies::isPathInStableDir(const ArrayRef<StringRef> Directories,
                                      const StringRef Input) {
   using namespace llvm::sys;
 
@@ -205,9 +205,8 @@ bool dependencies::isPathInStableDir(const 
ArrayRef<SmallString<0>> Directories,
   });
 }
 
-bool dependencies::areOptionsInStableDir(
-    const ArrayRef<SmallString<0>> Directories,
-    const HeaderSearchOptions &HSOpts) {
+bool dependencies::areOptionsInStableDir(const ArrayRef<StringRef> Directories,
+                                         const HeaderSearchOptions &HSOpts) {
   assert(isPathInStableDir(Directories, HSOpts.Sysroot) &&
          "Sysroots differ between module dependencies and current TU");
 
@@ -850,10 +849,10 @@ ModuleDepCollector::ModuleDepCollector(
     CompilerInstance &ScanInstance, DependencyActionController &Controller,
     CompilerInvocation OriginalCI,
     const PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
-    SmallVector<SmallString<0>, 2> StableDirs)
+    const ArrayRef<StringRef> StableDirs)
     : Service(Service), ScanInstance(ScanInstance), Controller(Controller),
       PrebuiltModulesASTMap(std::move(PrebuiltModulesASTMap)),
-      StableDirs(std::move(StableDirs)), Opts(std::move(Opts)),
+      StableDirs(StableDirs), Opts(std::move(Opts)),
       CommonInvocation(
           makeCommonInvocationForModuleBuild(std::move(OriginalCI))) {}
 
diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp 
b/clang/lib/Tooling/DependencyScanningTool.cpp
index b4b45798c514d4..517615741f78a2 100644
--- a/clang/lib/Tooling/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanningTool.cpp
@@ -473,7 +473,7 @@ bool CompilerInstanceWithContext::initialize(
       CI, std::move(FS), DiagEngineWithCmdAndOpts->DiagEngine->getClient(),
       Worker.Service, Worker.DepFS);
 
-  StableDirs = getInitialStableDirs(CI);
+  StableDirs = getInitialStableDirs(CI, StableDirsStrings);
   auto MaybePrebuiltModulesASTMap =
       computePrebuiltModulesASTMap(CI, StableDirs);
   if (!MaybePrebuiltModulesASTMap)

>From abd47e55ba761fb7144eb57a9bd51928bcb338ea Mon Sep 17 00:00:00 2001
From: Jan Svoboda <[email protected]>
Date: Tue, 19 May 2026 11:21:38 -0700
Subject: [PATCH 5/7] Undo `remove_dots` change

---
 llvm/lib/Support/Path.cpp              | 3 +++
 llvm/lib/Support/VirtualFileSystem.cpp | 3 ---
 llvm/unittests/Support/Path.cpp        | 5 +----
 3 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp
index 26746d5dff80ac..d051904152bab6 100644
--- a/llvm/lib/Support/Path.cpp
+++ b/llvm/lib/Support/Path.cpp
@@ -806,6 +806,9 @@ bool remove_dots(SmallVectorImpl<char> &the_path, bool 
remove_dot_dot,
     if (!remaining.empty()) {
       needs_change |= remaining.front() != preferred_separator(style);
       remaining = remaining.drop_front();
+      // The path needs to be rewritten if it has a trailing slash.
+      // FIXME: This is emergent behavior that could be removed.
+      needs_change |= remaining.empty();
     }
 
     // Check for path traversal components or double separators.
diff --git a/llvm/lib/Support/VirtualFileSystem.cpp 
b/llvm/lib/Support/VirtualFileSystem.cpp
index 02778f28f90447..69f3bb8582b87c 100644
--- a/llvm/lib/Support/VirtualFileSystem.cpp
+++ b/llvm/lib/Support/VirtualFileSystem.cpp
@@ -1018,9 +1018,6 @@ InMemoryFileSystem::lookupNode(const Twine &P, bool 
FollowFinalSymlink,
 
   auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
   while (true) {
-    if (*I == ".")
-      return detail::NamedNodeOrError(Path, Dir);
-
     detail::InMemoryNode *Node = Dir->getChild(*I);
     ++I;
     if (!Node)
diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp
index f9d10466d67943..beb109bed983b5 100644
--- a/llvm/unittests/Support/Path.cpp
+++ b/llvm/unittests/Support/Path.cpp
@@ -443,10 +443,7 @@ TEST(Support, AbsolutePathIteratorEnd) {
 
 TEST(Support, PathStartsWith) {
   EXPECT_TRUE(path::starts_with("/foo", "/foo"));
-  EXPECT_TRUE(path::starts_with("/foo/", "/foo"));
-  // FIXME: This should probably work too.
-  // EXPECT_TRUE(path::starts_with("/foo", "/foo/"));
-  EXPECT_TRUE(path::starts_with("/foo/", "/foo/"));
+  EXPECT_TRUE(path::starts_with("/foo/bar", "/foo"));
   EXPECT_FALSE(path::starts_with("/foo", "/fooo"));
   EXPECT_FALSE(path::starts_with("/fooo", "/foo"));
 }

>From fca5ad81865ca63d013e25dd3781bcf0d00e5189 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <[email protected]>
Date: Tue, 19 May 2026 11:28:23 -0700
Subject: [PATCH 6/7] Documentation wording

---
 llvm/include/llvm/Support/Path.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/Support/Path.h b/llvm/include/llvm/Support/Path.h
index 75edd1a9489643..9d28a23975f203 100644
--- a/llvm/include/llvm/Support/Path.h
+++ b/llvm/include/llvm/Support/Path.h
@@ -180,8 +180,8 @@ LLVM_ABI void replace_extension(SmallVectorImpl<char> &path,
 /// of the last component in order to correctly return false for path 
"/foo/bar"
 /// and prefix "/foo/b", which are likely different file system entities.
 ///
-/// Both \a Path and \a Prefix need to be normalized - without './' components
-/// and with consistent separators.
+/// Both \a Path and \a Prefix must be normalized - without './' components and
+/// with consistent separators.
 LLVM_ABI bool starts_with(StringRef Path, StringRef Prefix,
                           Style style = Style::native);
 

>From ff5058ad92ba622923128675d95787b624271507 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <[email protected]>
Date: Tue, 19 May 2026 13:06:06 -0700
Subject: [PATCH 7/7] Fix unit tests

---
 llvm/unittests/Support/Path.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp
index beb109bed983b5..25918d64134369 100644
--- a/llvm/unittests/Support/Path.cpp
+++ b/llvm/unittests/Support/Path.cpp
@@ -442,10 +442,10 @@ TEST(Support, AbsolutePathIteratorEnd) {
 }
 
 TEST(Support, PathStartsWith) {
-  EXPECT_TRUE(path::starts_with("/foo", "/foo"));
-  EXPECT_TRUE(path::starts_with("/foo/bar", "/foo"));
-  EXPECT_FALSE(path::starts_with("/foo", "/fooo"));
-  EXPECT_FALSE(path::starts_with("/fooo", "/foo"));
+  EXPECT_TRUE(path::starts_with("/foo", "/foo", path::Style::posix));
+  EXPECT_TRUE(path::starts_with("/foo/bar", "/foo", path::Style::posix));
+  EXPECT_FALSE(path::starts_with("/foo", "/fooo", path::Style::posix));
+  EXPECT_FALSE(path::starts_with("/fooo", "/foo", path::Style::posix));
 }
 
 #ifdef _WIN32

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

Reply via email to