From 252ca09f37cf5103d561772809b63233807b03fc Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Fri, 31 Jul 2026 17:27:30 +0200 Subject: [PATCH] Report structured BuilderProblems from compiler diagnostics Map javax.tools.Diagnostic to BuilderProblem and report via the new DiagnosticReporter service, so compiler warnings and errors appear with structured keys, source locations, and severity in the build report and mvnlog --diagnostics output. - Add @Inject DiagnosticReporter to AbstractCompilerMojo - Pass DiagnosticReporter to DiagnosticLogger constructor - Map each Diagnostic to BuilderProblem with key "compiler:", severity from Diagnostic.Kind, and source file/line/column - Use per-type key for dedup (e.g. "compiler:compiler.warn.unchecked" counts all unchecked warnings as one summary entry) - Bump mavenVersion to 4.1.0-SNAPSHOT for DiagnosticReporter API - Update test imports for maven-testing package relocation - Add no-op DiagnosticReporter provider in test configuration Co-Authored-By: Claude Opus 4.6 --- pom.xml | 2 +- .../plugin/compiler/AbstractCompilerMojo.java | 12 ++++ .../plugin/compiler/DiagnosticLogger.java | 68 ++++++++++++++++++- .../maven/plugin/compiler/ToolExecutor.java | 2 +- .../plugin/compiler/CompilerMojoTestCase.java | 25 ++++--- 5 files changed, 96 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 75b3e25f6..8927b8137 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ under the License. 17 - 4.0.0-rc-4 + 4.1.0-SNAPSHOT 9.10.1 7.0.0 diff --git a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java index cac92f902..521b861bd 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java @@ -73,6 +73,7 @@ import org.apache.maven.api.services.DependencyResolver; import org.apache.maven.api.services.DependencyResolverRequest; import org.apache.maven.api.services.DependencyResolverResult; +import org.apache.maven.api.services.DiagnosticReporter; import org.apache.maven.api.services.MavenException; import org.apache.maven.api.services.MessageBuilder; import org.apache.maven.api.services.MessageBuilderFactory; @@ -942,6 +943,17 @@ final void amendincrementalCompilation(EnumSet aspects, @Inject protected MessageBuilderFactory messageBuilderFactory; + /** + * Service for reporting structured build diagnostics to the build report. + * Compiler warnings and errors are reported through this service so they + * appear with structured keys, source locations, and suggestions in + * {@code mvnlog --diagnostics} and the JSON build report. + * + * @since 4.0.0-beta-5 + */ + @Inject + protected DiagnosticReporter diagnosticReporter; + /** * The logger for reporting information or warnings to the user. * Currently, this is also used for console output. diff --git a/src/main/java/org/apache/maven/plugin/compiler/DiagnosticLogger.java b/src/main/java/org/apache/maven/plugin/compiler/DiagnosticLogger.java index 9bce7a546..625f82206 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/DiagnosticLogger.java +++ b/src/main/java/org/apache/maven/plugin/compiler/DiagnosticLogger.java @@ -30,11 +30,14 @@ import java.util.Optional; import org.apache.maven.api.plugin.Log; +import org.apache.maven.api.services.BuilderProblem; +import org.apache.maven.api.services.DiagnosticReporter; import org.apache.maven.api.services.MessageBuilder; import org.apache.maven.api.services.MessageBuilderFactory; /** - * A Java compiler diagnostic listener which send the messages to the Maven logger. + * A Java compiler diagnostic listener which sends the messages to the Maven logger + * and reports structured {@link BuilderProblem}s to the {@link DiagnosticReporter}. * * @author Martin Desruisseaux */ @@ -49,6 +52,12 @@ final class DiagnosticLogger implements DiagnosticListener { */ private final MessageBuilderFactory messageBuilderFactory; + /** + * The service for reporting structured diagnostics to the build report. + * May be {@code null} if no reporter is available (e.g. Maven 4.0.x). + */ + private final DiagnosticReporter diagnosticReporter; + /** * The locale for compiler message. */ @@ -75,16 +84,24 @@ final class DiagnosticLogger implements DiagnosticListener { private String firstError; /** - * Creates a listener which will send the diagnostics to the given logger. + * Creates a listener which will send the diagnostics to the given logger + * and to the given diagnostic reporter. * * @param logger the logger where to send diagnostics * @param messageBuilderFactory the factory for creating message builders + * @param diagnosticReporter the reporter for structured build diagnostics, or {@code null} * @param locale the locale for compiler message * @param directory the base directory with which to relativize the paths to source files */ - DiagnosticLogger(Log logger, MessageBuilderFactory messageBuilderFactory, Locale locale, Path directory) { + DiagnosticLogger( + Log logger, + MessageBuilderFactory messageBuilderFactory, + DiagnosticReporter diagnosticReporter, + Locale locale, + Path directory) { this.logger = logger; this.messageBuilderFactory = messageBuilderFactory; + this.diagnosticReporter = diagnosticReporter; this.locale = locale; this.directory = directory; codeCount = new LinkedHashMap<>(); @@ -107,6 +124,17 @@ private String relativize(String file) { return file; } + /** + * Maps a {@link Diagnostic.Kind} to a {@link BuilderProblem.Severity}. + */ + private static BuilderProblem.Severity mapSeverity(Diagnostic.Kind kind) { + return switch (kind) { + case ERROR -> BuilderProblem.Severity.ERROR; + case WARNING, MANDATORY_WARNING -> BuilderProblem.Severity.WARNING; + default -> BuilderProblem.Severity.INFO; + }; + } + /** * Invoked when the compiler emitted a warning. * @@ -176,6 +204,40 @@ public void report(Diagnostic diagnostic) { if (code != null) { codeCount.merge(code, 1, (old, initial) -> old + 1); } + // Report structured diagnostic to the build report + reportToBuildReport(diagnostic, message, code); + } + + /** + * Reports a structured {@link BuilderProblem} to the {@link DiagnosticReporter}. + *

+ * Each diagnostic is reported with a per-type key ({@code "compiler:"}) + * so that the build report deduplicates by diagnostic kind. For example, 50 + * unchecked warnings produce a single entry with count=50 in the summary. + * Individual per-file details remain in the build log. + */ + private void reportToBuildReport(Diagnostic diagnostic, String message, String code) { + if (diagnosticReporter == null || code == null) { + return; + } + BuilderProblem.Builder builder = BuilderProblem.builder() + .severity(mapSeverity(diagnostic.getKind())) + .message(message) + .key("compiler:" + code); + // Attach source location from the first occurrence (collector deduplicates by key) + JavaFileObject sourceFile = diagnostic.getSource(); + if (sourceFile != null) { + builder.source(relativize(sourceFile.getName())); + long line = diagnostic.getLineNumber(); + if (line != Diagnostic.NOPOS) { + builder.lineNumber((int) line); + } + long column = diagnostic.getColumnNumber(); + if (column != Diagnostic.NOPOS) { + builder.columnNumber((int) column); + } + } + diagnosticReporter.report(builder.build()); } /** diff --git a/src/main/java/org/apache/maven/plugin/compiler/ToolExecutor.java b/src/main/java/org/apache/maven/plugin/compiler/ToolExecutor.java index 907d32390..bd8bb26b0 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/ToolExecutor.java +++ b/src/main/java/org/apache/maven/plugin/compiler/ToolExecutor.java @@ -225,7 +225,7 @@ protected ToolExecutor(final AbstractCompilerMojo mojo, DiagnosticListener {}; + } + @Provides @Singleton @SuppressWarnings("unused")