Use ServiceLoader instead of IntelliJ extensions for DefaultErrorMessages

#KT-10473 Fixed
This commit is contained in:
Alexander Udalov
2017-10-20 20:01:19 +02:00
parent 1c6dce3674
commit 1a8be635b9
24 changed files with 128 additions and 109 deletions
@@ -0,0 +1,3 @@
# Note that this file is also present in idea/src/META-INF/services
org.jetbrains.kotlin.resolve.jvm.diagnostics.DefaultErrorMessagesJvm
org.jetbrains.kotlin.js.resolve.diagnostics.DefaultErrorMessagesJs
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersion;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
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;
@@ -982,9 +983,9 @@ public interface Errors {
DiagnosticFactory1<PsiElement, CallableDescriptor> ILLEGAL_SUSPEND_FUNCTION_CALL = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL = DiagnosticFactory0.create(ERROR);
DiagnosticFactory2<PsiElement, String, String> PLUGIN_ERROR = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, String, String> PLUGIN_WARNING = DiagnosticFactory2.create(WARNING);
DiagnosticFactory2<PsiElement, String, String> PLUGIN_INFO = DiagnosticFactory2.create(INFO);
DiagnosticFactory1<PsiElement, RenderedDiagnostic<?>> PLUGIN_ERROR = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, RenderedDiagnostic<?>> PLUGIN_WARNING = DiagnosticFactory1.create(WARNING);
DiagnosticFactory1<PsiElement, RenderedDiagnostic<?>> PLUGIN_INFO = DiagnosticFactory1.create(INFO);
// Function contracts
DiagnosticFactory1<KtElement, String> ERROR_IN_CONTRACT_DESCRIPTION = DiagnosticFactory1.create(ERROR);
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.diagnostics
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticRenderer
class RenderedDiagnostic<D : Diagnostic>(
val diagnostic: D,
val renderer: DiagnosticRenderer<D>
) {
val text = renderer.render(diagnostic)
val factory: DiagnosticFactory<*> get() = diagnostic.factory
override fun toString() = text
}
@@ -168,11 +168,11 @@ fun <D : Diagnostic> DiagnosticSink.reportFromPlugin(diagnostic: D, ext: Default
val renderer = ext.map[diagnostic.factory] as? DiagnosticRenderer<D>
?: error("Renderer not found for diagnostic ${diagnostic.factory.name}")
val text = renderer.render(diagnostic)
val renderedDiagnostic = RenderedDiagnostic(diagnostic, renderer)
when (diagnostic.severity) {
Severity.ERROR -> report(Errors.PLUGIN_ERROR.on(diagnostic.psiElement, diagnostic.factory.name, text))
Severity.WARNING -> report(Errors.PLUGIN_WARNING.on(diagnostic.psiElement, diagnostic.factory.name, text))
Severity.INFO -> report(Errors.PLUGIN_INFO.on(diagnostic.psiElement, diagnostic.factory.name, text))
Severity.ERROR -> report(Errors.PLUGIN_ERROR.on(diagnostic.psiElement, renderedDiagnostic))
Severity.WARNING -> report(Errors.PLUGIN_WARNING.on(diagnostic.psiElement, renderedDiagnostic))
Severity.INFO -> report(Errors.PLUGIN_INFO.on(diagnostic.psiElement, renderedDiagnostic))
}
}
@@ -16,26 +16,27 @@
package org.jetbrains.kotlin.diagnostics.rendering;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.util.io.FileUtil;
import kotlin.Pair;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.kotlin.config.LanguageVersion;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.diagnostics.RenderedDiagnostic;
import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.VersionRequirement;
import org.jetbrains.kotlin.types.KotlinTypeKt;
import org.jetbrains.kotlin.util.MappedExtensionProvider;
import org.jetbrains.kotlin.util.OperatorNameConventions;
import org.jetbrains.kotlin.utils.addToStdlib.AddToStdlibKt;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ServiceLoader;
import static org.jetbrains.kotlin.diagnostics.Errors.*;
import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.*;
@@ -44,47 +45,30 @@ import static org.jetbrains.kotlin.diagnostics.rendering.RenderingContext.of;
public class DefaultErrorMessages {
public interface Extension {
ExtensionPointName<Extension> EP_NAME = ExtensionPointName.create("org.jetbrains.kotlin.defaultErrorMessages");
@NotNull
DiagnosticFactoryToRendererMap getMap();
}
private static final DiagnosticFactoryToRendererMap MAP = new DiagnosticFactoryToRendererMap("Default");
private static final MappedExtensionProvider<Extension, List<DiagnosticFactoryToRendererMap>> RENDERER_MAPS = MappedExtensionProvider.create(
Extension.EP_NAME,
extensions -> {
List<DiagnosticFactoryToRendererMap> result = new ArrayList<>(extensions.size() + 1);
for (Extension extension : extensions) {
result.add(extension.getMap());
}
result.add(MAP);
return result;
});
private static final List<DiagnosticFactoryToRendererMap> RENDERER_MAPS =
CollectionsKt.plus(
Collections.singletonList(MAP),
CollectionsKt.map(ServiceLoader.load(Extension.class, DefaultErrorMessages.class.getClassLoader()), Extension::getMap)
);
@NotNull
@SuppressWarnings("unchecked")
public static String render(@NotNull Diagnostic diagnostic) {
for (DiagnosticFactoryToRendererMap map : RENDERER_MAPS.get()) {
DiagnosticRenderer renderer = map.get(diagnostic.getFactory());
if (renderer != null) {
//noinspection unchecked
return renderer.render(diagnostic);
}
DiagnosticRenderer renderer = getRendererForDiagnostic(diagnostic);
if (renderer != null) {
return renderer.render(diagnostic);
}
throw new IllegalArgumentException("Don't know how to render diagnostic of type " + diagnostic.getFactory().getName() +
" with the following renderer maps: " + RENDERER_MAPS.get());
return diagnostic.toString() + " (error: could not render message)";
}
@TestOnly
@Nullable
public static DiagnosticRenderer getRendererForDiagnostic(@NotNull Diagnostic diagnostic) {
for (DiagnosticFactoryToRendererMap map : RENDERER_MAPS.get()) {
DiagnosticRenderer renderer = map.get(diagnostic.getFactory());
if (renderer != null) return renderer;
}
return null;
return AddToStdlibKt.firstNotNullResult(RENDERER_MAPS, map -> map.get(diagnostic.getFactory()));
}
static {
@@ -888,9 +872,9 @@ public class DefaultErrorMessages {
MAP.put(ILLEGAL_SUSPEND_FUNCTION_CALL, "Suspend function ''{0}'' should be called only from a coroutine or another suspend function", NAME);
MAP.put(ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL, "Restricted suspending functions can only invoke member or extension suspending functions on their restricted coroutine scope");
MAP.put(PLUGIN_ERROR, "{0}: {1}", TO_STRING, TO_STRING);
MAP.put(PLUGIN_WARNING, "{0}: {1}", TO_STRING, TO_STRING);
MAP.put(PLUGIN_INFO, "{0}: {1}", TO_STRING, TO_STRING);
MAP.put(PLUGIN_ERROR, "{0}", (d, c) -> d.getText());
MAP.put(PLUGIN_WARNING, "{0}", (d, c) -> d.getText());
MAP.put(PLUGIN_INFO, "{0}", (d, c) -> d.getText());
MAP.put(ERROR_IN_CONTRACT_DESCRIPTION, "Error in contract description: {0}", TO_STRING);
MAP.put(CONTRACT_NOT_ALLOWED, "Contract is not allowed here");
@@ -46,15 +46,10 @@ protected constructor(
cached = WeakReference(newVal)
return newVal.second
}
companion object {
@JvmStatic fun <T, R> create(epName: ExtensionPointName<T>, map: (List<T>) -> R): MappedExtensionProvider<T, R>
= MappedExtensionProvider(epName, map)
}
}
class ExtensionProvider<T>(epName: ExtensionPointName<T>) : MappedExtensionProvider<T, List<T>>(epName, { it }) {
companion object {
@JvmStatic fun <T> create(epName: ExtensionPointName<T>): ExtensionProvider<T> = ExtensionProvider(epName)
}
}
}