Improve error message for rebase_path builtin
When rebase_path encounters an invalid path (e.g., an empty string), it
previously blamed the origin of the value. If the value originated from
another function (like read_file) or a variable definition far away,
the error message could be confusing as it pointed to the definition
site rather than the usage site.
This change improves the error reporting by:
- Rewriting the error to blame the rebase_path call.
- Appending the original error location as a sub-error. This makes it clear that
rebase_path failed, while still providing context on where the bad value came
from.
Before
```
ERROR at //BUILD.gn:2:9: Empty directory path.
lines = read_file("test.txt", "list lines")
^---------------------------------
You can't use empty strings as directories.
```
After
```
ERROR at //BUILD.gn:4:13: Empty directory path.
rebase_path(lines)
^----
You can't use empty strings as directories.
See //BUILD.gn:3:9: The value that caused the error.
lines = read_file("test.txt", "list lines")
^
```
Change-Id: I7c5d26ee13ac5fea3560a808a9d4262a0b9304fc
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/20660
Reviewed-by: David Turner <digit@google.com>
Commit-Queue: Neri Marschik <nerima@google.com>
diff --git a/src/gn/function_rebase_path.cc b/src/gn/function_rebase_path.cc
index 7e912aa..a998785 100644
--- a/src/gn/function_rebase_path.cc
+++ b/src/gn/function_rebase_path.cc
@@ -5,6 +5,7 @@
#include <stddef.h>
#include "gn/build_settings.h"
+#include <utility>
#include "gn/filesystem_utils.h"
#include "gn/functions.h"
#include "gn/parse_tree.h"
@@ -53,6 +54,14 @@
return false;
}
+void AddCallLocationToError(const ParseNode* blame_node, Err* err) {
+ if (blame_node && err->has_error()) {
+ Err new_err(blame_node, err->message(), err->help_text());
+ new_err.AppendSubErr(Err(err->location(), "The value that caused the error."));
+ *err = std::move(new_err);
+ }
+}
+
Value ConvertOnePath(const Scope* scope,
const FunctionCallNode* function,
const Value& value,
@@ -93,19 +102,21 @@
result = Value(function, Value::STRING);
if (looks_like_dir) {
+ SourceDir resolved = from_dir.ResolveRelativeDir(
+ value, err, scope->settings()->build_settings()->root_path_utf8());
+ if (err->has_error()) {
+ return Value();
+ }
result.string_value() = RebasePath(
- from_dir
- .ResolveRelativeDir(
- value, err,
- scope->settings()->build_settings()->root_path_utf8())
- .value(),
+ resolved.value(),
to_dir, scope->settings()->build_settings()->root_path_utf8());
MakeSlashEndingMatchInput(string_value, &result.string_value());
} else {
SourceFile resolved_file = from_dir.ResolveRelativeFile(
value, err, scope->settings()->build_settings()->root_path_utf8());
- if (err->has_error())
+ if (err->has_error()) {
return Value();
+ }
result.string_value() =
RebasePath(resolved_file.value(), to_dir,
scope->settings()->build_settings()->root_path_utf8());
@@ -227,6 +238,10 @@
return result;
}
const Value& inputs = args[kArgIndexInputs];
+ const ParseNode* inputs_origin = nullptr;
+ if (function->args() && function->args()->contents().size() > kArgIndexInputs) {
+ inputs_origin = function->args()->contents()[kArgIndexInputs].get();
+ }
// To path.
bool convert_to_system_absolute = true;
@@ -262,8 +277,12 @@
// Path conversion.
if (inputs.type() == Value::STRING) {
- return ConvertOnePath(scope, function, inputs, from_dir, to_dir,
- convert_to_system_absolute, err);
+ Value ret = ConvertOnePath(scope, function, inputs, from_dir, to_dir,
+ convert_to_system_absolute, err);
+ if (err->has_error()) {
+ AddCallLocationToError(inputs_origin, err);
+ }
+ return ret;
} else if (inputs.type() == Value::LIST) {
result = Value(function, Value::LIST);
@@ -274,6 +293,7 @@
ConvertOnePath(scope, function, input, from_dir, to_dir,
convert_to_system_absolute, err));
if (err->has_error()) {
+ AddCallLocationToError(inputs_origin, err);
result = Value();
return result;
}