Extract the `isLocatedUnderRootPath` check (#169)

Previously, we were incrementing the pointer with the first `res.second++`, so the next part of the if statement would see the character after that.

This extracts that code, fixes the issue and adds unit tests.
diff --git a/include/llbuild/BuildSystem/BuildSystem.h b/include/llbuild/BuildSystem/BuildSystem.h
index 0823eaa..2d91337 100644
--- a/include/llbuild/BuildSystem/BuildSystem.h
+++ b/include/llbuild/BuildSystem/BuildSystem.h
@@ -39,6 +39,8 @@
 class BuildValue;
 class Command;
 class Tool;
+
+bool pathIsPrefixedByPath(std::string path, std::string prefixPath);
   
 class BuildSystemDelegate {
   // DO NOT COPY
diff --git a/lib/BuildSystem/BuildSystem.cpp b/lib/BuildSystem/BuildSystem.cpp
index 9f6565d..2965c94 100644
--- a/lib/BuildSystem/BuildSystem.cpp
+++ b/lib/BuildSystem/BuildSystem.cpp
@@ -2457,8 +2457,7 @@
 
       // Check if the file is located under one of the allowed root paths.
       for (auto root : roots) {
-        auto res = std::mismatch(root.begin(), root.end(), fileToDelete.begin());
-        if (res.first == root.end() && ((*(res.second++) == '\0') || (*(res.second++) == path_separator))) {
+        if (pathIsPrefixedByPath(fileToDelete, root)) {
           isLocatedUnderRootPath = true;
         }
       }
@@ -2650,3 +2649,13 @@
 void BuildSystem::resetForBuild() {
   static_cast<BuildSystemImpl*>(impl)->resetForBuild();
 }
+
+// This function checks if the given path is prefixed by another path.
+bool llbuild::buildsystem::pathIsPrefixedByPath(std::string path, std::string prefixPath) {
+  static char path_separator = llvm::sys::path::get_separator()[0];
+  auto res = std::mismatch(prefixPath.begin(), prefixPath.end(), path.begin());
+  // Check if `prefixPath` has been exhausted or just a separator remains.
+  bool isPrefix = res.first == prefixPath.end() || (*(res.first++) == path_separator);
+  // Check if `path` has been exhausted or just a separator remains.
+  return isPrefix && (res.second == path.end() || (*(res.second++) == path_separator));
+}
diff --git a/unittests/BuildSystem/BuildSystemTaskTests.cpp b/unittests/BuildSystem/BuildSystemTaskTests.cpp
index 4c932c0..7397774 100644
--- a/unittests/BuildSystem/BuildSystemTaskTests.cpp
+++ b/unittests/BuildSystem/BuildSystemTaskTests.cpp
@@ -776,4 +776,14 @@
   ASSERT_FALSE(std::find(messages.begin(), messages.end(), "cannot remove stale file '" + linkFileList + "': No such file or directory") != messages.end());
 }
 
+TEST(BuildSystemTaskTests, staleFileRemovalPathIsPrefixedByPath) {
+  ASSERT_TRUE(pathIsPrefixedByPath("/foo/bar", "/foo"));
+  ASSERT_TRUE(pathIsPrefixedByPath("/foo", "/foo"));
+  ASSERT_TRUE(pathIsPrefixedByPath("/foo/", "/foo"));
+  ASSERT_TRUE(pathIsPrefixedByPath("/foo", "/foo/"));
+
+  ASSERT_FALSE(pathIsPrefixedByPath("/bar", "/foo"));
+  ASSERT_FALSE(pathIsPrefixedByPath("/foobar", "/foo"));
+}
+
 }