From aeb6b28c2787746f0b3594bb2a52ed24075d32d0 Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Sun, 12 Jul 2026 14:29:58 +0200 Subject: [PATCH] Fix off by one column in diagnostic when using "forked" execution of javac. --- .../plexus/compiler/javac/JavacCompiler.java | 2 +- .../javac/ErrorMessageParserTest.java | 41 ++++++++++------ .../compiler/javac/JavacCompilerTest.java | 47 +++++++++++++++++++ 3 files changed, 74 insertions(+), 16 deletions(-) diff --git a/plexus-compilers/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java b/plexus-compilers/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java index 9bf8fabe8..334a831f7 100644 --- a/plexus-compilers/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java +++ b/plexus-compilers/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java @@ -1016,7 +1016,7 @@ static CompilerMessage parseModernError(int exitCode, String error) { msgBuffer.append(EOL); final String message = msgBuffer.toString(); - final int startcolumn = Objects.requireNonNull(pointer).indexOf("^"); + final int startcolumn = Objects.requireNonNull(pointer).indexOf("^") + 1; int endcolumn = (context == null) ? startcolumn : context.indexOf(" ", startcolumn); if (endcolumn == -1) { endcolumn = Objects.requireNonNull(context).length(); diff --git a/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/ErrorMessageParserTest.java b/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/ErrorMessageParserTest.java index 8b159b1a2..60be6bdf3 100644 --- a/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/ErrorMessageParserTest.java +++ b/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/ErrorMessageParserTest.java @@ -68,7 +68,7 @@ public void testDeprecationMessage() throws Exception { assertEquals("Date(java.lang.String) in java.util.Date has been deprecated", compilerError.getMessage()); - assertEquals(63, compilerError.getStartColumn()); + assertEquals(64, compilerError.getStartColumn()); assertEquals(66, compilerError.getEndColumn()); @@ -92,7 +92,7 @@ public void testWarningMessage() { assertEquals("finally clause cannot complete normally", compilerError.getMessage()); - assertEquals(26, compilerError.getStartColumn()); + assertEquals(27, compilerError.getStartColumn()); assertEquals(27, compilerError.getEndColumn()); @@ -113,7 +113,7 @@ public void testErrorMessage() { assertEquals("not a statement", compilerError.getMessage()); - assertEquals(9, compilerError.getStartColumn()); + assertEquals(10, compilerError.getStartColumn()); assertEquals(11, compilerError.getEndColumn()); @@ -122,6 +122,17 @@ public void testErrorMessage() { assertEquals(7, compilerError.getEndLine()); } + @Test + public void testErrorColumnIsOneBased() { + String error = "Test.java:5: error: not a statement" + EOL + " null;" + EOL + " ^" + EOL; + + CompilerMessage compilerError = JavacCompiler.parseModernError(1, error); + + assertEquals(5, compilerError.getStartLine()); + assertEquals(2, compilerError.getStartColumn()); + assertEquals("Test.java:[5,2] not a statement", compilerError.toString()); + } + @Test public void testUnknownSymbolError() { String error = "./org/codehaus/foo/UnknownSymbol.java:7: cannot find symbol" + EOL + "symbol : method foo()" @@ -141,7 +152,7 @@ public void testUnknownSymbolError() { + "location: class org.codehaus.foo.UnknownSymbol", compilerError.getMessage()); - assertEquals(8, compilerError.getStartColumn()); + assertEquals(9, compilerError.getStartColumn()); assertEquals(14, compilerError.getEndColumn()); @@ -229,7 +240,7 @@ public void testUnixFileNames() { CompilerMessage compilerError = JavacCompiler.parseModernError(1, error); - assertEquals("/my/prj/src/main/java/test/prj/App.java:[11,45] not a statement", String.valueOf(compilerError)); + assertEquals("/my/prj/src/main/java/test/prj/App.java:[11,46] not a statement", String.valueOf(compilerError)); } @Test @@ -243,7 +254,7 @@ public void testWindowsDriveLettersMCOMPILER140() { CompilerMessage compilerError = JavacCompiler.parseModernError(1, error); assertEquals( - "c:\\Documents and Settings\\My Self\\Documents\\prj\\src\\main\\java\\test\\prj\\App.java:[11,45] not a statement", + "c:\\Documents and Settings\\My Self\\Documents\\prj\\src\\main\\java\\test\\prj\\App.java:[11,46] not a statement", String.valueOf(compilerError)); } @@ -650,7 +661,7 @@ public void testCRLF_windows() throws Exception { error1.getMessage(), "message"); assertEquals(31, error1.getStartLine(), "line"); - assertEquals(38, error1.getStartColumn(), "column"); + assertEquals(39, error1.getStartColumn(), "column"); CompilerMessage error2 = compilerErrors.get(2); assertEquals( "C:\\commander\\pre\\ec\\ec-http\\src\\main\\java\\com\\electriccloud\\http\\HttpUtil.java", @@ -661,7 +672,7 @@ public void testCRLF_windows() throws Exception { error2.getMessage(), "message"); assertEquals(151, error2.getStartLine(), "line"); - assertEquals(8, error2.getStartColumn(), "column"); + assertEquals(9, error2.getStartColumn(), "column"); CompilerMessage error3 = compilerErrors.get(3); assertEquals( "C:\\commander\\pre\\ec\\ec-http\\src\\main\\java\\com\\electriccloud\\http\\HttpUtil.java", @@ -672,7 +683,7 @@ public void testCRLF_windows() throws Exception { error3.getMessage(), "message"); assertEquals(152, error3.getStartLine(), "line"); - assertEquals(16, error3.getStartColumn(), "column"); + assertEquals(17, error3.getStartColumn(), "column"); } @Test @@ -701,7 +712,7 @@ public void testJava6Error() throws Exception { "cannot find symbol" + EOL + "symbol : class Properties" + EOL + "location: class Error", message1.getMessage()); - assertEquals(16, message1.getStartColumn()); + assertEquals(17, message1.getStartColumn()); assertEquals(26, message1.getEndColumn()); @@ -717,7 +728,7 @@ public void testJava6Error() throws Exception { "cannot find symbol" + EOL + "symbol : class Properties" + EOL + "location: class Error", message2.getMessage()); - assertEquals(35, message2.getStartColumn()); + assertEquals(36, message2.getStartColumn()); assertEquals(48, message2.getEndColumn()); @@ -753,7 +764,7 @@ public void testJava7Error() throws Exception { "cannot find symbol" + EOL + " symbol: class Properties" + EOL + " location: class Error", message1.getMessage()); - assertEquals(16, message1.getStartColumn()); + assertEquals(17, message1.getStartColumn()); assertEquals(26, message1.getEndColumn()); @@ -769,7 +780,7 @@ public void testJava7Error() throws Exception { "cannot find symbol" + EOL + " symbol: class Properties" + EOL + " location: class Error", message2.getMessage()); - assertEquals(35, message2.getStartColumn()); + assertEquals(36, message2.getStartColumn()); assertEquals(48, message2.getEndColumn()); @@ -1051,7 +1062,7 @@ public void testAnchoredWarning() throws IOException { "C:\\repo\\src\\it\\includes-output-when-compiler-forked\\src\\main\\java\\MyClass" + ".java", CompilerMessage.Kind.WARNING, 23, - 27, + 28, 23, 30, "[divzero] division by zero"), @@ -1082,7 +1093,7 @@ public void testMixedWarnings() throws IOException { "C:\\repo\\src\\it\\includes-output-when-compiler-forked\\src\\main\\java\\MyClass" + ".java", CompilerMessage.Kind.WARNING, 23, - 27, + 28, 23, 30, "[divzero] division by zero"), diff --git a/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/JavacCompilerTest.java b/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/JavacCompilerTest.java index 82a311cd9..f2f23ccec 100644 --- a/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/JavacCompilerTest.java +++ b/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/JavacCompilerTest.java @@ -1,15 +1,23 @@ package org.codehaus.plexus.compiler.javac; import java.io.BufferedReader; +import java.io.File; import java.io.IOException; import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; import java.util.List; import java.util.stream.Stream; +import org.codehaus.plexus.compiler.CompilerConfiguration; import org.codehaus.plexus.compiler.CompilerMessage; +import org.codehaus.plexus.compiler.CompilerResult; import org.codehaus.plexus.compiler.javac.JavacCompiler.JavaVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -119,4 +127,43 @@ void testExtractMajorAndMinorVersion() { assertEquals("21", JavacCompiler.extractMajorAndMinorVersion("javac 21")); assertEquals("1.8", JavacCompiler.extractMajorAndMinorVersion("javac 1.8.0_432")); } + + @Test + void testForkedAndInProcessDiagnosticsAreEqual(@TempDir Path tempDirectory) throws Exception { + Path sourceDirectory = tempDirectory.resolve("src"); + Files.createDirectories(sourceDirectory); + Files.write( + sourceDirectory.resolve("Test.java"), + Arrays.asList("class Test {", "Object foo() {", " null;", "}", "}"), + StandardCharsets.UTF_8); + + CompilerResult inProcess = compile(tempDirectory, sourceDirectory, false); + CompilerResult forked = compile(tempDirectory, sourceDirectory, true); + + assertFalse(inProcess.isSuccess()); + assertFalse(forked.isSuccess()); + assertEquals(1, inProcess.getCompilerMessages().size()); + assertEquals(1, forked.getCompilerMessages().size()); + assertEquals( + inProcess.getCompilerMessages().toString(), + forked.getCompilerMessages().toString()); + } + + private CompilerResult compile(Path tempDirectory, Path sourceDirectory, boolean fork) throws Exception { + File buildDirectory = + tempDirectory.resolve(fork ? "forked" : "in-process").toFile(); + Files.createDirectories(buildDirectory.toPath()); + + CompilerConfiguration configuration = new CompilerConfiguration(); + configuration.setFork(fork); + configuration.setWorkingDirectory(tempDirectory.toFile()); + configuration.setBuildDirectory(buildDirectory); + configuration.setOutputLocation(new File(buildDirectory, "classes").getAbsolutePath()); + configuration.addSourceLocation(sourceDirectory.toString()); + configuration.setSourceVersion("8"); + configuration.setTargetVersion("8"); + configuration.addCompilerCustomArgument("-Xlint:-options", null); + + return getCompiler().performCompile(configuration); + } }