bazel syntax: break dependence on EventHandler
This change removes nearly all uses of EventHandler in the interpreter.
(The remaining uses are for events created by the Starlark 'print' statement.
A follow-up change will create a dedicated StarlarkThread.PrintHandler.)
All parse functions now report errors in one of two ways:
1) by throwing a SyntaxError exception containing a list of events.
This approach is used by "all-or-nothing" parse operations related
to expressions. Clients may catch the exception, extract the events,
and replay them on their handler should they choose. Example:
try {
Expression expr = Expression.parse(input)
...
} catch (SyntaxError ex) {
Event.replayEventsOn(handler, ex.errors());
}
2) by recording a list of scan/parse errors in StarlarkFile.errors.
The result of parsing a file is both a syntax tree (possibly incomplete),
and a list of errors. If parsing is followed by validation,
the validator appends its errors to this list. (Validation is now
capable of reporting multiple errors per file. ValidationException is gone.)
StarlarkFile file = StarlarkFile.parse(input)
if (!file.ok()) {
Event.replayEventsOn(handler, file.errors());
return;
}
Or:
StarlarkFile file = StarlarkFile.parse(input)
if (!file.ok()) {
throw new SyntaxError(file.errors());
}
Details:
- error helpers for Identifiers have moved from Eval to ValidationEnvironment
and no longer depend on EvalException.
- The EvalException.incompleteAST concept is irrelevant now that
clients do not proceed to evaluation if the parser or validation
step produced errors.
- StarlarkFile: exec + execTopLevelStatement have moved to EvalUtils, and will
change further.
- replayLexerEvents is gone from the API. The validator (which has access
to a StarlarkSemantics) can optionally copy the string escape events
into the main error bucket, or ignore them.
- StarlarkFile: eliminate parseWithoutImports
- Parser: delete dead code parseStatement{,s}
- Lexer: delete dead code stringAtLine
Numerous fixes were required in the tests, because they are such a muddle
of different phases (scan, parse, validate, evaluate)
and modes (BUILD vs .bzl vs core Starlark).
RELNOTES: Multiple Starlark validation errors are reported in a single pass.
PiperOrigin-RevId: 272205103
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
index 037943b..b9d3a1d 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
@@ -38,7 +38,6 @@
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
-import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
@@ -1179,44 +1178,25 @@
return vars;
}
- private static final class EvalEventHandler implements EventHandler {
- List<Event> messages = new ArrayList<>();
-
- @Override
- public void handle(Event event) {
- if (event.getKind() == EventKind.ERROR) {
- messages.add(event);
- }
- }
- }
-
- /** Evaluates a Skylark statement in this thread. (Debugger API) */
+ /** Evaluates a Skylark statement in this thread. (Debugger API) This operation mutates expr. */
// TODO(adonovan): push this up into the debugger once the eval API is finalized.
- public Object debugEval(ParserInput input) throws EvalException, InterruptedException {
- EvalEventHandler handler = new EvalEventHandler();
- Expression expr = Expression.parse(input, handler);
- if (!handler.messages.isEmpty()) {
- Event ev = handler.messages.get(0);
- throw new EvalException(ev.getLocation(), ev.getMessage());
- }
+ public Object debugEval(Expression expr) throws EvalException, InterruptedException {
return Eval.eval(this, expr);
}
/** Executes a Skylark file (sequence of statements) in this thread. (Debugger API) */
// TODO(adonovan): push this up into the debugger once the exec API is finalized.
- public void debugExec(ParserInput input) throws EvalException, InterruptedException {
- EvalEventHandler handler = new EvalEventHandler();
- StarlarkFile file = StarlarkFile.parse(input, handler);
- if (!handler.messages.isEmpty()) {
- Event ev = handler.messages.get(0);
- throw new EvalException(ev.getLocation(), ev.getMessage());
+ public void debugExec(ParserInput input) throws SyntaxError, EvalException, InterruptedException {
+ StarlarkFile file = StarlarkFile.parse(input);
+ ValidationEnvironment.validateFile(file, this, /*isBuildFile=*/ false);
+ if (!file.ok()) {
+ throw new SyntaxError(file.errors());
}
for (Statement stmt : file.getStatements()) {
if (stmt instanceof LoadStatement) {
- throw new EvalException(null, "cannot execute load statements in debugger");
+ throw new EvalException(stmt.getLocation(), "cannot execute load statements in debugger");
}
}
- ValidationEnvironment.validateFile(file, this, /*isBuildFile=*/ false);
Eval.execStatements(this, file.getStatements());
}
@@ -1441,6 +1421,7 @@
/** An exception thrown by {@link #FAIL_FAST_HANDLER}. */
// TODO(bazel-team): Possibly extend RuntimeException instead of IllegalArgumentException.
+ // TODO(adonovan): move to EventCollectionApparatus.
public static class FailFastException extends IllegalArgumentException {
public FailFastException(String s) {
super(s);
@@ -1455,6 +1436,7 @@
* assertions) need to be able to distinguish between organically occurring exceptions and
* exceptions thrown by this handler.
*/
+ // TODO(adonovan): move to EventCollectionApparatus.
public static final EventHandler FAIL_FAST_HANDLER =
new EventHandler() {
@Override