blob: ed914594d6c9e52cc0e5c8f47497096e904507e2 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.syntax;
16
Francois-Rene Rideau41d19f02015-08-27 15:00:12 +000017import com.google.common.base.Joiner;
Francois-Rene Rideau41d19f02015-08-27 15:00:12 +000018import com.google.common.base.Throwables;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010019import com.google.devtools.build.lib.events.Location;
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +000020import com.google.devtools.build.lib.util.LoggingUtil;
Mark Schaller6df81792015-12-10 18:47:47 +000021import com.google.devtools.build.lib.util.Preconditions;
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +000022import java.util.logging.Level;
Laurent Le Brun6e5eecb2015-09-10 11:37:32 +000023import javax.annotation.Nullable;
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +000024
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025/**
26 * Exceptions thrown during evaluation of BUILD ASTs or Skylark extensions.
27 *
28 * <p>This exception must always correspond to a repeatable, permanent error, i.e. evaluating the
29 * same package again must yield the same exception. Notably, do not use this for reporting I/O
30 * errors.
31 *
32 * <p>This requirement is in place so that we can cache packages where an error is reported by way
33 * of {@link EvalException}.
34 */
35public class EvalException extends Exception {
36
Laurent Le Brun6e5eecb2015-09-10 11:37:32 +000037 @Nullable private Location location;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038 private final String message;
39 private final boolean dueToIncompleteAST;
40
Francois-Rene Rideau41d19f02015-08-27 15:00:12 +000041 private static final Joiner LINE_JOINER = Joiner.on("\n").skipNulls();
42 private static final Joiner FIELD_JOINER = Joiner.on(": ").skipNulls();
43
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010044 /**
45 * @param location the location where evaluation/execution failed.
46 * @param message the error message.
47 */
48 public EvalException(Location location, String message) {
49 this.location = location;
50 this.message = Preconditions.checkNotNull(message);
51 this.dueToIncompleteAST = false;
52 }
53
Laurent Le Brunfa407e52016-11-04 15:53:08 +000054 public EvalException(Location location, String message, String url) {
55 this.location = location;
56 this.dueToIncompleteAST = false;
57 this.message =
58 Preconditions.checkNotNull(message)
59 + "\n"
60 + "Need help? See "
61 + Preconditions.checkNotNull(url);
62 }
63
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010064 /**
65 * @param location the location where evaluation/execution failed.
66 * @param message the error message.
67 * @param dueToIncompleteAST if the error is caused by a previous error, such as parsing.
68 */
69 public EvalException(Location location, String message, boolean dueToIncompleteAST) {
Michajlo Matijkiw32994ca2016-08-01 15:43:25 +000070 this(location, message, dueToIncompleteAST, true);
71 }
72
73 /**
74 * Create an EvalException with the option to not fill in the java stack trace. An optimization
75 * for ReturnException, and potentially others, which aren't exceptional enough to include a
76 * stack trace.
77 *
78 * @param location the location where evaluation/execution failed.
79 * @param message the error message.
80 * @param dueToIncompleteAST if the error is caused by a previous error, such as parsing.
81 * @param fillInJavaStackTrace whether or not to fill in the java stack trace for this exception
82 */
83 EvalException(
84 Location location,
85 String message,
86 boolean dueToIncompleteAST,
87 boolean fillInJavaStackTrace) {
88 super(null, null, /*enableSuppression=*/ true, fillInJavaStackTrace);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010089 this.location = location;
90 this.message = Preconditions.checkNotNull(message);
91 this.dueToIncompleteAST = dueToIncompleteAST;
92 }
93
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +000094 /**
95 * @param location the location where evaluation/execution failed.
96 * @param message the error message.
97 * @param cause a Throwable that caused this exception.
98 */
99 public EvalException(Location location, String message, Throwable cause) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100 super(cause);
101 this.location = location;
Francois-Rene Rideau41d19f02015-08-27 15:00:12 +0000102 // This is only used from Skylark, it's useful for debugging.
103 this.message = FIELD_JOINER.join(message, getCauseMessage(message));
104 if (this.message.isEmpty()) {
105 String details;
106 if (cause == null) {
107 details = "Invalid EvalException: no cause given!";
108 } else {
109 details = "Invalid EvalException:\n" + Throwables.getStackTraceAsString(cause);
Florian Weikert77303b42015-08-27 08:24:24 +0000110 }
Francois-Rene Rideau41d19f02015-08-27 15:00:12 +0000111 LoggingUtil.logToRemote(Level.SEVERE, details, cause);
112 throw new IllegalArgumentException(details);
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000113 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100114 this.dueToIncompleteAST = false;
115 }
116
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000117 public EvalException(Location location, Throwable cause) {
118 this(location, null, cause);
119 }
120
121 /**
122 * Returns the error message with location info if exists.
123 */
124 public String print() { // TODO(bazel-team): do we also need a toString() method?
Francois-Rene Rideau41d19f02015-08-27 15:00:12 +0000125 return LINE_JOINER.join("\n", FIELD_JOINER.join(getLocation(), message),
126 (dueToIncompleteAST ? "due to incomplete AST" : ""),
127 getCauseMessage(message));
Florian Weikert3f610e82015-08-18 14:37:46 +0000128 }
Francois-Rene Rideau41d19f02015-08-27 15:00:12 +0000129
130 /**
131 * @param message the message of this exception, so far.
132 * @return a message for the cause of the exception, if the main message (passed as argument)
133 * doesn't already contain this cause; return null if no new information is available.
134 */
135 private String getCauseMessage(String message) {
Florian Weikert3f610e82015-08-18 14:37:46 +0000136 Throwable cause = getCause();
137 if (cause == null) {
Francois-Rene Rideau41d19f02015-08-27 15:00:12 +0000138 return null;
Florian Weikert3f610e82015-08-18 14:37:46 +0000139 }
140 String causeMessage = cause.getMessage();
Francois-Rene Rideau41d19f02015-08-27 15:00:12 +0000141 if (causeMessage == null) {
142 return null;
143 }
144 if (message == null) {
145 return causeMessage;
146 }
147 // Skip the cause if it is redundant with the message so far.
148 if (message.contains(causeMessage)) {
149 return null;
150 }
151 return causeMessage;
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000152 }
153
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100154 /**
155 * Returns the error message.
156 */
157 @Override
158 public String getMessage() {
159 return message;
160 }
161
162 /**
163 * Returns the location of the evaluation error.
164 */
Laurent Le Brun6e5eecb2015-09-10 11:37:32 +0000165 @Nullable
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100166 public Location getLocation() {
167 return location;
168 }
169
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000170 /**
171 * Returns a boolean that tells whether this exception was due to an incomplete AST
172 */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100173 public boolean isDueToIncompleteAST() {
174 return dueToIncompleteAST;
175 }
176
177 /**
Francois-Rene Rideaub6038e02015-06-11 19:51:16 +0000178 * Ensures that this EvalException has proper location information.
179 * Does nothing if the exception already had a location, or if no location is provided.
180 * @return this EvalException, in fluent style.
181 */
182 public EvalException ensureLocation(Location loc) {
183 if (location == null && loc != null) {
184 location = loc;
185 }
186 return this;
187 }
188
189 /**
Florian Weikert4b67d4f2015-09-14 13:35:34 +0000190 * Returns whether this exception can be added to a stack trace created by {@link
191 * EvalExceptionWithStackTrace}.
192 */
193 public boolean canBeAddedToStackTrace() {
194 return true;
195 }
196
197 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100198 * A class to support a special case of EvalException when the cause of the error is an
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000199 * Exception during a direct Java call. Allow the throwing code to provide context in a message.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100200 */
201 public static final class EvalExceptionWithJavaCause extends EvalException {
202
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000203 /**
204 * @param location the location where evaluation/execution failed.
205 * @param message the error message.
206 * @param cause a Throwable that caused this exception.
207 */
208 public EvalExceptionWithJavaCause(Location location, String message, Throwable cause) {
209 super(location, message, cause);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100210 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100211
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000212 /**
213 * @param location the location where evaluation/execution failed.
214 * @param cause a Throwable that caused this exception.
215 */
216 public EvalExceptionWithJavaCause(Location location, Throwable cause) {
217 this(location, null, cause);
218 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100219 }
220}