Skip to content

Commit

Permalink
[improvement] Helpful error output is provided when formatting fails (#…
Browse files Browse the repository at this point in the history
…366)



## Before this PR
```
java.lang.RuntimeException: com.google.googlejavaformat.java.FormatterException: 5:15: error: ';' expected

	at com.palantir.conjure.java.util.Goethe.formatAndEmit(Goethe.java:55)
	at com.palantir.conjure.java.util.GoetheTest.testFormatAndEmit(GoetheTest.java:38)
Caused by: com.google.googlejavaformat.java.FormatterException: 5:15: error: ';' expected
	at com.google.googlejavaformat.java.FormatterException.fromJavacDiagnostics(FormatterException.java:50)
	at com.google.googlejavaformat.java.Formatter.format(Formatter.java:151)
	at com.google.googlejavaformat.java.Formatter.getFormatReplacements(Formatter.java:258)
	at com.google.googlejavaformat.java.Formatter.formatSource(Formatter.java:234)
	at com.google.googlejavaformat.java.Formatter.formatSource(Formatter.java:202)
	at com.google.googlejavaformat.java.Formatter.formatSource(Formatter.java:189)
	at com.palantir.conjure.java.util.Goethe.formatAndEmit(Goethe.java:52)
	... 25 more
```

## After this PR
```
java.lang.RuntimeException: Failed to format 'com.palantir.foo.Foo'
';' expected
    type oops name = bar;
            ^

	at com.palantir.conjure.java.util.Goethe.formatAndEmit(Goethe.java:55)
	at com.palantir.conjure.java.util.GoetheTest.testFormatAndEmit(GoetheTest.java:38)
Caused by: com.google.googlejavaformat.java.FormatterException: 5:15: error: ';' expected
	at com.google.googlejavaformat.java.FormatterException.fromJavacDiagnostics(FormatterException.java:50)
	at com.google.googlejavaformat.java.Formatter.format(Formatter.java:151)
	at com.google.googlejavaformat.java.Formatter.getFormatReplacements(Formatter.java:258)
	at com.google.googlejavaformat.java.Formatter.formatSource(Formatter.java:234)
	at com.google.googlejavaformat.java.Formatter.formatSource(Formatter.java:202)
	at com.google.googlejavaformat.java.Formatter.formatSource(Formatter.java:189)
	at com.palantir.conjure.java.util.Goethe.formatAndEmit(Goethe.java:52)
	... 25 more
```

==COMMIT_MSG==
Helpful error output is provided when formatting fails
==COMMIT_MSG=
  • Loading branch information
carterkozak authored and bulldozer-bot[bot] committed May 7, 2019
1 parent 8bb1d49 commit 45e300a
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

package com.palantir.conjure.java.util;

import com.google.common.base.CharMatcher;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.io.CharSink;
import com.google.common.io.CharSource;
import com.google.googlejavaformat.FormatterDiagnostic;
import com.google.googlejavaformat.java.Formatter;
import com.google.googlejavaformat.java.FormatterException;
import com.google.googlejavaformat.java.JavaFormatterOptions;
Expand All @@ -29,6 +32,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

/** Tools for a better JavaPoet. */
public final class Goethe {
Expand All @@ -46,13 +50,39 @@ public static Path formatAndEmit(JavaFile file, Path baseDir) {
file.writeTo(code);

CharSink sink = com.google.common.io.Files.asCharSink(outputFile.toFile(), StandardCharsets.UTF_8);
JAVA_FORMATTER.formatSource(CharSource.wrap(code), sink);
try {
JAVA_FORMATTER.formatSource(CharSource.wrap(code), sink);
} catch (FormatterException e) {
throw new RuntimeException(generateMessage(file, code.toString(), e.diagnostics()), e);
}
return outputFile;
} catch (IOException | FormatterException e) {
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static String generateMessage(
JavaFile file, String unformattedSource, List<FormatterDiagnostic> formatterDiagnostics) {
try {
List<String> lines = Splitter.on('\n').splitToList(unformattedSource);
StringBuilder failureText = new StringBuilder();
failureText.append("Failed to format '")
.append(file.packageName).append('.').append(file.typeSpec.name).append("'\n");
for (FormatterDiagnostic formatterDiagnostic : formatterDiagnostics) {
failureText.append(formatterDiagnostic.message()).append("\n")
// Diagnostic values are one-indexed, while our list is zero-indexed.
.append(lines.get(formatterDiagnostic.line() - 1))
.append('\n')
// Offset by two to convert from one-indexed to zero indexed values, and account for the caret.
.append(Strings.repeat(" ", Math.max(0, formatterDiagnostic.column() - 2)))
.append("^\n\n");
}
return CharMatcher.is('\n').trimFrom(failureText.toString());
} catch (RuntimeException e) {
return "Failed to format:\n" + unformattedSource;
}
}

/**
* Returns the full path for the given Java file and Java base dir. In a nutshell, turns packages into directories,
* e.g., {@code com.foo.bar.MyClass -> /<baseDir>/com/foo/bar/MyClass.java} and creates all directories.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.conjure.java.util;

import static org.assertj.core.api.Assertions.assertThatThrownBy;

import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

public class GoetheTest {

@Rule
public TemporaryFolder folder = new TemporaryFolder();

@Test
public void testFormatAndEmit() {
JavaFile javaFile = JavaFile.builder("com.palantir.foo", TypeSpec.classBuilder("Foo")
.addStaticBlock(CodeBlock.builder().addStatement("type oops name = bar").build())
.build()).build();
assertThatThrownBy(() -> Goethe.formatAndEmit(javaFile, folder.getRoot().toPath()))
.hasMessageContaining("Failed to format 'com.palantir.foo.Foo'")
.hasMessageContaining("';' expected")
.hasMessageContaining("" // newline to align the output
+ " type oops name = bar;\n"
+ " ^");
}

}

0 comments on commit 45e300a

Please sign in to comment.