Allow labels to go above the source root.

Supports paths in labels that go above the source root. These will then
be expanded to the absolute location of the source root on the local
system, and the relative directories will be expanded from there. This
patches the behavior for file path handling.

For example, if the current directory is "//" and the source root is
"/home/me/project", the strings "../..:foo" and "//../..:foo" will
resolve to "/home:foo".

Change-Id: I5e0a73105a24d91c9c6dc8905663421493caffe6
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/7480
Reviewed-by: Scott Graham <scottmg@chromium.org>
Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/action_target_generator.cc b/src/gn/action_target_generator.cc
index ea5b0ae..73fa288 100644
--- a/src/gn/action_target_generator.cc
+++ b/src/gn/action_target_generator.cc
@@ -162,8 +162,10 @@
   if (!value)
     return true;
 
-  Label label = Label::Resolve(scope_->GetSourceDir(),
-                               ToolchainLabelForScope(scope_), *value, err_);
+  Label label =
+      Label::Resolve(scope_->GetSourceDir(),
+                     scope_->settings()->build_settings()->root_path_utf8(),
+                     ToolchainLabelForScope(scope_), *value, err_);
   if (err_->has_error())
     return false;
 
diff --git a/src/gn/analyzer.cc b/src/gn/analyzer.cc
index 18563b8..c269af7 100644
--- a/src/gn/analyzer.cc
+++ b/src/gn/analyzer.cc
@@ -116,8 +116,8 @@
                "\"" + s + "\" is not a source-absolute or absolute path.");
     return Label();
   }
-  return Label::Resolve(SourceDir("//"), default_toolchain, Value(nullptr, s),
-                        err);
+  return Label::Resolve(SourceDir("//"), std::string_view(), default_toolchain,
+                        Value(nullptr, s), err);
 }
 
 Err JSONToInputs(const Label& default_toolchain,
diff --git a/src/gn/binary_target_generator.cc b/src/gn/binary_target_generator.cc
index 1098a0c..7322adb 100644
--- a/src/gn/binary_target_generator.cc
+++ b/src/gn/binary_target_generator.cc
@@ -130,7 +130,8 @@
 bool BinaryTargetGenerator::FillFriends() {
   const Value* value = scope_->GetValue(variables::kFriend, true);
   if (value) {
-    return ExtractListOfLabelPatterns(*value, scope_->GetSourceDir(),
+    return ExtractListOfLabelPatterns(scope_->settings()->build_settings(),
+                                      *value, scope_->GetSourceDir(),
                                       &target_->friends(), err_);
   }
   return true;
@@ -186,7 +187,8 @@
     return true;
 
   UniqueVector<Label> circular;
-  ExtractListOfUniqueLabels(*value, scope_->GetSourceDir(),
+  ExtractListOfUniqueLabels(scope_->settings()->build_settings(), *value,
+                            scope_->GetSourceDir(),
                             ToolchainLabelForScope(scope_), &circular, err_);
   if (err_->has_error())
     return false;
diff --git a/src/gn/command_gen.cc b/src/gn/command_gen.cc
index fb33f30..57828ad 100644
--- a/src/gn/command_gen.cc
+++ b/src/gn/command_gen.cc
@@ -490,7 +490,8 @@
     return 1;
   }
 
-  if (!WriteRuntimeDepsFilesIfNecessary(setup->builder(), &err)) {
+  if (!WriteRuntimeDepsFilesIfNecessary(&setup->build_settings(),
+                                        setup->builder(), &err)) {
     err.PrintToStdout();
     return 1;
   }
diff --git a/src/gn/commands.cc b/src/gn/commands.cc
index b1ea76c..6dcf16a 100644
--- a/src/gn/commands.cc
+++ b/src/gn/commands.cc
@@ -38,7 +38,7 @@
   Err err;
   LabelPattern pattern = LabelPattern::GetPattern(
       SourceDirForCurrentDirectory(setup->build_settings().root_path()),
-      pattern_value, &err);
+      setup->build_settings().root_path_utf8(), pattern_value, &err);
   if (err.has_error()) {
     err.PrintToStdout();
     return false;
@@ -86,9 +86,9 @@
 
   // Try to figure out what this thing is.
   Err err;
-  Label label =
-      Label::Resolve(current_dir, setup->loader()->default_toolchain_label(),
-                     Value(nullptr, input), &err);
+  Label label = Label::Resolve(
+      current_dir, setup->build_settings().root_path_utf8(),
+      setup->loader()->default_toolchain_label(), Value(nullptr, input), &err);
   if (err.has_error()) {
     // Not a valid label, assume this must be a file.
     err = Err();
@@ -405,7 +405,8 @@
   Err err;
   Label label = Label::Resolve(
       SourceDirForCurrentDirectory(setup->build_settings().root_path()),
-      default_toolchain, arg_value, &err);
+      setup->build_settings().root_path_utf8(), default_toolchain, arg_value,
+      &err);
   if (err.has_error()) {
     err.PrintToStdout();
     return nullptr;
@@ -496,7 +497,8 @@
   filters->reserve(tokens.size());
   for (const std::string& token : tokens) {
     LabelPattern pattern = LabelPattern::GetPattern(
-        root_dir, Value(nullptr, FixGitBashLabelEdit(token)), err);
+        root_dir, build_settings->root_path_utf8(),
+        Value(nullptr, FixGitBashLabelEdit(token)), err);
     if (err->has_error())
       return false;
     filters->push_back(pattern);
diff --git a/src/gn/create_bundle_target_generator.cc b/src/gn/create_bundle_target_generator.cc
index 1632572..dae8683 100644
--- a/src/gn/create_bundle_target_generator.cc
+++ b/src/gn/create_bundle_target_generator.cc
@@ -293,8 +293,9 @@
   std::vector<LabelPattern>& bundle_deps_filter =
       target_->bundle_data().bundle_deps_filter();
   for (const auto& item : value->list_value()) {
-    bundle_deps_filter.push_back(
-        LabelPattern::GetPattern(current_dir, item, err_));
+    bundle_deps_filter.push_back(LabelPattern::GetPattern(
+        current_dir, scope_->settings()->build_settings()->root_path_utf8(),
+        item, err_));
     if (err_->has_error())
       return false;
   }
diff --git a/src/gn/function_get_label_info.cc b/src/gn/function_get_label_info.cc
index d915732..04fa9a7 100644
--- a/src/gn/function_get_label_info.cc
+++ b/src/gn/function_get_label_info.cc
@@ -85,8 +85,10 @@
   }
 
   // Resolve the requested label.
-  Label label = Label::Resolve(scope->GetSourceDir(),
-                               ToolchainLabelForScope(scope), args[0], err);
+  Label label =
+      Label::Resolve(scope->GetSourceDir(),
+                     scope->settings()->build_settings()->root_path_utf8(),
+                     ToolchainLabelForScope(scope), args[0], err);
   if (label.is_null())
     return Value();
 
diff --git a/src/gn/function_get_target_outputs.cc b/src/gn/function_get_target_outputs.cc
index f7c1c3e..7995a49 100644
--- a/src/gn/function_get_target_outputs.cc
+++ b/src/gn/function_get_target_outputs.cc
@@ -73,8 +73,10 @@
   }
 
   // Resolve the requested label.
-  Label label = Label::Resolve(scope->GetSourceDir(),
-                               ToolchainLabelForScope(scope), args[0], err);
+  Label label =
+      Label::Resolve(scope->GetSourceDir(),
+                     scope->settings()->build_settings()->root_path_utf8(),
+                     ToolchainLabelForScope(scope), args[0], err);
   if (label.is_null())
     return Value();
 
diff --git a/src/gn/function_set_default_toolchain.cc b/src/gn/function_set_default_toolchain.cc
index 8620b2e..1369377 100644
--- a/src/gn/function_set_default_toolchain.cc
+++ b/src/gn/function_set_default_toolchain.cc
@@ -75,8 +75,9 @@
 
   if (!EnsureSingleStringArg(function, args, err))
     return Value();
-  Label toolchain_label(
-      Label::Resolve(current_dir, default_toolchain, args[0], err));
+  Label toolchain_label(Label::Resolve(
+      current_dir, scope->settings()->build_settings()->root_path_utf8(),
+      default_toolchain, args[0], err));
   if (toolchain_label.is_null())
     return Value();
 
diff --git a/src/gn/function_toolchain.cc b/src/gn/function_toolchain.cc
index 0efcd1e..e5ddc59 100644
--- a/src/gn/function_toolchain.cc
+++ b/src/gn/function_toolchain.cc
@@ -221,7 +221,8 @@
   // Read deps (if any).
   const Value* deps_value = block_scope.GetValue(variables::kDeps, true);
   if (deps_value) {
-    ExtractListOfLabels(*deps_value, block_scope.GetSourceDir(),
+    ExtractListOfLabels(scope->settings()->build_settings(), *deps_value,
+                        block_scope.GetSourceDir(),
                         ToolchainLabelForScope(&block_scope),
                         &toolchain->deps(), err);
     if (err->has_error())
diff --git a/src/gn/functions.cc b/src/gn/functions.cc
index 640bcea..abbf188 100644
--- a/src/gn/functions.cc
+++ b/src/gn/functions.cc
@@ -259,8 +259,8 @@
 
   // Assertion failed; try to make a useful message and report it.
   if (args.size() == 2) {
-    *err = Err(function->function(), "Assertion failed.",
-               args[1].string_value());
+    *err =
+        Err(function->function(), "Assertion failed.", args[1].string_value());
   } else {
     *err = Err(function->function(), "Assertion failed.");
   }
@@ -383,7 +383,8 @@
   // Read sub-configs.
   const Value* configs_value = scope->GetValue(variables::kConfigs, true);
   if (configs_value) {
-    ExtractListOfUniqueLabels(*configs_value, scope->GetSourceDir(),
+    ExtractListOfUniqueLabels(scope->settings()->build_settings(),
+                              *configs_value, scope->GetSourceDir(),
                               ToolchainLabelForScope(scope), &config->configs(),
                               err);
   }
@@ -794,7 +795,7 @@
       // We don't need the return value, we invoke scope::GetValue only to mark
       // the value as used. Note that we cannot use Scope::MarkUsed because we
       // want to also search in the parent scope.
-      (void) source->GetValue(cur.string_value(), true);
+      (void)source->GetValue(cur.string_value(), true);
     }
     return Value();
   }
@@ -1162,16 +1163,20 @@
 
   // Check usage: separator is a string.
   if (!args[0].VerifyTypeIs(Value::STRING, err)) {
-    *err = Err(function, "separator in string_join(separator, strings) is not "
-               "a string", "Expecting separator argument to be a string.");
+    *err = Err(function,
+               "separator in string_join(separator, strings) is not "
+               "a string",
+               "Expecting separator argument to be a string.");
     return Value();
   }
   const std::string separator = args[0].string_value();
 
   // Check usage: strings is a list.
   if (!args[1].VerifyTypeIs(Value::LIST, err)) {
-    *err = Err(function, "strings in string_join(separator, strings) "
-               "is not a list", "Expecting strings argument to be a list.");
+    *err = Err(function,
+               "strings in string_join(separator, strings) "
+               "is not a list",
+               "Expecting strings argument to be a list.");
     return Value();
   }
   const std::vector<Value> strings = args[1].list_value();
@@ -1313,8 +1318,10 @@
     }
     separator = args[1].string_value();
     if (separator.empty()) {
-      *err = Err(function, "Separator argument to string_split() "
-                 "cannot be empty string", "Usage: string_split(str[, sep])");
+      *err = Err(function,
+                 "Separator argument to string_split() "
+                 "cannot be empty string",
+                 "Usage: string_split(str[, sep])");
       return Value();
     }
   }
diff --git a/src/gn/header_checker_unittest.cc b/src/gn/header_checker_unittest.cc
index ba59f5d..a290d45 100644
--- a/src/gn/header_checker_unittest.cc
+++ b/src/gn/header_checker_unittest.cc
@@ -404,8 +404,8 @@
 
   // List A as a friend of C.
   Err err;
-  c_.friends().push_back(
-      LabelPattern::GetPattern(SourceDir("//"), Value(nullptr, "//a:*"), &err));
+  c_.friends().push_back(LabelPattern::GetPattern(
+      SourceDir("//"), std::string_view(), Value(nullptr, "//a:*"), &err));
   ASSERT_FALSE(err.has_error());
 
   // Must be after setting everything up for it to find the files.
diff --git a/src/gn/label.cc b/src/gn/label.cc
index 9bba8b5..4811196 100644
--- a/src/gn/label.cc
+++ b/src/gn/label.cc
@@ -29,6 +29,7 @@
 // used. The value is used only for generating error messages.
 bool ComputeBuildLocationFromDep(const Value& input_value,
                                  const SourceDir& current_dir,
+                                 const std::string_view& source_root,
                                  const std::string_view& input,
                                  SourceDir* result,
                                  Err* err) {
@@ -38,7 +39,8 @@
     return true;
   }
 
-  *result = current_dir.ResolveRelativeDir(input_value, input, err);
+  *result =
+      current_dir.ResolveRelativeDir(input_value, input, err, source_root);
   return true;
 }
 
@@ -86,6 +88,7 @@
 // Returns true on success. On failure, the out* variables might be written to
 // but shouldn't be used.
 bool Resolve(const SourceDir& current_dir,
+             const std::string_view& source_root,
              const Label& current_toolchain,
              const Value& original_value,
              const std::string_view& input,
@@ -167,8 +170,8 @@
     return false;
   }
 
-  if (!ComputeBuildLocationFromDep(original_value, current_dir, location_piece,
-                                   out_dir, err))
+  if (!ComputeBuildLocationFromDep(original_value, current_dir, source_root,
+                                   location_piece, out_dir, err))
     return false;
 
   if (!ComputeTargetNameFromDep(original_value, *out_dir, name_piece, out_name,
@@ -185,9 +188,9 @@
       *out_toolchain_name = current_toolchain.name();
       return true;
     } else {
-      return Resolve(current_dir, current_toolchain, original_value,
-                     toolchain_piece, out_toolchain_dir, out_toolchain_name,
-                     nullptr, nullptr, err);
+      return Resolve(current_dir, source_root, current_toolchain,
+                     original_value, toolchain_piece, out_toolchain_dir,
+                     out_toolchain_name, nullptr, nullptr, err);
     }
   }
   return true;
@@ -265,6 +268,7 @@
 
 // static
 Label Label::Resolve(const SourceDir& current_dir,
+                     const std::string_view& source_root,
                      const Label& current_toolchain,
                      const Value& input,
                      Err* err) {
@@ -279,8 +283,9 @@
     return ret;
   }
 
-  if (!::Resolve(current_dir, current_toolchain, input, input_string, &ret.dir_,
-                 &ret.name_, &ret.toolchain_dir_, &ret.toolchain_name_, err))
+  if (!::Resolve(current_dir, source_root, current_toolchain, input,
+                 input_string, &ret.dir_, &ret.name_, &ret.toolchain_dir_,
+                 &ret.toolchain_name_, err))
     return Label();
   return ret;
 }
diff --git a/src/gn/label.h b/src/gn/label.h
index 3be06e0..c266828 100644
--- a/src/gn/label.h
+++ b/src/gn/label.h
@@ -35,6 +35,7 @@
   // current directory into a fully qualified label. On failure returns an
   // is_null() label and sets the error.
   static Label Resolve(const SourceDir& current_dir,
+                       const std::string_view& source_root,
                        const Label& current_toolchain,
                        const Value& input,
                        Err* err);
diff --git a/src/gn/label_pattern.cc b/src/gn/label_pattern.cc
index d5f8f6f..ec90b5c 100644
--- a/src/gn/label_pattern.cc
+++ b/src/gn/label_pattern.cc
@@ -63,6 +63,7 @@
 
 // static
 LabelPattern LabelPattern::GetPattern(const SourceDir& current_dir,
+                                      const std::string_view& source_root,
                                       const Value& value,
                                       Err* err) {
   if (!value.VerifyTypeIs(Value::STRING, err))
@@ -78,7 +79,7 @@
   // label resolution code to get all the implicit name stuff.
   size_t star = str.find('*');
   if (star == std::string::npos) {
-    Label label = Label::Resolve(current_dir, Label(), value, err);
+    Label label = Label::Resolve(current_dir, source_root, Label(), value, err);
     if (err->has_error())
       return LabelPattern();
 
@@ -110,8 +111,8 @@
 
     // Parse the inside of the parens as a label for a toolchain.
     Value value_for_toolchain(value.origin(), toolchain_string);
-    toolchain_label =
-        Label::Resolve(current_dir, Label(), value_for_toolchain, err);
+    toolchain_label = Label::Resolve(current_dir, source_root, Label(),
+                                     value_for_toolchain, err);
     if (err->has_error())
       return LabelPattern();
 
@@ -180,7 +181,7 @@
     }
 
     // Resolve the non-wildcard stuff.
-    dir = current_dir.ResolveRelativeDir(value, path, err);
+    dir = current_dir.ResolveRelativeDir(value, path, err, source_root);
     if (err->has_error())
       return LabelPattern();
   }
diff --git a/src/gn/label_pattern.h b/src/gn/label_pattern.h
index b64e838..f40750e 100644
--- a/src/gn/label_pattern.h
+++ b/src/gn/label_pattern.h
@@ -38,6 +38,7 @@
   // Converts the given input string to a pattern. This does special stuff
   // to treat the pattern as a label. Sets the error on failure.
   static LabelPattern GetPattern(const SourceDir& current_dir,
+                                 const std::string_view& source_root,
                                  const Value& value,
                                  Err* err);
 
diff --git a/src/gn/label_pattern_unittest.cc b/src/gn/label_pattern_unittest.cc
index de7b88d..ad98401 100644
--- a/src/gn/label_pattern_unittest.cc
+++ b/src/gn/label_pattern_unittest.cc
@@ -75,8 +75,8 @@
   for (size_t i = 0; i < std::size(cases); i++) {
     const PatternCase& cur = cases[i];
     Err err;
-    LabelPattern result =
-        LabelPattern::GetPattern(current_dir, Value(nullptr, cur.input), &err);
+    LabelPattern result = LabelPattern::GetPattern(
+        current_dir, std::string_view(), Value(nullptr, cur.input), &err);
 
     EXPECT_EQ(cur.success, !err.has_error()) << i << " " << cur.input;
     EXPECT_EQ(cur.type, result.type()) << i << " " << cur.input;
@@ -86,3 +86,18 @@
         << i << " " << cur.input;
   }
 }
+
+// Tests a non-empty source root which allows patterns to reference above the
+// source root.
+TEST(LabelPattern, PatternParseAboveSourceRoot) {
+  SourceDir current_dir("//foo/");
+  std::string source_root = "/foo/bar/baz/";
+
+  Err err;
+  LabelPattern result = LabelPattern::GetPattern(
+      current_dir, source_root, Value(nullptr, "../../../*"), &err);
+  ASSERT_FALSE(err.has_error());
+
+  EXPECT_EQ(LabelPattern::RECURSIVE_DIRECTORY, result.type());
+  EXPECT_EQ("/foo/", result.dir().value()) << result.dir().value();
+}
diff --git a/src/gn/label_unittest.cc b/src/gn/label_unittest.cc
index f46970b..8882019 100644
--- a/src/gn/label_unittest.cc
+++ b/src/gn/label_unittest.cc
@@ -82,8 +82,8 @@
     Err err;
     Value v(nullptr, Value::STRING);
     v.string_value() = cur.str;
-    Label result =
-        Label::Resolve(SourceDir(cur.cur_dir), default_toolchain, v, &err);
+    Label result = Label::Resolve(SourceDir(cur.cur_dir), std::string_view(),
+                                  default_toolchain, v, &err);
     EXPECT_EQ(cur.success, !err.has_error()) << i << " " << cur.str;
     if (!err.has_error() && cur.success) {
       EXPECT_EQ(cur.expected_dir, result.dir().value()) << i << " " << cur.str;
@@ -95,3 +95,46 @@
     }
   }
 }
+
+// Tests the case where the path resolves to something above "//". It should get
+// converted to an absolute path "/foo/bar".
+TEST(Label, ResolveAboveRootBuildDir) {
+  Label default_toolchain(SourceDir("//t/"), "d");
+
+  std::string location, name;
+  Err err;
+
+  SourceDir cur_dir("//cur/");
+  std::string source_root("/foo/bar/baz");
+
+  // No source root given, should not go above the root build dir.
+  Label result = Label::Resolve(cur_dir, std::string_view(), default_toolchain,
+                                Value(nullptr, "../../..:target"), &err);
+  EXPECT_FALSE(err.has_error());
+  EXPECT_EQ("//", result.dir().value()) << result.dir().value();
+  EXPECT_EQ("target", result.name());
+
+  // Source root provided, it should go into that.
+  result = Label::Resolve(cur_dir, source_root, default_toolchain,
+                          Value(nullptr, "../../..:target"), &err);
+  EXPECT_FALSE(err.has_error());
+  EXPECT_EQ("/foo/", result.dir().value()) << result.dir().value();
+  EXPECT_EQ("target", result.name());
+
+  // It should't go up higher than the system root.
+  result = Label::Resolve(cur_dir, source_root, default_toolchain,
+                          Value(nullptr, "../../../../..:target"), &err);
+  EXPECT_FALSE(err.has_error());
+  EXPECT_EQ("/", result.dir().value()) << result.dir().value();
+  EXPECT_EQ("target", result.name());
+
+  // Test an absolute label that goes above the source root. This currently
+  // stops at the source root. It should arguably keep going and produce "/foo/"
+  // but this test just makes sure the current behavior isn't regressed by
+  // accident.
+  result = Label::Resolve(cur_dir, source_root, default_toolchain,
+                          Value(nullptr, "//../.."), &err);
+  EXPECT_FALSE(err.has_error()) << err.message();
+  EXPECT_EQ("/foo/", result.dir().value()) << result.dir().value();
+  EXPECT_EQ("foo", result.name());
+}
diff --git a/src/gn/runtime_deps.cc b/src/gn/runtime_deps.cc
index 422e8c9..3b6d683 100644
--- a/src/gn/runtime_deps.cc
+++ b/src/gn/runtime_deps.cc
@@ -127,7 +127,8 @@
   }
 }
 
-bool CollectRuntimeDepsFromFlag(const Builder& builder,
+bool CollectRuntimeDepsFromFlag(const BuildSettings* build_settings,
+                                const Builder& builder,
                                 RuntimeDepsVector* files_to_write,
                                 Err* err) {
   std::string deps_target_list_file =
@@ -155,8 +156,9 @@
            list_contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
     if (line.empty())
       continue;
-    Label label = Label::Resolve(root_dir, default_toolchain_label,
-                                 Value(nullptr, line), err);
+    Label label =
+        Label::Resolve(root_dir, build_settings->root_path_utf8(),
+                       default_toolchain_label, Value(nullptr, line), err);
     if (err->has_error())
       return false;
 
@@ -292,9 +294,12 @@
   return result;
 }
 
-bool WriteRuntimeDepsFilesIfNecessary(const Builder& builder, Err* err) {
+bool WriteRuntimeDepsFilesIfNecessary(const BuildSettings* build_settings,
+                                      const Builder& builder,
+                                      Err* err) {
   RuntimeDepsVector files_to_write;
-  if (!CollectRuntimeDepsFromFlag(builder, &files_to_write, err))
+  if (!CollectRuntimeDepsFromFlag(build_settings, builder, &files_to_write,
+                                  err))
     return false;
 
   // Files scheduled by write_runtime_deps.
diff --git a/src/gn/runtime_deps.h b/src/gn/runtime_deps.h
index 8592677..82e0f10 100644
--- a/src/gn/runtime_deps.h
+++ b/src/gn/runtime_deps.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 class Builder;
+class BuildSettings;
 class Err;
 class OutputFile;
 class Target;
@@ -23,6 +24,8 @@
 
 // Writes all runtime deps files requested on the command line, or does nothing
 // if no files were specified.
-bool WriteRuntimeDepsFilesIfNecessary(const Builder& builder, Err* err);
+bool WriteRuntimeDepsFilesIfNecessary(const BuildSettings* build_settings,
+                                      const Builder& builder,
+                                      Err* err);
 
 #endif  // TOOLS_GN_RUNTIME_DEPS_H
diff --git a/src/gn/rust_values_generator.cc b/src/gn/rust_values_generator.cc
index 38588b4..926ab21 100644
--- a/src/gn/rust_values_generator.cc
+++ b/src/gn/rust_values_generator.cc
@@ -175,8 +175,9 @@
   value->scope_value()->GetCurrentScopeValues(&aliased_deps);
   for (const auto& pair : aliased_deps) {
     Label dep_label =
-        Label::Resolve(scope_->GetSourceDir(), ToolchainLabelForScope(scope_),
-                       pair.second, err_);
+        Label::Resolve(scope_->GetSourceDir(),
+                       scope_->settings()->build_settings()->root_path_utf8(),
+                       ToolchainLabelForScope(scope_), pair.second, err_);
 
     if (err_->has_error())
       return false;
diff --git a/src/gn/setup.cc b/src/gn/setup.cc
index cbaa958..75638cf 100644
--- a/src/gn/setup.cc
+++ b/src/gn/setup.cc
@@ -161,7 +161,8 @@
 namespace {
 
 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
-const char kDefaultArgsGn[] = "# Set build arguments here. See `gn help buildargs`.";
+const char kDefaultArgsGn[] =
+    "# Set build arguments here. See `gn help buildargs`.";
 
 base::FilePath FindDotFile(const base::FilePath& current_dir) {
   base::FilePath try_this_file = current_dir.Append(kGnFile);
@@ -789,7 +790,8 @@
       return false;
     }
 
-    root_target_label = Label::Resolve(current_dir, Label(), *root_value, &err);
+    root_target_label = Label::Resolve(current_dir, std::string_view(), Label(),
+                                       *root_value, &err);
     if (err.has_error()) {
       err.PrintToStdout();
       return false;
@@ -821,8 +823,8 @@
       dotfile_scope_.GetValue("check_targets", true);
   if (check_targets_value) {
     check_patterns_.reset(new std::vector<LabelPattern>);
-    ExtractListOfLabelPatterns(*check_targets_value, current_dir,
-                               check_patterns_.get(), &err);
+    ExtractListOfLabelPatterns(&build_settings_, *check_targets_value,
+                               current_dir, check_patterns_.get(), &err);
     if (err.has_error()) {
       err.PrintToStdout();
       return false;
@@ -839,7 +841,6 @@
     check_system_includes_ = check_system_includes_value->boolean_value();
   }
 
-
   // Fill exec_script_whitelist.
   const Value* exec_script_whitelist_value =
       dotfile_scope_.GetValue("exec_script_whitelist", true);
diff --git a/src/gn/target.cc b/src/gn/target.cc
index 6df5ede..fdf473e 100644
--- a/src/gn/target.cc
+++ b/src/gn/target.cc
@@ -537,8 +537,7 @@
   // Direct dependent libraries.
   if (dep->output_type() == STATIC_LIBRARY ||
       dep->output_type() == SHARED_LIBRARY ||
-      dep->output_type() == SOURCE_SET ||
-      dep->output_type() == RUST_LIBRARY ||
+      dep->output_type() == SOURCE_SET || dep->output_type() == RUST_LIBRARY ||
       dep->output_type() == RUST_PROC_MACRO)
     inherited_libraries_.Append(dep, is_public);
 
@@ -972,8 +971,9 @@
 
     // Otherwise, look through the target's deps for the specified one.
     // Canonicalize the label if possible.
-    Label next_label =
-        Label::Resolve(current_dir, settings()->toolchain_label(), next, err);
+    Label next_label = Label::Resolve(
+        current_dir, settings()->build_settings()->root_path_utf8(),
+        settings()->toolchain_label(), next, err);
     if (next_label.is_null()) {
       *err = Err(next.origin(), std::string("Failed to canonicalize ") +
                                     next.string_value() + std::string("."));
diff --git a/src/gn/target_generator.cc b/src/gn/target_generator.cc
index 0111d72..0ae9daf 100644
--- a/src/gn/target_generator.cc
+++ b/src/gn/target_generator.cc
@@ -314,7 +314,8 @@
 bool TargetGenerator::FillAssertNoDeps() {
   const Value* value = scope_->GetValue(variables::kAssertNoDeps, true);
   if (value) {
-    return ExtractListOfLabelPatterns(*value, scope_->GetSourceDir(),
+    return ExtractListOfLabelPatterns(scope_->settings()->build_settings(),
+                                      *value, scope_->GetSourceDir(),
                                       &target_->assert_no_deps(), err_);
   }
   return true;
@@ -412,7 +413,8 @@
                                          UniqueVector<LabelConfigPair>* dest) {
   const Value* value = scope_->GetValue(var_name, true);
   if (value) {
-    ExtractListOfUniqueLabels(*value, scope_->GetSourceDir(),
+    ExtractListOfUniqueLabels(scope_->settings()->build_settings(), *value,
+                              scope_->GetSourceDir(),
                               ToolchainLabelForScope(scope_), dest, err_);
   }
   return !err_->has_error();
@@ -422,8 +424,9 @@
                                       LabelTargetVector* dest) {
   const Value* value = scope_->GetValue(var_name, true);
   if (value) {
-    ExtractListOfLabels(*value, scope_->GetSourceDir(),
-                        ToolchainLabelForScope(scope_), dest, err_);
+    ExtractListOfLabels(scope_->settings()->build_settings(), *value,
+                        scope_->GetSourceDir(), ToolchainLabelForScope(scope_),
+                        dest, err_);
   }
   return !err_->has_error();
 }
diff --git a/src/gn/test_with_scope.cc b/src/gn/test_with_scope.cc
index 70d1612..ae9c939 100644
--- a/src/gn/test_with_scope.cc
+++ b/src/gn/test_with_scope.cc
@@ -40,8 +40,8 @@
 
 Label TestWithScope::ParseLabel(const std::string& str) const {
   Err err;
-  Label result = Label::Resolve(SourceDir("//"), toolchain_.label(),
-                                Value(nullptr, str), &err);
+  Label result = Label::Resolve(SourceDir("//"), std::string_view(),
+                                toolchain_.label(), Value(nullptr, str), &err);
   CHECK(!err.has_error());
   return result;
 }
diff --git a/src/gn/tool.cc b/src/gn/tool.cc
index 6dc46e7..3b29775 100644
--- a/src/gn/tool.cc
+++ b/src/gn/tool.cc
@@ -7,6 +7,7 @@
 #include "gn/c_tool.h"
 #include "gn/general_tool.h"
 #include "gn/rust_tool.h"
+#include "gn/settings.h"
 #include "gn/target.h"
 
 const char* Tool::kToolNone = "";
@@ -156,7 +157,9 @@
     return true;  // Not present is fine.
 
   Label label =
-      Label::Resolve(scope->GetSourceDir(), current_toolchain, *v, err);
+      Label::Resolve(scope->GetSourceDir(),
+                     scope->settings()->build_settings()->root_path_utf8(),
+                     current_toolchain, *v, err);
   if (err->has_error())
     return false;
 
diff --git a/src/gn/value_extractors.cc b/src/gn/value_extractors.cc
index baf116c..74ea345 100644
--- a/src/gn/value_extractors.cc
+++ b/src/gn/value_extractors.cc
@@ -107,9 +107,10 @@
 
 struct ExternConverter {
   ExternConverter(const BuildSettings* build_settings_in,
-                   const SourceDir& current_dir_in)
+                  const SourceDir& current_dir_in)
       : build_settings(build_settings_in), current_dir(current_dir_in) {}
-  bool operator()(const Value& v, std::pair<std::string, LibFile>* out,
+  bool operator()(const Value& v,
+                  std::pair<std::string, LibFile>* out,
                   Err* err) const {
     if (!v.VerifyTypeIs(Value::SCOPE, err))
       return false;
@@ -146,15 +147,20 @@
 // Fills in a label.
 template <typename T>
 struct LabelResolver {
-  LabelResolver(const SourceDir& current_dir_in,
+  LabelResolver(const BuildSettings* build_settings_in,
+                const SourceDir& current_dir_in,
                 const Label& current_toolchain_in)
-      : current_dir(current_dir_in), current_toolchain(current_toolchain_in) {}
+      : build_settings(build_settings_in),
+        current_dir(current_dir_in),
+        current_toolchain(current_toolchain_in) {}
   bool operator()(const Value& v, Label* out, Err* err) const {
     if (!v.VerifyTypeIs(Value::STRING, err))
       return false;
-    *out = Label::Resolve(current_dir, current_toolchain, v, err);
+    *out = Label::Resolve(current_dir, build_settings->root_path_utf8(),
+                          current_toolchain, v, err);
     return !err->has_error();
   }
+  const BuildSettings* build_settings;
   const SourceDir& current_dir;
   const Label& current_toolchain;
 };
@@ -162,27 +168,36 @@
 // Fills the label part of a LabelPtrPair, leaving the pointer null.
 template <typename T>
 struct LabelPtrResolver {
-  LabelPtrResolver(const SourceDir& current_dir_in,
+  LabelPtrResolver(const BuildSettings* build_settings_in,
+                   const SourceDir& current_dir_in,
                    const Label& current_toolchain_in)
-      : current_dir(current_dir_in), current_toolchain(current_toolchain_in) {}
+      : build_settings(build_settings_in),
+        current_dir(current_dir_in),
+        current_toolchain(current_toolchain_in) {}
   bool operator()(const Value& v, LabelPtrPair<T>* out, Err* err) const {
     if (!v.VerifyTypeIs(Value::STRING, err))
       return false;
-    out->label = Label::Resolve(current_dir, current_toolchain, v, err);
+    out->label = Label::Resolve(current_dir, build_settings->root_path_utf8(),
+                                current_toolchain, v, err);
     out->origin = v.origin();
     return !err->has_error();
   }
+  const BuildSettings* build_settings;
   const SourceDir& current_dir;
   const Label& current_toolchain;
 };
 
 struct LabelPatternResolver {
-  LabelPatternResolver(const SourceDir& current_dir_in)
-      : current_dir(current_dir_in) {}
+  LabelPatternResolver(const BuildSettings* build_settings_in,
+                       const SourceDir& current_dir_in)
+      : build_settings(build_settings_in), current_dir(current_dir_in) {}
   bool operator()(const Value& v, LabelPattern* out, Err* err) const {
-    *out = LabelPattern::GetPattern(current_dir, v, err);
+    *out = LabelPattern::GetPattern(current_dir,
+                                    build_settings->root_path_utf8(), v, err);
     return !err->has_error();
   }
+
+  const BuildSettings* build_settings;
   const SourceDir& current_dir;
 };
 
@@ -230,43 +245,48 @@
                             RelativeDirConverter(build_settings, current_dir));
 }
 
-bool ExtractListOfLabels(const Value& value,
+bool ExtractListOfLabels(const BuildSettings* build_settings,
+                         const Value& value,
                          const SourceDir& current_dir,
                          const Label& current_toolchain,
                          LabelTargetVector* dest,
                          Err* err) {
   return ListValueExtractor(
       value, dest, err,
-      LabelPtrResolver<Target>(current_dir, current_toolchain));
+      LabelPtrResolver<Target>(build_settings, current_dir, current_toolchain));
 }
 
-bool ExtractListOfUniqueLabels(const Value& value,
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+                               const Value& value,
                                const SourceDir& current_dir,
                                const Label& current_toolchain,
                                UniqueVector<Label>* dest,
                                Err* err) {
   return ListValueUniqueExtractor(
-      value, dest, err, LabelResolver<Config>(current_dir, current_toolchain));
+      value, dest, err,
+      LabelResolver<Config>(build_settings, current_dir, current_toolchain));
 }
 
-bool ExtractListOfUniqueLabels(const Value& value,
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+                               const Value& value,
                                const SourceDir& current_dir,
                                const Label& current_toolchain,
                                UniqueVector<LabelConfigPair>* dest,
                                Err* err) {
   return ListValueUniqueExtractor(
       value, dest, err,
-      LabelPtrResolver<Config>(current_dir, current_toolchain));
+      LabelPtrResolver<Config>(build_settings, current_dir, current_toolchain));
 }
 
-bool ExtractListOfUniqueLabels(const Value& value,
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+                               const Value& value,
                                const SourceDir& current_dir,
                                const Label& current_toolchain,
                                UniqueVector<LabelTargetPair>* dest,
                                Err* err) {
   return ListValueUniqueExtractor(
       value, dest, err,
-      LabelPtrResolver<Target>(current_dir, current_toolchain));
+      LabelPtrResolver<Target>(build_settings, current_dir, current_toolchain));
 }
 
 bool ExtractRelativeFile(const BuildSettings* build_settings,
@@ -278,19 +298,20 @@
   return converter(value, file, err);
 }
 
-bool ExtractListOfLabelPatterns(const Value& value,
+bool ExtractListOfLabelPatterns(const BuildSettings* build_settings,
+                                const Value& value,
                                 const SourceDir& current_dir,
                                 std::vector<LabelPattern>* patterns,
                                 Err* err) {
   return ListValueExtractor(value, patterns, err,
-                            LabelPatternResolver(current_dir));
+                            LabelPatternResolver(build_settings, current_dir));
 }
 
 bool ExtractListOfExterns(const BuildSettings* build_settings,
-                         const Value& value,
-                         const SourceDir& current_dir,
-                         std::vector<std::pair<std::string, LibFile>>* externs,
-                         Err* err) {
+                          const Value& value,
+                          const SourceDir& current_dir,
+                          std::vector<std::pair<std::string, LibFile>>* externs,
+                          Err* err) {
   return ListValueExtractor(value, externs, err,
                             ExternConverter(build_settings, current_dir));
 }
diff --git a/src/gn/value_extractors.h b/src/gn/value_extractors.h
index 0518147..9cc9e00 100644
--- a/src/gn/value_extractors.h
+++ b/src/gn/value_extractors.h
@@ -49,7 +49,8 @@
 
 // Extracts the list of labels and their origins to the given vector. Only the
 // labels are filled in, the ptr for each pair in the vector will be null.
-bool ExtractListOfLabels(const Value& value,
+bool ExtractListOfLabels(const BuildSettings* build_settings,
+                         const Value& value,
                          const SourceDir& current_dir,
                          const Label& current_toolchain,
                          LabelTargetVector* dest,
@@ -59,17 +60,20 @@
 // version taking Label*Pair, only the labels are filled in, the ptr for each
 // pair in the vector will be null. Sets an error and returns false if a label
 // is maformed or there are duplicates.
-bool ExtractListOfUniqueLabels(const Value& value,
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+                               const Value& value,
                                const SourceDir& current_dir,
                                const Label& current_toolchain,
                                UniqueVector<Label>* dest,
                                Err* err);
-bool ExtractListOfUniqueLabels(const Value& value,
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+                               const Value& value,
                                const SourceDir& current_dir,
                                const Label& current_toolchain,
                                UniqueVector<LabelConfigPair>* dest,
                                Err* err);
-bool ExtractListOfUniqueLabels(const Value& value,
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+                               const Value& value,
                                const SourceDir& current_dir,
                                const Label& current_toolchain,
                                UniqueVector<LabelTargetPair>* dest,
@@ -81,7 +85,8 @@
                          SourceFile* file,
                          Err* err);
 
-bool ExtractListOfLabelPatterns(const Value& value,
+bool ExtractListOfLabelPatterns(const BuildSettings* build_settings,
+                                const Value& value,
                                 const SourceDir& current_dir,
                                 std::vector<LabelPattern>* patterns,
                                 Err* err);
diff --git a/src/gn/visibility.cc b/src/gn/visibility.cc
index d15706d..eb99a27 100644
--- a/src/gn/visibility.cc
+++ b/src/gn/visibility.cc
@@ -22,6 +22,7 @@
 Visibility::~Visibility() = default;
 
 bool Visibility::Set(const SourceDir& current_dir,
+                     const std::string_view& source_root,
                      const Value& value,
                      Err* err) {
   patterns_.clear();
@@ -32,7 +33,8 @@
   }
 
   for (const auto& item : value.list_value()) {
-    patterns_.push_back(LabelPattern::GetPattern(current_dir, item, err));
+    patterns_.push_back(
+        LabelPattern::GetPattern(current_dir, source_root, item, err));
     if (err->has_error())
       return false;
   }
@@ -109,7 +111,9 @@
 bool Visibility::FillItemVisibility(Item* item, Scope* scope, Err* err) {
   const Value* vis_value = scope->GetValue(variables::kVisibility, true);
   if (vis_value)
-    item->visibility().Set(scope->GetSourceDir(), *vis_value, err);
+    item->visibility().Set(
+        scope->GetSourceDir(),
+        scope->settings()->build_settings()->root_path_utf8(), *vis_value, err);
   else  // Default to public.
     item->visibility().SetPublic();
   return !err->has_error();
diff --git a/src/gn/visibility.h b/src/gn/visibility.h
index 5c7f753..fc16731 100644
--- a/src/gn/visibility.h
+++ b/src/gn/visibility.h
@@ -6,6 +6,7 @@
 #define TOOLS_GN_VISIBILITY_H_
 
 #include <memory>
+#include <string_view>
 #include <vector>
 
 #include "base/macros.h"
@@ -30,7 +31,10 @@
 
   // Set the visibility to the thing specified by the given value. On failure,
   // returns false and sets the error.
-  bool Set(const SourceDir& current_dir, const Value& value, Err* err);
+  bool Set(const SourceDir& current_dir,
+           const std::string_view& source_root,
+           const Value& value,
+           Err* err);
 
   // Sets the visibility to be public.
   void SetPublic();
diff --git a/src/gn/visibility_unittest.cc b/src/gn/visibility_unittest.cc
index fde7494..0122119 100644
--- a/src/gn/visibility_unittest.cc
+++ b/src/gn/visibility_unittest.cc
@@ -17,7 +17,7 @@
 
   Err err;
   Visibility vis;
-  ASSERT_TRUE(vis.Set(SourceDir("//"), list, &err));
+  ASSERT_TRUE(vis.Set(SourceDir("//"), std::string_view(), list, &err));
 
   EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
   EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//my/"), "notname")));
@@ -37,7 +37,7 @@
 
   Value list(nullptr, Value::LIST);
   list.list_value().push_back(Value(nullptr, "*"));
-  ASSERT_TRUE(vis.Set(SourceDir("//"), list, &err));
+  ASSERT_TRUE(vis.Set(SourceDir("//"), std::string_view(), list, &err));
 
   EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
   EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//"), "")));
@@ -46,8 +46,26 @@
 TEST(Visibility, Private) {
   Err err;
   Visibility vis;
-  ASSERT_TRUE(vis.Set(SourceDir("//"), Value(nullptr, Value::LIST), &err));
+  ASSERT_TRUE(vis.Set(SourceDir("//"), std::string_view(),
+                      Value(nullptr, Value::LIST), &err));
 
   EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
   EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//"), "")));
 }
+
+TEST(Visibility, AboveSourceDir) {
+  std::string source_root = "/foo/bar/baz/";
+  SourceDir cur_dir("//");
+
+  Err err;
+  Visibility vis;
+
+  Value list(nullptr, Value::LIST);
+  list.list_value().push_back(Value(nullptr, "../../*"));
+  ASSERT_TRUE(vis.Set(cur_dir, source_root, list, &err));
+
+  EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
+  EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("/foo/"), "foo")));
+  EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("/foo/bar/"), "bar")));
+  EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("/nowhere/"), "foo")));
+}