[FE 1.0] Introduce new kind of diagnostic factories for deprecation warnings/errors

This commit is contained in:
Dmitriy Novozhilov
2021-07-29 18:42:13 +03:00
parent ff9643b70e
commit 7c73840e4a
6 changed files with 226 additions and 14 deletions
@@ -0,0 +1,145 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
sealed class DiagnosticFactoryForDeprecation<E : PsiElement, D : Diagnostic, F : DiagnosticFactoryWithPsiElement<E, D>>(
val deprecatingFeature: LanguageFeature,
val warningFactory: F,
val errorFactory: F
) {
fun LanguageVersionSettings.chooseFactory(): F {
return if (supportsFeature(deprecatingFeature)) errorFactory else warningFactory
}
}
class DiagnosticFactoryForDeprecation0<E : PsiElement>(
featureForError: LanguageFeature,
warningFactory: DiagnosticFactory0<E>,
errorFactory: DiagnosticFactory0<E>
) : DiagnosticFactoryForDeprecation<E, SimpleDiagnostic<E>, DiagnosticFactory0<E>>(featureForError, warningFactory, errorFactory) {
companion object {
@JvmStatic
@JvmOverloads
fun <E : PsiElement> create(
featureForError: LanguageFeature,
positioningStrategy: PositioningStrategy<E> = PositioningStrategies.DEFAULT
): DiagnosticFactoryForDeprecation0<E> {
return DiagnosticFactoryForDeprecation0(
featureForError,
warningFactory = DiagnosticFactory0.create(Severity.WARNING, positioningStrategy),
errorFactory = DiagnosticFactory0.create(Severity.ERROR, positioningStrategy),
)
}
}
fun on(languageVersionSettings: LanguageVersionSettings, element: E): SimpleDiagnostic<E> {
return languageVersionSettings.chooseFactory().on(element)
}
}
class DiagnosticFactoryForDeprecation1<E : PsiElement, A : Any>(
featureForError: LanguageFeature,
warningFactory: DiagnosticFactory1<E, A>,
errorFactory: DiagnosticFactory1<E, A>
) : DiagnosticFactoryForDeprecation<E, DiagnosticWithParameters1<E, A>, DiagnosticFactory1<E, A>>(featureForError, warningFactory, errorFactory) {
companion object {
@JvmStatic
@JvmOverloads
fun <E : PsiElement, A : Any> create(
featureForError: LanguageFeature,
positioningStrategy: PositioningStrategy<E> = PositioningStrategies.DEFAULT
): DiagnosticFactoryForDeprecation1<E, A> {
return DiagnosticFactoryForDeprecation1(
featureForError,
warningFactory = DiagnosticFactory1.create(Severity.WARNING, positioningStrategy),
errorFactory = DiagnosticFactory1.create(Severity.ERROR, positioningStrategy),
)
}
}
fun on(languageVersionSettings: LanguageVersionSettings, element: E, a: A): ParametrizedDiagnostic<E> {
return languageVersionSettings.chooseFactory().on(element, a)
}
}
class DiagnosticFactoryForDeprecation2<E : PsiElement, A : Any, B : Any>(
featureForError: LanguageFeature,
warningFactory: DiagnosticFactory2<E, A, B>,
errorFactory: DiagnosticFactory2<E, A, B>
) : DiagnosticFactoryForDeprecation<E, DiagnosticWithParameters2<E, A, B>, DiagnosticFactory2<E, A, B>>(featureForError, warningFactory, errorFactory) {
companion object {
@JvmStatic
@JvmOverloads
fun <E : PsiElement, A : Any, B : Any> create(
featureForError: LanguageFeature,
positioningStrategy: PositioningStrategy<E> = PositioningStrategies.DEFAULT
): DiagnosticFactoryForDeprecation2<E, A, B> {
return DiagnosticFactoryForDeprecation2(
featureForError,
warningFactory = DiagnosticFactory2.create(Severity.WARNING, positioningStrategy),
errorFactory = DiagnosticFactory2.create(Severity.ERROR, positioningStrategy),
)
}
}
fun on(languageVersionSettings: LanguageVersionSettings, element: E, a: A, b: B): ParametrizedDiagnostic<E> {
return languageVersionSettings.chooseFactory().on(element, a, b)
}
}
class DiagnosticFactoryForDeprecation3<E : PsiElement, A : Any, B : Any, C : Any>(
featureForError: LanguageFeature,
warningFactory: DiagnosticFactory3<E, A, B, C>,
errorFactory: DiagnosticFactory3<E, A, B, C>
) : DiagnosticFactoryForDeprecation<E, DiagnosticWithParameters3<E, A, B, C>, DiagnosticFactory3<E, A, B, C>>(featureForError, warningFactory, errorFactory) {
companion object {
@JvmStatic
@JvmOverloads
fun <E : PsiElement, A : Any, B : Any, C : Any> create(
featureForError: LanguageFeature,
positioningStrategy: PositioningStrategy<E> = PositioningStrategies.DEFAULT
): DiagnosticFactoryForDeprecation3<E, A, B, C> {
return DiagnosticFactoryForDeprecation3(
featureForError,
warningFactory = DiagnosticFactory3.create(Severity.WARNING, positioningStrategy),
errorFactory = DiagnosticFactory3.create(Severity.ERROR, positioningStrategy),
)
}
}
fun on(languageVersionSettings: LanguageVersionSettings, element: E, a: A, b: B, c: C): ParametrizedDiagnostic<E> {
return languageVersionSettings.chooseFactory().on(element, a, b, c)
}
}
class DiagnosticFactoryForDeprecation4<E : PsiElement, A : Any, B : Any, C : Any, D : Any>(
featureForError: LanguageFeature,
warningFactory: DiagnosticFactory4<E, A, B, C, D>,
errorFactory: DiagnosticFactory4<E, A, B, C, D>
) : DiagnosticFactoryForDeprecation<E, DiagnosticWithParameters4<E, A, B, C, D>, DiagnosticFactory4<E, A, B, C, D>>(featureForError, warningFactory, errorFactory) {
companion object {
@JvmStatic
@JvmOverloads
fun <E : PsiElement, A : Any, B : Any, C : Any, D : Any> create(
featureForError: LanguageFeature,
positioningStrategy: PositioningStrategy<E> = PositioningStrategies.DEFAULT
): DiagnosticFactoryForDeprecation4<E, A, B, C, D> {
return DiagnosticFactoryForDeprecation4(
featureForError,
warningFactory = DiagnosticFactory4.create(Severity.WARNING, positioningStrategy),
errorFactory = DiagnosticFactory4.create(Severity.ERROR, positioningStrategy),
)
}
}
fun on(languageVersionSettings: LanguageVersionSettings, element: E, a: A, b: B, c: C, d: D): ParametrizedDiagnostic<E> {
return languageVersionSettings.chooseFactory().on(element, a, b, c, d)
}
}
@@ -824,8 +824,8 @@ public interface Errors {
DiagnosticFactory1<PsiElement, InferenceErrorData> TYPE_INFERENCE_CANNOT_CAPTURE_TYPES = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, InferenceErrorData> TYPE_INFERENCE_PARAMETER_CONSTRAINT_ERROR = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> TYPE_INFERENCE_INCORPORATION_ERROR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, TypeParameterDescriptor> TYPE_INFERENCE_ONLY_INPUT_TYPES = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, TypeParameterDescriptor> TYPE_INFERENCE_ONLY_INPUT_TYPES_WARNING = DiagnosticFactory1.create(WARNING);
DiagnosticFactoryForDeprecation1<PsiElement, TypeParameterDescriptor> TYPE_INFERENCE_ONLY_INPUT_TYPES = DiagnosticFactoryForDeprecation1.create(LanguageFeature.StrictOnlyInputTypesChecks);
DiagnosticFactory1<PsiElement, InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<KtElement, KotlinType, KotlinType> TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR);
DiagnosticFactory0<PsiElement> TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG = DiagnosticFactory0.create(WARNING);
@@ -1218,6 +1218,9 @@ public interface Errors {
Initializer __initializer = Initializer.INSTANCE;
class Initializer {
private static final String WARNING = "_WARNING";
private static final String ERROR = "_ERROR";
static {
initializeFactoryNames(Errors.class);
}
@@ -1226,7 +1229,6 @@ public interface Errors {
initializeFactoryNamesAndDefaultErrorMessages(aClass, DiagnosticFactoryToRendererMap::new);
}
@SuppressWarnings({"unchecked", "rawtypes"})
public static void initializeFactoryNamesAndDefaultErrorMessages(
@NotNull Class<?> aClass,
@NotNull DefaultErrorMessages.Extension defaultErrorMessages
@@ -1237,10 +1239,13 @@ public interface Errors {
try {
Object value = field.get(null);
if (value instanceof DiagnosticFactory) {
DiagnosticFactory<?> factory = (DiagnosticFactory<?>)value;
factory.initializeName(field.getName());
factory.setDefaultRenderer((DiagnosticRenderer) diagnosticToRendererMap.get(factory));
initializeNameAndRenderer(diagnosticToRendererMap, field.getName(), (DiagnosticFactory<?>) value);
}
if (value instanceof DiagnosticFactoryForDeprecation) {
String errorName = field.getName();
DiagnosticFactoryForDeprecation<?, ?, ?> factory = (DiagnosticFactoryForDeprecation<?, ?, ?>) value;
initializeNameAndRenderer(diagnosticToRendererMap, field.getName() + ERROR, factory.getErrorFactory());
initializeNameAndRenderer(diagnosticToRendererMap, field.getName() + WARNING, factory.getWarningFactory());
}
}
catch (IllegalAccessException e) {
@@ -1248,7 +1253,11 @@ public interface Errors {
}
}
}
}
private static void initializeNameAndRenderer(DiagnosticFactoryToRendererMap diagnosticToRendererMap, String name, DiagnosticFactory<?> factory) {
factory.initializeName(name);
factory.setDefaultRenderer((DiagnosticRenderer) diagnosticToRendererMap.get(factory));
}
private static final Initializer INSTANCE = new Initializer();
@@ -929,8 +929,6 @@ public class DefaultErrorMessages {
MAP.put(TYPE_INFERENCE_INCORPORATION_ERROR, "Type inference failed. Please try to specify type arguments explicitly.");
MAP.put(TYPE_INFERENCE_ONLY_INPUT_TYPES, "Type inference failed. The value of the type parameter {0} should be mentioned in input types " +
"(argument types, receiver type or expected type). Try to specify it explicitly.", NAME);
MAP.put(TYPE_INFERENCE_ONLY_INPUT_TYPES_WARNING, "Type inference failed. The value of the type parameter {0} should be mentioned in input types " +
"(argument types, receiver type or expected type). Try to specify it explicitly.", NAME);
MAP.put(TYPE_INFERENCE_UPPER_BOUND_VIOLATED, "{0}", TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER);
MAP.put(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH, "Type inference failed. Expected type mismatch: inferred type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE);
MAP.put(TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG, "Please use spread operator to pass an array as vararg. It will be an error in 1.5.");
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.diagnostics.rendering;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.config.LanguageVersion;
import org.jetbrains.kotlin.diagnostics.*;
import java.util.HashMap;
@@ -94,6 +95,66 @@ public final class DiagnosticFactoryToRendererMap {
map.put(factory, renderer);
}
public <E extends PsiElement> void put(@NotNull DiagnosticFactoryForDeprecation0<E> factory, @NotNull String message) {
checkMutability();
map.put(factory.getErrorFactory(), new SimpleDiagnosticRenderer(message));
map.put(factory.getWarningFactory(), new SimpleDiagnosticRenderer(deprecationMessage(factory, message)));
}
public <E extends PsiElement, A> void put(@NotNull DiagnosticFactoryForDeprecation1<E, A> factory, @NotNull String message, @Nullable DiagnosticParameterRenderer<? super A> rendererA) {
checkMutability();
map.put(factory.getErrorFactory(), new DiagnosticWithParameters1Renderer<A>(message, rendererA));
map.put(factory.getWarningFactory(), new DiagnosticWithParameters1Renderer<A>(deprecationMessage(factory, message), rendererA));
}
public <E extends PsiElement, A> void put(@NotNull DiagnosticFactoryForDeprecation1<E, A> factory, @NotNull String message, @NotNull MultiRenderer<? super A> rendererA) {
checkMutability();
map.put(factory.getErrorFactory(), new DiagnosticWithParametersMultiRenderer<A>(message, rendererA));
map.put(factory.getWarningFactory(), new DiagnosticWithParametersMultiRenderer<A>(deprecationMessage(factory, message), rendererA));
}
public <E extends PsiElement, A, B> void put(@NotNull DiagnosticFactoryForDeprecation2<E, A, B> factory,
@NotNull String message,
@Nullable DiagnosticParameterRenderer<? super A> rendererA,
@Nullable DiagnosticParameterRenderer<? super B> rendererB) {
checkMutability();
map.put(factory.getErrorFactory(), new DiagnosticWithParameters2Renderer<A, B>(message, rendererA, rendererB));
map.put(factory.getWarningFactory(), new DiagnosticWithParameters2Renderer<A, B>(deprecationMessage(factory, message), rendererA, rendererB));
}
public <E extends PsiElement, A, B, C> void put(@NotNull DiagnosticFactoryForDeprecation3<E, A, B, C> factory,
@NotNull String message,
@Nullable DiagnosticParameterRenderer<? super A> rendererA,
@Nullable DiagnosticParameterRenderer<? super B> rendererB,
@Nullable DiagnosticParameterRenderer<? super C> rendererC) {
checkMutability();
map.put(factory.getErrorFactory(), new DiagnosticWithParameters3Renderer<A, B, C>(message, rendererA, rendererB, rendererC));
map.put(factory.getWarningFactory(), new DiagnosticWithParameters3Renderer<A, B, C>(deprecationMessage(factory, message), rendererA, rendererB, rendererC));
}
public <E extends PsiElement, A, B, C, D> void put(@NotNull DiagnosticFactoryForDeprecation4<E, A, B, C, D> factory,
@NotNull String message,
@Nullable DiagnosticParameterRenderer<? super A> rendererA,
@Nullable DiagnosticParameterRenderer<? super B> rendererB,
@Nullable DiagnosticParameterRenderer<? super C> rendererC,
@Nullable DiagnosticParameterRenderer<? super D> rendererD) {
checkMutability();
map.put(factory.getErrorFactory(), new DiagnosticWithParameters4Renderer<A, B, C, D>(message, rendererA, rendererB, rendererC, rendererD));
map.put(factory.getWarningFactory(), new DiagnosticWithParameters4Renderer<A, B, C, D>(deprecationMessage(factory, message), rendererA, rendererB, rendererC, rendererD));
}
private static String deprecationMessage(DiagnosticFactoryForDeprecation<?, ?, ?> factory, String errorMessage) {
LanguageVersion sinceVersion = factory.getDeprecatingFeature().getSinceVersion();
StringBuilder builder = new StringBuilder();
builder.append(errorMessage).append(". This will become an error");
if (sinceVersion != null) {
builder.append(" in Kotlin ").append(sinceVersion.getVersionString());
} else {
builder.append(" in a future release");
}
return builder.toString();
}
@Nullable
public DiagnosticRenderer<?> get(@NotNull DiagnosticFactory<?> factory) {
return map.get(factory);
@@ -506,10 +506,9 @@ class DiagnosticReporterByTrackingStrategy(
OnlyInputTypesDiagnostic::class.java -> {
val typeVariable = (error as OnlyInputTypesDiagnostic).typeVariable as? TypeVariableFromCallableDescriptor ?: return
psiKotlinCall.psiCall.calleeExpression?.let {
val factory = if (context.languageVersionSettings.supportsFeature(LanguageFeature.StrictOnlyInputTypesChecks))
TYPE_INFERENCE_ONLY_INPUT_TYPES
else TYPE_INFERENCE_ONLY_INPUT_TYPES_WARNING
trace.report(factory.on(it, typeVariable.originalTypeParameter))
trace.report(
TYPE_INFERENCE_ONLY_INPUT_TYPES.on(context.languageVersionSettings, it, typeVariable.originalTypeParameter)
)
}
}
}
@@ -271,7 +271,7 @@ public abstract class AbstractTracingStrategy implements TracingStrategy {
}
else if (status.hasTypeParameterWithUnsatisfiedOnlyInputTypesError()) {
//todo
return TYPE_INFERENCE_ONLY_INPUT_TYPES.on(reference, data.descriptor.getTypeParameters().get(0));
return TYPE_INFERENCE_ONLY_INPUT_TYPES.getErrorFactory().on(reference, data.descriptor.getTypeParameters().get(0));
}
else {
assert status.hasUnknownParameters();