Make rebase_path("//foo", "//foo/bar/baz") return "../..".

Before this change, it returned "../../../foo" which technically
wasn't wrong, but it was ugly.

It's also slightly less code than the less general previous similar
fix https://codereview.chromium.org/2346913003

Bug: gn:166, chromium:647679
Change-Id: Ieac42c317426b86ef2a56371d8b83ce921aa31c3
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/8421
Commit-Queue: Brett Wilson <brettw@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/filesystem_utils.cc b/src/gn/filesystem_utils.cc
index bc95dce..ae6d4d9 100644
--- a/src/gn/filesystem_utils.cc
+++ b/src/gn/filesystem_utils.cc
@@ -415,7 +415,7 @@
   // part after it.
 
   if (!IsSlash(path[after_common_index])) {
-    // path is ${source-root}SUFIX/...
+    // path is ${source-root}SUFFIX/...
     return false;
   }
   // A source-root relative path, The input may have an unknown number of
@@ -440,7 +440,7 @@
       *dest = "//";
       return true;
     } else if (!IsSlash(path[source_root_len])) {
-      // path is ${source-root}SUFIX/...
+      // path is ${source-root}SUFFIX/...
       return false;
     }
     // A source-root relative path, The input may have an unknown number of
@@ -671,14 +671,16 @@
   }
 #endif
 
+  DCHECK(EndsWithSlash(dest));
   std::string ret;
 
   // Skip the common prefixes of the source and dest as long as they end in
-  // a [back]slash.
+  // a [back]slash or end the string. dest always ends with a (back)slash in
+  // this function, so checking dest for just that is sufficient.
   size_t common_prefix_len = 0;
   size_t max_common_length = std::min(input.size(), dest.size());
-  for (size_t i = common_prefix_len; i < max_common_length; i++) {
-    if (IsSlash(input[i]) && IsSlash(dest[i]))
+  for (size_t i = common_prefix_len; i <= max_common_length; i++) {
+    if ((IsSlash(input[i]) || input[i] == '\0') && IsSlash(dest[i]))
       common_prefix_len = i + 1;
     else if (input[i] != dest[i])
       break;
@@ -691,7 +693,10 @@
   }
 
   // Append any remaining unique input.
-  ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
+  if (common_prefix_len <= input.size())
+    ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
+  else if (input.back() != '/' && !ret.empty())
+    ret.pop_back();
 
   // If the result is still empty, the paths are the same.
   if (ret.empty())
@@ -745,7 +750,7 @@
     }
     ret = MakeRelativePath(input_full, dest_full);
     if (remove_slash && ret.size() > 1)
-      ret.resize(ret.size() - 1);
+      ret.pop_back();
     return ret;
   }
 
diff --git a/src/gn/filesystem_utils_unittest.cc b/src/gn/filesystem_utils_unittest.cc
index 3d30d52..347d0fe 100644
--- a/src/gn/filesystem_utils_unittest.cc
+++ b/src/gn/filesystem_utils_unittest.cc
@@ -451,6 +451,7 @@
 
   // Sharing prefix.
   EXPECT_EQ("foo", RebasePath("//a/foo", SourceDir("//a/"), source_root));
+  EXPECT_EQ("foo", RebasePath("//a/foo", SourceDir("//a"), source_root));
   EXPECT_EQ("foo/", RebasePath("//a/foo/", SourceDir("//a/"), source_root));
   EXPECT_EQ("foo", RebasePath("//a/b/foo", SourceDir("//a/b/"), source_root));
   EXPECT_EQ("foo/", RebasePath("//a/b/foo/", SourceDir("//a/b/"), source_root));
@@ -458,13 +459,12 @@
             RebasePath("//a/b/foo/bar", SourceDir("//a/b/"), source_root));
   EXPECT_EQ("foo/bar/",
             RebasePath("//a/b/foo/bar/", SourceDir("//a/b/"), source_root));
-
-  // One could argue about this case. Since the input doesn't have a slash it
-  // would normally not be treated like a directory and we'd go up, which is
-  // simpler. However, since it matches the output directory's name, we could
-  // potentially infer that it's the same and return "." for this.
-  EXPECT_EQ("../bar",
+  EXPECT_EQ(".",
             RebasePath("//foo/bar", SourceDir("//foo/bar/"), source_root));
+  EXPECT_EQ("..",
+            RebasePath("//foo", SourceDir("//foo/bar/"), source_root));
+  EXPECT_EQ("../",
+            RebasePath("//foo/", SourceDir("//foo/bar/"), source_root));
 
   // Check when only |input| is system-absolute
   EXPECT_EQ("foo", RebasePath("/source/root/foo", SourceDir("//"),
diff --git a/src/gn/function_rebase_path.cc b/src/gn/function_rebase_path.cc
index bf7ec42..7e912aa 100644
--- a/src/gn/function_rebase_path.cc
+++ b/src/gn/function_rebase_path.cc
@@ -106,17 +106,9 @@
         value, err, scope->settings()->build_settings()->root_path_utf8());
     if (err->has_error())
       return Value();
-    // Special case:
-    //   rebase_path("//foo", "//bar") ==> "../foo"
-    //   rebase_path("//foo", "//foo") ==> "." and not "../foo"
-    if (resolved_file.value() ==
-        to_dir.value().substr(0, to_dir.value().size() - 1)) {
-      result.string_value() = ".";
-    } else {
-      result.string_value() =
-          RebasePath(resolved_file.value(), to_dir,
-                     scope->settings()->build_settings()->root_path_utf8());
-    }
+    result.string_value() =
+        RebasePath(resolved_file.value(), to_dir,
+                   scope->settings()->build_settings()->root_path_utf8());
   }
 
   return result;
@@ -151,12 +143,12 @@
   with a double slash like "//foo/bar"), you should use the get_path_info()
   function. This function won't work because it will always make relative
   paths, and it needs to support making paths relative to the source root, so
-  can't also generate source-absolute paths without more special-cases.
+  it can't also generate source-absolute paths without more special-cases.
 
 Arguments
 
   input
-      A string or list of strings representing file or directory names These
+      A string or list of strings representing file or directory names. These
       can be relative paths ("foo/bar.txt"), system absolute paths
       ("/foo/bar.txt"), or source absolute paths ("//foo/bar.txt").