Deprecated reportFromPlugin way to report diagnostics from plugin

Originally reportFromPlugin method was introduced to address the problem
with loading of DefaultErrorMessages.Extension vis ServiceLoader.
For some cases this extension was not loaded by ServiceLoader because
classes was loaded via different class loader, common scenario here is
compiler plugins. Ideally we should load such extension point via
getService approach, but unfortunately to do that we need project and
DefaultErrorMessages.render is static method for now.
Also with reportFromPlugin approach is a problem -- all diagnostics
reported via this method has the same id: PLUGIN_[WARNING|ERROR|INFO]
and it isn't possible to suppress only one particular diagnostic.
To bypass this problem the new method
initializeFactoryNamesAndDefaultErrorMessages was introduced.
It basically store DiagnosticRenderer inside DiagnosticFactory.
It is not ideal, because one DiagnosticFactory could have different
renderers for different scenarios -- for compiler and for IDE, but
I think that it is better than reportByPlugin approach.
This commit is contained in:
Stanislav Erokhin
2020-02-07 22:18:25 +03:00
parent 84baa0b4c2
commit 453008e488
20 changed files with 104 additions and 106 deletions
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.diagnostics;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticRenderer;
import java.util.Arrays;
import java.util.Collection;
@@ -26,6 +28,8 @@ public abstract class DiagnosticFactory<D extends Diagnostic> {
private String name = null;
private final Severity severity;
private DiagnosticRenderer<D> defaultRenderer;
protected DiagnosticFactory(@NotNull Severity severity) {
this.severity = severity;
}
@@ -49,6 +53,15 @@ public abstract class DiagnosticFactory<D extends Diagnostic> {
return severity;
}
@Nullable
public DiagnosticRenderer<D> getDefaultRenderer() {
return defaultRenderer;
}
void setDefaultRenderer(@Nullable DiagnosticRenderer<D> defaultRenderer) {
this.defaultRenderer = defaultRenderer;
}
@NotNull
@SuppressWarnings("unchecked")
public D cast(@NotNull Diagnostic diagnostic) {
@@ -16,6 +16,9 @@ import org.jetbrains.kotlin.config.LanguageVersion;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.rendering.DeclarationWithDiagnosticComponents;
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages;
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap;
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticRenderer;
import org.jetbrains.kotlin.lexer.KtKeywordToken;
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken;
import org.jetbrains.kotlin.lexer.KtTokens;
@@ -1157,6 +1160,14 @@ public interface Errors {
}
public static void initializeFactoryNames(@NotNull Class<?> aClass) {
initializeFactoryNamesAndDefaultErrorMessages(aClass, DiagnosticFactoryToRendererMap::new);
}
public static void initializeFactoryNamesAndDefaultErrorMessages(
@NotNull Class<?> aClass,
@NotNull DefaultErrorMessages.Extension defaultErrorMessages
) {
DiagnosticFactoryToRendererMap diagnosticToRendererMap = defaultErrorMessages.getMap();
for (Field field : aClass.getFields()) {
if (Modifier.isStatic(field.getModifiers())) {
try {
@@ -1164,6 +1175,9 @@ public interface Errors {
if (value instanceof DiagnosticFactory) {
DiagnosticFactory<?> factory = (DiagnosticFactory<?>)value;
factory.setName(field.getName());
//noinspection rawtypes, unchecked
factory.setDefaultRenderer((DiagnosticRenderer) diagnosticToRendererMap.get(factory));
}
}
catch (IllegalAccessException e) {
@@ -1171,6 +1185,7 @@ public interface Errors {
}
}
}
}
private static final Initializer INSTANCE = new Initializer();
@@ -179,10 +179,17 @@ inline fun <reified T : KtDeclaration> reportOnDeclarationAs(
} ?: throw AssertionError("No declaration for $descriptor")
}
// this method should not be used in the project, but it is leaved for some time for compatibility with old compiler plugins
@Deprecated(
"Please register DefaultErrorMessages.Extension in moment of DiagnosticFactory initialization by calling " +
"initializeFactoryNamesAndDefaultErrorMessages method instead of initializeFactoryNames",
ReplaceWith("report(diagnostic)"),
level = DeprecationLevel.ERROR
)
fun <D : Diagnostic> DiagnosticSink.reportFromPlugin(diagnostic: D, ext: DefaultErrorMessages.Extension) {
@Suppress("UNCHECKED_CAST")
val renderer = ext.map[diagnostic.factory] as? DiagnosticRenderer<D>
?: error("Renderer not found for diagnostic ${diagnostic.factory.name}")
?: error("Renderer not found for diagnostic ${diagnostic.factory.name}")
val renderedDiagnostic = RenderedDiagnostic(diagnostic, renderer)
@@ -56,7 +56,11 @@ public class DefaultErrorMessages {
@Nullable
public static DiagnosticRenderer getRendererForDiagnostic(@NotNull Diagnostic diagnostic) {
return AddToStdlibKt.firstNotNullResult(RENDERER_MAPS, map -> map.get(diagnostic.getFactory()));
DiagnosticRenderer<?> renderer = AddToStdlibKt.firstNotNullResult(RENDERER_MAPS, map -> map.get(diagnostic.getFactory()));
if (renderer != null)
return renderer;
else
return diagnostic.getFactory().getDefaultRenderer();
}
static {