From 972def7cdc3841b4f8538ce0cfa505f0fcfeed68 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 25 Apr 2024 17:39:47 +0800 Subject: [PATCH] Report Javac compiler's problems to IMarkers & Map parts of Javac problems to ECJ ids --- .../eclipse/jdt/core/dom/JavacConverter.java | 30 +---- .../eclipse/jdt/core/dom/JavacProblem.java | 44 +++++++ .../jdt/core/dom/JavacProblemConverter.java | 113 ++++++++++++++++++ .../jdt/internal/javac/JavacCompiler.java | 46 ++++--- .../jdt/internal/javac/JavacFileObject.java | 33 +++++ 5 files changed, 218 insertions(+), 48 deletions(-) create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacProblem.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacProblemConverter.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacFileObject.java diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacConverter.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacConverter.java index 0c205d28d6a..18316841d3a 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacConverter.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacConverter.java @@ -20,7 +20,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.PriorityQueue; @@ -35,8 +34,6 @@ import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; import org.eclipse.jdt.core.dom.ModuleModifier.ModuleModifierKeyword; import org.eclipse.jdt.core.dom.PrimitiveType.Code; -import org.eclipse.jdt.internal.compiler.problem.DefaultProblem; -import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; import com.sun.source.tree.CaseTree.CaseKind; import com.sun.tools.javac.code.BoundKind; @@ -1962,32 +1959,7 @@ public org.eclipse.jdt.core.dom.Comment convert(Comment javac, int pos, int endP } static IProblem convertDiagnostic(Diagnostic javacDiagnostic) { - // TODO use a problem factory? Map code to category...? - return new DefaultProblem( - javacDiagnostic.getSource().getName().toCharArray(), - javacDiagnostic.getMessage(Locale.getDefault()), - toProblemId(javacDiagnostic.getCode()), // TODO probably use `getCode()` here - null, - switch (javacDiagnostic.getKind()) { - case ERROR -> ProblemSeverities.Error; - case WARNING, MANDATORY_WARNING -> ProblemSeverities.Warning; - case NOTE -> ProblemSeverities.Info; - default -> ProblemSeverities.Error; - }, - (int)Math.min(javacDiagnostic.getPosition(), javacDiagnostic.getStartPosition()), - (int)javacDiagnostic.getEndPosition(), - (int)javacDiagnostic.getLineNumber(), - (int)javacDiagnostic.getColumnNumber()); - } - - private static int toProblemId(String javacDiagnosticCode) { - // better use a Map if there is a 1->0..1 mapping - return switch (javacDiagnosticCode) { - case "compiler.warn.raw.class.use" -> IProblem.RawTypeReference; - // TODO complete mapping list; dig in https://github.com/openjdk/jdk/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties - // for an exhaustive (but polluted) list, unless a better source can be found (spec?) - default -> 0; - }; + return JavacProblemConverter.createJavacProblem(javacDiagnostic); } class FixPositions extends ASTVisitor { diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacProblem.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacProblem.java new file mode 100644 index 00000000000..f003ce5dfcf --- /dev/null +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacProblem.java @@ -0,0 +1,44 @@ +/******************************************************************************* +* Copyright (c) 2024 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.jdt.core.dom; + +import org.eclipse.jdt.internal.compiler.problem.DefaultProblem; + +public class JavacProblem extends DefaultProblem { + private String javacCode; + + public JavacProblem(char[] originatingFileName, String message, String javacCode, int jdtProblemId, String[] stringArguments, int severity, + int startPosition, int endPosition, int line, int column) { + super(originatingFileName, message, jdtProblemId, stringArguments, severity, startPosition, endPosition, line, column); + this.javacCode = javacCode; + } + + public String getJavacCode() { + return this.javacCode; + } + + public void setJavacCode(String javacCode) { + this.javacCode = javacCode; + } + + @Override + public String[] getExtraMarkerAttributeNames() { + return new String[] { "javacCode" }; + } + + @Override + public Object[] getExtraMarkerAttributeValues() { + return new Object[] { this.javacCode }; + } +} diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacProblemConverter.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacProblemConverter.java new file mode 100644 index 00000000000..fa5abead26b --- /dev/null +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacProblemConverter.java @@ -0,0 +1,113 @@ +/******************************************************************************* +* Copyright (c) 2024 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.jdt.core.dom; + +import java.util.Locale; + +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; + +import org.eclipse.jdt.core.compiler.IProblem; +import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; + +import com.sun.tools.javac.code.Kinds; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.util.JCDiagnostic; + +public class JavacProblemConverter { + public static JavacProblem createJavacProblem(Diagnostic diagnostic) { + return new JavacProblem( + diagnostic.getSource().getName().toCharArray(), + diagnostic.getMessage(Locale.getDefault()), + diagnostic.getCode(), + toProblemId(diagnostic), + new String[0], + switch (diagnostic.getKind()) { + case ERROR -> ProblemSeverities.Error; + case WARNING, MANDATORY_WARNING -> ProblemSeverities.Warning; + case NOTE -> ProblemSeverities.Info; + default -> ProblemSeverities.Error; + }, + (int) Math.min(diagnostic.getPosition(), diagnostic.getStartPosition()), + (int) (diagnostic.getEndPosition() - 1), + (int) diagnostic.getLineNumber(), + (int) diagnostic.getColumnNumber()); + } + + /** + * See the link below for Javac problem list: + * https://github.com/openjdk/jdk/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties + * + * And the examples to reproduce the Javac problems: + * https://github.com/openjdk/jdk/tree/master/test/langtools/tools/javac/diags/examples + */ + public static int toProblemId(Diagnostic javacDiagnostic) { + String javacDiagnosticCode = javacDiagnostic.getCode(); + // better use a Map if there is a 1->0..1 mapping + return switch (javacDiagnosticCode) { + case "compiler.err.expected" -> IProblem.ParsingErrorInsertTokenAfter; + case "compiler.warn.raw.class.use" -> IProblem.RawTypeReference; + case "compiler.err.cant.resolve.location" -> convertUnresolvedSymbol(javacDiagnostic); + case "compiler.err.cant.resolve.location.args" -> IProblem.UndefinedMethod; + case "compiler.err.cant.resolve" -> IProblem.UndefinedField; + case "compiler.err.cant.apply.symbols" -> IProblem.UndefinedConstructor; + case "compiler.err.premature.eof" -> IProblem.ParsingErrorUnexpectedEOF; // syntax error + case "compiler.err.report.access" -> convertNotVisibleAccess(javacDiagnostic); + // TODO complete mapping list; dig in https://github.com/openjdk/jdk/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties + // for an exhaustive (but polluted) list, unless a better source can be found (spec?) + default -> 0; + }; + } + + private static int convertUnresolvedSymbol(Diagnostic javacDiagnostic) { + if (javacDiagnostic instanceof JCDiagnostic jcDiagnostic) { + Object[] args = jcDiagnostic.getArgs(); + if (args != null) { + for (Object arg : args) { + if (arg instanceof Kinds.KindName kindName) { + return switch (kindName) { + case CLASS -> IProblem.UndefinedType; + case METHOD -> IProblem.UndefinedMethod; + case VAR -> IProblem.UnresolvedVariable; + default -> IProblem.UndefinedName; + }; + } + } + } + } + + return IProblem.UndefinedName; + } + + private static int convertNotVisibleAccess(Diagnostic javacDiagnostic) { + if (javacDiagnostic instanceof JCDiagnostic jcDiagnostic) { + Object[] args = jcDiagnostic.getArgs(); + if (args != null && args.length > 0) { + if (args[0] instanceof Symbol.MethodSymbol methodSymbol) { + if (methodSymbol.isConstructor()) { + return IProblem.NotVisibleConstructor; + } + + return IProblem.NotVisibleMethod; + } else if (args[0] instanceof Symbol.ClassSymbol) { + return IProblem.NotVisibleType; + } else if (args[0] instanceof Symbol.VarSymbol) { + return IProblem.NotVisibleField; + } + } + } + + return 0; + } +} diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java index 7650c377767..3969cf1dbbe 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java @@ -11,14 +11,22 @@ package org.eclipse.jdt.internal.javac; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.stream.Stream; +import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; import org.eclipse.core.resources.IResource; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.compiler.CompilerConfiguration; +import org.eclipse.jdt.core.compiler.IProblem; +import org.eclipse.jdt.core.dom.JavacProblem; +import org.eclipse.jdt.core.dom.JavacProblemConverter; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; @@ -26,13 +34,11 @@ import org.eclipse.jdt.internal.compiler.IProblemFactory; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; -import org.eclipse.jdt.internal.compiler.tool.EclipseFileObject; import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.builder.SourceFile; import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.List; public class JavacCompiler extends Compiler { CompilerConfiguration compilerConfig; @@ -46,16 +52,18 @@ public JavacCompiler(INameEnvironment environment, IErrorHandlingPolicy policy, @Override public void compile(ICompilationUnit[] sourceUnits) { Context javacContext = new Context(); -// javacContext.put(DiagnosticListener.class, diagnostic -> { -// this.problemReporter. -// if (Objects.equals(diagnostic.getSource(), fileObject) || -// diagnostic.getSource() instanceof DiagnosticSource source && Objects.equals(source.getFile(), fileObject)) { -// IProblem[] previous = res.getProblems(); -// IProblem[] newProblems = Arrays.copyOf(previous, previous.length + 1); -// newProblems[newProblems.length - 1] = JavacConverter.convertDiagnostic(diagnostic); -// res.setProblems(newProblems); -// } -// }); + Map> javacProblems = new HashMap<>(); + javacContext.put(DiagnosticListener.class, diagnostic -> { + if (diagnostic.getSource() instanceof JavacFileObject fileObject) { + JavacProblem javacProblem = JavacProblemConverter.createJavacProblem(diagnostic); + List previous = javacProblems.get(fileObject.getOriginalUnit()); + if (previous == null) { + previous = new ArrayList<>(); + javacProblems.put(fileObject.getOriginalUnit(), previous); + } + previous.add(javacProblem); + } + }); JavacUtils.configureJavacContext(javacContext, this.compilerConfig, Stream.of(sourceUnits) .filter(SourceFile.class::isInstance) .map(SourceFile.class::cast) @@ -65,14 +73,12 @@ public void compile(ICompilationUnit[] sourceUnits) { .map(JavaCore::create) .findFirst() .orElse(null)); - // TODO map context DiagnosticHandler to IProblemFactory to create markers JavaCompiler javac = JavaCompiler.instance(javacContext); try { - javac.compile(List.from(Stream.of(sourceUnits) + javac.compile(com.sun.tools.javac.util.List.from(Stream.of(sourceUnits) .filter(SourceFile.class::isInstance) .map(SourceFile.class::cast) - .map(source -> source.resource.getLocationURI()) - .map(uri -> new EclipseFileObject(null, uri, Kind.SOURCE, Charset.defaultCharset())) + .map(source -> new JavacFileObject(source, null, source.resource.getLocationURI(), Kind.SOURCE, Charset.defaultCharset())) .map(JavaFileObject.class::cast) .toList())); } catch (Throwable e) { @@ -81,10 +87,12 @@ public void compile(ICompilationUnit[] sourceUnits) { for (int i = 0; i < sourceUnits.length; i++) { ICompilationUnit in = sourceUnits[i]; CompilationResult result = new CompilationResult(in, i, sourceUnits.length, Integer.MAX_VALUE); + if (javacProblems.containsKey(in)) { + JavacProblem[] problems = javacProblems.get(in).toArray(new JavacProblem[0]); + result.problems = problems; // JavaBuilder is responsible for converting the problems to IMarkers + result.problemCount = problems.length; + } this.requestor.acceptResult(result); } - } - - } diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacFileObject.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacFileObject.java new file mode 100644 index 00000000000..4b77f225893 --- /dev/null +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacFileObject.java @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright (c) 2024 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.jdt.internal.javac; + +import java.net.URI; +import java.nio.charset.Charset; + +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.compiler.tool.EclipseFileObject; + +public class JavacFileObject extends EclipseFileObject { + private ICompilationUnit originalUnit; + + public JavacFileObject(ICompilationUnit originalUnit, String className, URI uri, Kind kind, Charset charset) { + super(className, uri, kind, charset); + this.originalUnit = originalUnit; + } + + public ICompilationUnit getOriginalUnit() { + return this.originalUnit; + } +}