Skip to content

Commit 00530c0

Browse files
committed
Improve error handling
Bail out of formatting if any invariants are violated (using a new unchecked exception) instead of trying to continue and accumulating a list of errors. Trying to continue rarely produces additional useful information, and may obscure the root cause of the problem. MOE_MIGRATED_REVID=130001891
1 parent e22931a commit 00530c0

File tree

8 files changed

+94
-42
lines changed

8 files changed

+94
-42
lines changed

core/src/main/java/com/google/googlejavaformat/FormatterDiagnostic.java

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,44 @@
1414

1515
package com.google.googlejavaformat;
1616

17-
import com.google.common.base.Preconditions;
17+
import static com.google.common.base.Preconditions.checkArgument;
18+
import static com.google.common.base.Preconditions.checkNotNull;
1819

1920
/** An error that prevented formatting from succeeding. */
2021
public class FormatterDiagnostic {
2122
private final int lineNumber;
2223
private final String message;
2324
private final int column;
2425

25-
public FormatterDiagnostic(int lineNumber, int column, String message) {
26-
Preconditions.checkArgument(lineNumber >= 0);
27-
Preconditions.checkArgument(column >= 0);
28-
Preconditions.checkNotNull(message);
26+
public static FormatterDiagnostic create(String message) {
27+
return new FormatterDiagnostic(-1, -1, message);
28+
}
29+
30+
public static FormatterDiagnostic create(int lineNumber, int column, String message) {
31+
checkArgument(lineNumber >= 0);
32+
checkArgument(column >= 0);
33+
checkNotNull(message);
34+
return new FormatterDiagnostic(lineNumber, column, message);
35+
}
2936

37+
private FormatterDiagnostic(int lineNumber, int column, String message) {
3038
this.lineNumber = lineNumber;
3139
this.column = column;
3240
this.message = message;
3341
}
3442

35-
/** Returns the line number on which the error occurred. */
43+
/**
44+
* Returns the line number on which the error occurred, or {@code -1} if the error does not have a
45+
* line number.
46+
*/
3647
public int line() {
3748
return lineNumber;
3849
}
3950

40-
/** Returns the 0-indexed column number on which the error occurred. */
51+
/**
52+
* Returns the 0-indexed column number on which the error occurred, or {@code -1} if the error
53+
* does not have a column.
54+
*/
4155
public int column() {
4256
return column;
4357
}
@@ -47,13 +61,18 @@ public String message() {
4761
return message;
4862
}
4963

50-
@Override
5164
public String toString() {
5265
StringBuilder sb = new StringBuilder();
53-
sb.append(lineNumber).append(':');
54-
// internal column numbers are 0-based, but diagnostics use 1-based indexing by convention
55-
sb.append(column + 1).append(':');
56-
sb.append(' ');
66+
if (lineNumber >= 0) {
67+
sb.append(lineNumber).append(':');
68+
}
69+
if (column >= 0) {
70+
// internal column numbers are 0-based, but diagnostics use 1-based indexing by convention
71+
sb.append(column + 1).append(':');
72+
}
73+
if (lineNumber >= 0 || column >= 0) {
74+
sb.append(' ');
75+
}
5776
sb.append("error: ").append(message);
5877
return sb.toString();
5978
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2016 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. 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 distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
15+
package com.google.googlejavaformat;
16+
17+
/** An unchecked formatting error. */
18+
public class FormattingError extends Error {
19+
20+
private final FormatterDiagnostic diagnostic;
21+
22+
public FormattingError(FormatterDiagnostic diagnostic) {
23+
this.diagnostic = diagnostic;
24+
}
25+
26+
public FormatterDiagnostic diagnostic() {
27+
return diagnostic;
28+
}
29+
}

core/src/main/java/com/google/googlejavaformat/Input.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public String toString() {
124124
* character offsets to numbers.
125125
* */
126126
public FormatterDiagnostic createDiagnostic(int inputPosition, String message) {
127-
return new FormatterDiagnostic(
127+
return FormatterDiagnostic.create(
128128
getLineNumber(inputPosition), getColumnNumber(inputPosition), message);
129129
}
130130
}

core/src/main/java/com/google/googlejavaformat/OpsBuilder.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,22 +144,20 @@ public BlankLineWanted merge(BlankLineWanted other) {
144144
private final Input input;
145145
private final List<Op> ops = new ArrayList<>();
146146
private final Output output;
147-
private final List<FormatterDiagnostic> errors;
148147
private static final Indent.Const ZERO = Indent.Const.ZERO;
149148

150149
private int tokenI = 0;
151150
private int inputPosition = Integer.MIN_VALUE;
152151

153152
/**
154153
* The {@code OpsBuilder} constructor.
154+
*
155155
* @param input the {@link Input}, used for retrieve information from the AST
156156
* @param output the {@link Output}, used here only to record blank-line information
157-
* @param errors mutable list to receive errors
158157
*/
159-
public OpsBuilder(Input input, Output output, List<FormatterDiagnostic> errors) {
158+
public OpsBuilder(Input input, Output output) {
160159
this.input = input;
161160
this.output = output;
162-
this.errors = errors; // Assignment of mutable collection.
163161
}
164162

165163
/** Get the {@code OpsBuilder}'s {@link Input}. */
@@ -255,7 +253,7 @@ public final void token(
255253
* (for example) we're guessing at an optional token.
256254
*/
257255
if (realOrImaginary.isReal()) {
258-
errors.add(
256+
throw new FormattingError(
259257
input.createDiagnostic(
260258
inputPosition, String.format("generated extra token \"%s\"", token)));
261259
}
@@ -550,7 +548,6 @@ public final String toString() {
550548
return MoreObjects.toStringHelper(this)
551549
.add("input", input)
552550
.add("ops", ops)
553-
.add("errors", errors)
554551
.add("output", output)
555552
.add("tokenI", tokenI)
556553
.add("inputPosition", inputPosition)

core/src/main/java/com/google/googlejavaformat/java/Formatter.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.googlejavaformat.Doc;
2626
import com.google.googlejavaformat.DocBuilder;
2727
import com.google.googlejavaformat.FormatterDiagnostic;
28+
import com.google.googlejavaformat.FormattingError;
2829
import com.google.googlejavaformat.Op;
2930
import com.google.googlejavaformat.OpsBuilder;
3031
import java.io.IOException;
@@ -89,18 +90,15 @@ public Formatter(JavaFormatterOptions options) {
8990
}
9091

9192
/**
92-
* Construct a {@code Formatter} given a Java compilation unit. Parses the code; builds a
93-
* {@link JavaInput} and the corresponding {@link JavaOutput}.
93+
* Construct a {@code Formatter} given a Java compilation unit. Parses the code; builds a {@link
94+
* JavaInput} and the corresponding {@link JavaOutput}.
95+
*
9496
* @param javaInput the input, a Java compilation unit
9597
* @param javaOutput the {@link JavaOutput}
9698
* @param options the {@link JavaFormatterOptions}
97-
* @param errors mutable list to receive errors
9899
*/
99-
static void format(
100-
JavaInput javaInput,
101-
JavaOutput javaOutput,
102-
JavaFormatterOptions options,
103-
List<FormatterDiagnostic> errors) {
100+
static void format(JavaInput javaInput, JavaOutput javaOutput, JavaFormatterOptions options)
101+
throws FormatterException {
104102
ASTParser parser = ASTParser.newParser(AST.JLS8);
105103
parser.setSource(javaInput.getText().toCharArray());
106104
@SuppressWarnings("unchecked") // safe by specification
@@ -110,12 +108,13 @@ static void format(
110108
CompilationUnit unit = (CompilationUnit) parser.createAST(null);
111109
javaInput.setCompilationUnit(unit);
112110
if (unit.getMessages().length > 0) {
111+
List<FormatterDiagnostic> errors = new ArrayList<>();
113112
for (Message message : unit.getMessages()) {
114113
errors.add(javaInput.createDiagnostic(message.getStartPosition(), message.getMessage()));
115114
}
116-
return;
115+
throw new FormatterException(errors);
117116
}
118-
OpsBuilder builder = new OpsBuilder(javaInput, javaOutput, errors);
117+
OpsBuilder builder = new OpsBuilder(javaInput, javaOutput);
119118
// Output the compilation unit.
120119
new JavaInputAstVisitor(builder, options.indentationMultiplier()).visit(unit);
121120
builder.sync(javaInput.getText().length());
@@ -182,10 +181,10 @@ public ImmutableList<Replacement> getFormatReplacements(
182181

183182
JavaInput javaInput = new JavaInput(input);
184183
JavaOutput javaOutput = new JavaOutput(javaInput, new JavaCommentsHelper(options));
185-
List<FormatterDiagnostic> errors = new ArrayList<>();
186-
format(javaInput, javaOutput, options, errors);
187-
if (!errors.isEmpty()) {
188-
throw new FormatterException(errors);
184+
try {
185+
format(javaInput, javaOutput, options);
186+
} catch (FormattingError e) {
187+
throw new FormatterException(e.diagnostic());
189188
}
190189
RangeSet<Integer> tokenRangeSet = javaInput.characterRangesToTokenRanges(characterRanges);
191190
return javaOutput.getFormatReplacements(tokenRangeSet);

core/src/main/java/com/google/googlejavaformat/java/FormatterException.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
package com.google.googlejavaformat.java;
1616

17-
import com.google.common.base.Joiner;
1817
import com.google.common.collect.ImmutableList;
1918
import com.google.googlejavaformat.FormatterDiagnostic;
2019
import java.util.List;
@@ -24,15 +23,18 @@ public final class FormatterException extends Exception {
2423

2524
private ImmutableList<FormatterDiagnostic> diagnostics;
2625

27-
FormatterException(String message) {
28-
super(message);
26+
public FormatterException(String message) {
27+
this(FormatterDiagnostic.create(message));
2928
}
3029

31-
public FormatterException(List<FormatterDiagnostic> diagnostics) {
32-
super(Joiner.on('\n').join(diagnostics));
33-
this.diagnostics = ImmutableList.copyOf(diagnostics);
30+
public FormatterException(FormatterDiagnostic diagnostic) {
31+
this(ImmutableList.of(diagnostic));
3432
}
3533

34+
public FormatterException(Iterable<FormatterDiagnostic> diagnostics) {
35+
super(diagnostics.iterator().next().toString());
36+
this.diagnostics = ImmutableList.copyOf(diagnostics);
37+
}
3638
public List<FormatterDiagnostic> diagnostics() {
3739
return diagnostics;
3840
}

core/src/main/java/com/google/googlejavaformat/java/Main.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import static java.nio.charset.StandardCharsets.UTF_8;
1818

1919
import com.google.common.io.ByteStreams;
20+
import com.google.googlejavaformat.FormatterDiagnostic;
2021
import com.google.googlejavaformat.java.JavaFormatterOptions.Style;
2122
import java.io.IOError;
2223
import java.io.IOException;
@@ -141,7 +142,9 @@ private int formatFiles(CommandLineOptions parameters, JavaFormatterOptions opti
141142
continue;
142143
} catch (ExecutionException e) {
143144
if (e.getCause() instanceof FormatterException) {
144-
errWriter.println(result.getKey() + ":" + e.getCause().getMessage());
145+
for (FormatterDiagnostic diagnostic : ((FormatterException) e.getCause()).diagnostics()) {
146+
errWriter.println(result.getKey() + ":" + diagnostic.toString());
147+
}
145148
} else {
146149
errWriter.println(result.getKey() + ": error: " + e.getCause().getMessage());
147150
e.getCause().printStackTrace(errWriter);
@@ -179,7 +182,9 @@ private int formatStdin(CommandLineOptions parameters, JavaFormatterOptions opti
179182
outWriter.write(output);
180183
return 0;
181184
} catch (FormatterException e) {
182-
errWriter.println(STDIN_FILENAME + ":" + e.getMessage());
185+
for (FormatterDiagnostic diagnostic : e.diagnostics()) {
186+
errWriter.println(STDIN_FILENAME + ":" + diagnostic.toString());
187+
}
183188
return 1;
184189
// TODO(cpovirk): Catch other types of exception (as we do in the formatFiles case).
185190
}

core/src/test/java/com/google/googlejavaformat/java/ImportOrdererTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ public void reorder() throws FormatterException {
386386
throw e;
387387
}
388388
assertThat(reordered).endsWith("\n");
389-
assertThat(e.getMessage()).isEqualTo(reordered.substring(2, reordered.length() - 1));
389+
assertThat(e.getMessage())
390+
.isEqualTo("error: " + reordered.substring(2, reordered.length() - 1));
390391
}
391392
}
392393
}

0 commit comments

Comments
 (0)