From 1a617aeb2b0ff4a278f85257e1a8193fa125d468 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Thu, 12 Sep 2024 15:31:42 -0700 Subject: [PATCH] Print the problem line as context in error messages PiperOrigin-RevId: 674035200 --- .../java/FormatterException.java | 21 ++++++++++++++++ .../google/googlejavaformat/java/Main.java | 9 ++----- .../googlejavaformat/java/MainTest.java | 25 +++++++++++++++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java b/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java index 808916c2c..0eb06461b 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java +++ b/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java @@ -16,10 +16,13 @@ import static java.util.Locale.ENGLISH; +import com.google.common.base.CharMatcher; +import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.googlejavaformat.FormatterDiagnostic; import java.util.List; +import java.util.regex.Pattern; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; @@ -55,4 +58,22 @@ private static FormatterDiagnostic toFormatterDiagnostic(Diagnostic input) { return FormatterDiagnostic.create( (int) input.getLineNumber(), (int) input.getColumnNumber(), input.getMessage(ENGLISH)); } + + public String formatDiagnostics(String path, String input) { + List lines = Splitter.on(NEWLINE_PATTERN).splitToList(input); + StringBuilder sb = new StringBuilder(); + for (FormatterDiagnostic diagnostic : diagnostics()) { + sb.append(path).append(":").append(diagnostic).append(System.lineSeparator()); + int line = diagnostic.line(); + int column = diagnostic.column(); + if (line != -1 && column != -1) { + sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(lines.get(line - 1))) + .append(System.lineSeparator()); + sb.append(" ".repeat(column)).append('^').append(System.lineSeparator()); + } + } + return sb.toString(); + } + + private static final Pattern NEWLINE_PATTERN = Pattern.compile("\\R"); } diff --git a/core/src/main/java/com/google/googlejavaformat/java/Main.java b/core/src/main/java/com/google/googlejavaformat/java/Main.java index 0845e0ec2..f1affa74d 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Main.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Main.java @@ -20,7 +20,6 @@ import com.google.common.io.ByteStreams; import com.google.common.util.concurrent.MoreExecutors; -import com.google.googlejavaformat.FormatterDiagnostic; import com.google.googlejavaformat.java.JavaFormatterOptions.Style; import java.io.IOError; import java.io.IOException; @@ -175,9 +174,7 @@ private int formatFiles(CommandLineOptions parameters, JavaFormatterOptions opti for (FormatFileCallable.Result result : results) { Path path = result.path(); if (result.exception() != null) { - for (FormatterDiagnostic diagnostic : result.exception().diagnostics()) { - errWriter.println(path + ":" + diagnostic); - } + errWriter.print(result.exception().formatDiagnostics(path.toString(), result.input())); allOk = false; continue; } @@ -224,9 +221,7 @@ private int formatStdin(CommandLineOptions parameters, JavaFormatterOptions opti FormatFileCallable.Result result = new FormatFileCallable(parameters, null, input, options).call(); if (result.exception() != null) { - for (FormatterDiagnostic diagnostic : result.exception().diagnostics()) { - errWriter.println(stdinFilename + ":" + diagnostic); - } + errWriter.print(result.exception().formatDiagnostics(stdinFilename, input)); ok = false; } else { String output = result.output(); diff --git a/core/src/test/java/com/google/googlejavaformat/java/MainTest.java b/core/src/test/java/com/google/googlejavaformat/java/MainTest.java index 42e12d860..d7de40510 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/MainTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/MainTest.java @@ -635,4 +635,29 @@ public void reorderModifiersOptionTest() throws Exception { .formatSource(source)) .isEqualTo(source); } + + @Test + public void badIdentifier() throws Exception { + Path path = testFolder.newFile("Test.java").toPath(); + String[] input = { + "class Test {", // + " void f(int package) {}", + "}", + "", + }; + String source = joiner.join(input); + Files.writeString(path, source, UTF_8); + StringWriter out = new StringWriter(); + StringWriter err = new StringWriter(); + Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in); + int errorCode = main.format(path.toAbsolutePath().toString()); + assertWithMessage("Error Code").that(errorCode).isEqualTo(1); + String[] expected = { + path + ":2:14: error: expected", // + " void f(int package) {}", + " ^", + "", + }; + assertThat(err.toString()).isEqualTo(joiner.join(expected)); + } }