Add fine grained detail to QueryException and Lexer.
Allow QueryException to take in a Query code from failure detail and construct a failure detail with its inner constructed message.
RELNOTES: None.
PiperOrigin-RevId: 319832572
diff --git a/src/main/java/com/google/devtools/build/lib/query2/engine/BUILD b/src/main/java/com/google/devtools/build/lib/query2/engine/BUILD
index 5f75194..3693efb 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/engine/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/query2/engine/BUILD
@@ -18,6 +18,7 @@
"//src/main/java/com/google/devtools/build/lib/graph",
"//src/main/java/com/google/devtools/build/lib/packages",
"//src/main/java/com/google/devtools/build/lib/profiler",
+ "//src/main/protobuf:failure_details_java_proto",
"//third_party:guava",
"//third_party:jsr305",
],
diff --git a/src/main/java/com/google/devtools/build/lib/query2/engine/LetExpression.java b/src/main/java/com/google/devtools/build/lib/query2/engine/LetExpression.java
index 7709ce7..92567bb 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/engine/LetExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/engine/LetExpression.java
@@ -16,6 +16,7 @@
import com.google.common.base.Function;
import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryTaskFuture;
import com.google.devtools.build.lib.query2.engine.QueryEnvironment.ThreadSafeMutableSet;
+import com.google.devtools.build.lib.server.FailureDetails.Query;
import java.util.Collection;
import java.util.regex.Pattern;
@@ -72,7 +73,10 @@
final Callback<T> callback) {
if (!NAME_PATTERN.matcher(varName).matches()) {
return env.immediateFailedFuture(
- new QueryException(this, "invalid variable name '" + varName + "' in let expression"));
+ new QueryException(
+ this,
+ "invalid variable name '" + varName + "' in let expression",
+ Query.Code.VARIABLE_NAME_INVALID));
}
QueryTaskFuture<ThreadSafeMutableSet<T>> varValueFuture =
QueryUtil.evalAll(env, context, varExpr);
diff --git a/src/main/java/com/google/devtools/build/lib/query2/engine/Lexer.java b/src/main/java/com/google/devtools/build/lib/query2/engine/Lexer.java
index 60ae900..c25777c 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/engine/Lexer.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/engine/Lexer.java
@@ -13,6 +13,7 @@
// limitations under the License.
package com.google.devtools.build.lib.query2.engine;
+import com.google.devtools.build.lib.server.FailureDetails.Query;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
@@ -158,7 +159,7 @@
}
}
}
- throw new QueryException("unclosed quotation");
+ throw new QueryException("unclosed quotation", Query.Code.UNCLOSED_QUOTATION_EXPRESSION_ERROR);
}
private TokenKind getTokenKindForWord(String word) {
diff --git a/src/main/java/com/google/devtools/build/lib/query2/engine/QueryException.java b/src/main/java/com/google/devtools/build/lib/query2/engine/QueryException.java
index 8a9a5df..3d92ba3 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/engine/QueryException.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/engine/QueryException.java
@@ -13,9 +13,14 @@
// limitations under the License.
package com.google.devtools.build.lib.query2.engine;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.Query;
+import java.util.Optional;
+
/**
*/
public class QueryException extends Exception {
+ private final Optional<FailureDetail> failureDetail;
/**
* Returns a better error message for the query.
@@ -36,17 +41,34 @@
public QueryException(QueryException e, QueryExpression toplevel) {
super(describeFailedQuery(e, toplevel), e);
this.expression = null;
+ this.failureDetail = Optional.empty();
}
public QueryException(QueryExpression expression, String message) {
super(message);
this.expression = expression;
+ this.failureDetail = Optional.empty();
+ }
+
+ public QueryException(QueryExpression expression, String message, Query.Code queryCode) {
+ super(message);
+ this.expression = expression;
+ this.failureDetail =
+ Optional.of(
+ FailureDetail.newBuilder()
+ .setMessage(message)
+ .setQuery(Query.newBuilder().setCode(queryCode).build())
+ .build());
}
public QueryException(String message) {
this(null, message);
}
+ public QueryException(String message, Query.Code queryCode) {
+ this(null, message, queryCode);
+ }
+
/**
* Returns the subexpression for which evaluation failed, or null if
* the failure occurred during lexing/parsing.
@@ -55,4 +77,8 @@
return expression;
}
+ /** Returns an optional {@link FailureDetail} containing fine grained detail code. */
+ public Optional<FailureDetail> getFailureDetail() {
+ return failureDetail;
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java
index 6713171..f39c973 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java
@@ -39,6 +39,7 @@
import com.google.devtools.build.lib.server.FailureDetails.Interrupted;
import com.google.devtools.build.lib.server.FailureDetails.Query;
import com.google.devtools.build.lib.server.FailureDetails.Query.Code;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.Either;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.InterruptedFailureDetails;
@@ -80,7 +81,11 @@
} catch (QueryException e) {
String message = "Error while parsing '" + query + "': " + e.getMessage();
env.getReporter().handle(Event.error(null, message));
- return Either.ofLeft(BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR));
+ return e.getFailureDetail().isPresent()
+ ? Either.ofLeft(
+ BlazeCommandResult.detailedExitCode(
+ DetailedExitCode.of(ExitCode.COMMAND_LINE_ERROR, e.getFailureDetail().get())))
+ : Either.ofLeft(BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR));
}
try {
@@ -117,64 +122,60 @@
QueryEvalResult result;
boolean catastrophe = true;
- try (SilentCloseable closeable = Profiler.instance().profile("queryEnv.evaluateQuery")) {
- result = queryEnv.evaluateQuery(expr, callback);
- catastrophe = false;
- } catch (QueryException e) {
- catastrophe = false;
- // Keep consistent with reportBuildFileError()
- env.getReporter()
- // TODO(bazel-team): this is a kludge to fix a bug observed in the wild. We should make
- // sure no null error messages ever get in.
- .handle(Event.error(e.getMessage() == null ? e.toString() : e.getMessage()));
- return Either.ofLeft(BlazeCommandResult.exitCode(ExitCode.ANALYSIS_FAILURE));
- } catch (InterruptedException e) {
- catastrophe = false;
- IOException ioException = callback.getIoException();
- if (ioException == null || ioException instanceof ClosedByInterruptException) {
- return reportAndCreateInterruptedResult(env);
- } else {
- env.getReporter().handle(Event.error("I/O error: " + e.getMessage()));
- return Either.ofLeft(BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR));
- }
- } catch (IOException e) {
- catastrophe = false;
- env.getReporter().handle(Event.error("I/O error: " + e.getMessage()));
- return Either.ofLeft(BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR));
- } finally {
- if (!catastrophe) {
- try {
- out.flush();
- } catch (IOException e) {
- return reportAndCreateFlushFailureResult(env, e);
+ try {
+ try (SilentCloseable closeable = Profiler.instance().profile("queryEnv.evaluateQuery")) {
+ result = queryEnv.evaluateQuery(expr, callback);
+ catastrophe = false;
+ } catch (QueryException e) {
+ catastrophe = false;
+ // Keep consistent with reportBuildFileError()
+ env.getReporter()
+ // TODO(bazel-team): this is a kludge to fix a bug observed in the wild. We should make
+ // sure no null error messages ever get in.
+ .handle(Event.error(e.getMessage() == null ? e.toString() : e.getMessage()));
+ return Either.ofLeft(BlazeCommandResult.exitCode(ExitCode.ANALYSIS_FAILURE));
+ } catch (InterruptedException e) {
+ catastrophe = false;
+ IOException ioException = callback.getIoException();
+ if (ioException == null || ioException instanceof ClosedByInterruptException) {
+ return reportAndCreateInterruptedResult(env);
+ } else {
+ env.getReporter().handle(Event.error("I/O error: " + e.getMessage()));
+ return Either.ofLeft(BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR));
}
- }
- }
- if (!streamResults) {
- disableAnsiCharactersFiltering(env);
- try (SilentCloseable closeable = Profiler.instance().profile("QueryOutputUtils.output")) {
- Set<Target> targets =
- ((AggregateAllOutputFormatterCallback<Target, ?>) callback).getResult();
- QueryOutputUtils.output(
- queryOptions,
- result,
- targets,
- formatter,
- out,
- queryOptions.aspectDeps.createResolver(env.getPackageManager(), env.getReporter()),
- env.getReporter());
- } catch (ClosedByInterruptException | InterruptedException e) {
- return reportAndCreateInterruptedResult(env);
} catch (IOException e) {
+ catastrophe = false;
env.getReporter().handle(Event.error("I/O error: " + e.getMessage()));
return Either.ofLeft(BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR));
} finally {
- try {
+ if (!catastrophe) {
out.flush();
- } catch (IOException e) {
- return reportAndCreateFlushFailureResult(env, e);
}
}
+ if (!streamResults) {
+ disableAnsiCharactersFiltering(env);
+ try (SilentCloseable closeable = Profiler.instance().profile("QueryOutputUtils.output")) {
+ Set<Target> targets =
+ ((AggregateAllOutputFormatterCallback<Target, ?>) callback).getResult();
+ QueryOutputUtils.output(
+ queryOptions,
+ result,
+ targets,
+ formatter,
+ out,
+ queryOptions.aspectDeps.createResolver(env.getPackageManager(), env.getReporter()),
+ env.getReporter());
+ } catch (ClosedByInterruptException | InterruptedException e) {
+ return reportAndCreateInterruptedResult(env);
+ } catch (IOException e) {
+ env.getReporter().handle(Event.error("I/O error: " + e.getMessage()));
+ return Either.ofLeft(BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR));
+ } finally {
+ out.flush();
+ }
+ }
+ } catch (IOException e) {
+ return reportAndCreateFlushFailureResult(env, e);
}
return Either.ofRight(result);
diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto
index 656be6a..1b59140 100644
--- a/src/main/protobuf/failure_details.proto
+++ b/src/main/protobuf/failure_details.proto
@@ -596,6 +596,8 @@
QUERY_STDOUT_FLUSH_FAILURE = 13 [(metadata) = { exit_code: 36 }];
ANALYSIS_QUERY_PREREQ_UNMET = 14 [(metadata) = { exit_code: 2 }];
QUERY_RESULTS_FLUSH_FAILURE = 15 [(metadata) = { exit_code: 36 }];
+ UNCLOSED_QUOTATION_EXPRESSION_ERROR = 16 [(metadata) = { exit_code: 2 }];
+ VARIABLE_NAME_INVALID = 17 [(metadata) = { exit_code: 7 }];
reserved 7 to 12; // For internal use
}