GN: Print the import trail when parse errors occur.
This is especially useful when asserts fail when the file should not be
included in the first place.
Example error message:
ERROR at //build/config/android/internal_rules.gni:11:1: Assertion failed.
assert(false)
^-----
See //build/config/android/rules.gni:7:1: whence it was imported.
import("//build/config/android/internal_rules.gni")
^-------------------------------------------------
See //media/midi/BUILD.gn:13:3: whence it was imported.
import("//build/config/android/rules.gni")
^----------------------------------------
See //BUILD.gn:193:7: which caused the package to be included.
"//media/midi:midi_unittests",
^----------------------------
BUG=604972
Review URL: https://codereview.chromium.org/1905473003
Cr-Original-Commit-Position: refs/heads/master@{#388986}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: dbecf02976975b93db37c3b5e3aa26d6fc92c517
diff --git a/tools/gn/import_manager.cc b/tools/gn/import_manager.cc
index 8c57008..86cfd45 100644
--- a/tools/gn/import_manager.cc
+++ b/tools/gn/import_manager.cc
@@ -30,8 +30,12 @@
scope->SetProcessingImport();
node->Execute(scope.get(), err);
- if (err->has_error())
+ if (err->has_error()) {
+ // If there was an error, append the caller location so the error message
+ // displays a why the file was imported (esp. useful for failed asserts).
+ err->AppendSubErr(Err(node_for_err, "whence it was imported."));
return nullptr;
+ }
scope->ClearProcessingImport();
return scope;
diff --git a/tools/gn/loader.cc b/tools/gn/loader.cc
index 3ac868c..e3165b9 100644
--- a/tools/gn/loader.cc
+++ b/tools/gn/loader.cc
@@ -211,7 +211,7 @@
pending_loads_++;
if (!AsyncLoadFile(origin, settings->build_settings(), file,
base::Bind(&LoaderImpl::BackgroundLoadFile, this,
- settings, file),
+ settings, file, origin),
&err)) {
g_scheduler->FailWithError(err);
DecrementPendingLoads();
@@ -235,6 +235,7 @@
void LoaderImpl::BackgroundLoadFile(const Settings* settings,
const SourceFile& file_name,
+ const LocationRange& origin,
const ParseNode* root) {
if (!root) {
main_loop_->PostTask(FROM_HERE,
@@ -260,11 +261,15 @@
Err err;
root->Execute(&our_scope, &err);
- if (err.has_error())
- g_scheduler->FailWithError(err);
+ if (!err.has_error())
+ our_scope.CheckForUnusedVars(&err);
- if (!our_scope.CheckForUnusedVars(&err))
+ if (err.has_error()) {
+ if (!origin.is_null())
+ err.AppendSubErr(Err(origin, "which caused the file to be included."));
g_scheduler->FailWithError(err);
+ }
+
// Pass all of the items that were defined off to the builder.
for (auto& item : collected_items) {
diff --git a/tools/gn/loader.h b/tools/gn/loader.h
index 37f7085..a610835 100644
--- a/tools/gn/loader.h
+++ b/tools/gn/loader.h
@@ -129,6 +129,7 @@
// input file manager.
void BackgroundLoadFile(const Settings* settings,
const SourceFile& file_name,
+ const LocationRange& origin,
const ParseNode* root);
void BackgroundLoadBuildConfig(
Settings* settings,
diff --git a/tools/gn/location.h b/tools/gn/location.h
index 44d1a6f..647c6f7 100644
--- a/tools/gn/location.h
+++ b/tools/gn/location.h
@@ -19,6 +19,7 @@
int line_number() const { return line_number_; }
int column_number() const { return column_number_; }
int byte() const { return byte_; }
+ bool is_null() const { return *this == Location(); }
bool operator==(const Location& other) const;
bool operator!=(const Location& other) const;
@@ -45,6 +46,10 @@
const Location& begin() const { return begin_; }
const Location& end() const { return end_; }
+ bool is_null() const {
+ return begin_.is_null(); // No need to check both for the null case.
+ }
+
LocationRange Union(const LocationRange& other) const;