bazel syntax: improve error for dynamic use-before-definition
The error emitted for a dynamic use-before-definition in a BUILD file
used to look confusingly like a static error:
"name x is not defined; did you mean...?"
Such errors are expected in programs like 'print(x); x=1'
where there is no misspelling.
This change makes the error resemble the dynamic error in a .bzl file:
"variable x referenced before assignment", which makes clear that
there is an assignment for x, it just hasn't been executed yet.
PiperOrigin-RevId: 294428556
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Eval.java b/src/main/java/com/google/devtools/build/lib/syntax/Eval.java
index 6cb3133..e42fb64 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Eval.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Eval.java
@@ -607,10 +607,17 @@
if (result != null) {
return result;
}
- String error =
- ValidationEnvironment.createInvalidIdentifierException(
- name, fr.thread.getVariableNames());
- throw new EvalException(id.getStartLocation(), error);
+
+ // Assuming validation was successfully applied before execution
+ // (which is not yet true for copybara, but will be soon),
+ // then the identifier must have been resolved but the
+ // resolution was not annotated onto the syntax tree---because
+ // it's a BUILD file that may share trees with the prelude.
+ // So this error does not mean "undefined variable" (morally a
+ // static error), but "variable was (dynamically) referenced
+ // before being bound", as in 'print(x); x=1'.
+ throw new EvalException(
+ id.getStartLocation(), "variable '" + name + "' is referenced before assignment");
}
Object result;
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java b/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
index 11abce1..167be97 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
@@ -231,8 +231,7 @@
}
}
- // This is exposed to Eval until validation becomes a precondition for evaluation.
- static String createInvalidIdentifierException(String name, Set<String> candidates) {
+ private static String createInvalidIdentifierException(String name, Set<String> candidates) {
if (name.equals("$error$")) {
return "contains syntax error(s)";
}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
index 240d39a..e53b709 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
@@ -466,11 +466,13 @@
@Test
public void testListComprehensionDefinitionOrder() throws Exception {
new BuildTest()
- .testIfErrorContains("name 'y' is not defined", "[x for x in (1, 2) if y for y in (3, 4)]");
+ .testIfErrorContains(
+ "variable 'y' is referenced before assignment", //
+ "[x for x in (1, 2) if y for y in (3, 4)]");
new SkylarkTest()
.testIfErrorContains(
- "local variable 'y' is referenced before assignment",
+ "local variable 'y' is referenced before assignment", //
"[x for x in (1, 2) if y for y in (3, 4)]");
}