From 7c73840e4a90393141f031a9a169e5cc376f50bb Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 29 Jul 2021 18:42:13 +0300 Subject: [PATCH] [FE 1.0] Introduce new kind of diagnostic factories for deprecation warnings/errors --- .../DiagnosticFactoryForDeprecation.kt | 145 ++++++++++++++++++ .../jetbrains/kotlin/diagnostics/Errors.java | 23 ++- .../rendering/DefaultErrorMessages.java | 2 - .../DiagnosticFactoryToRendererMap.java | 61 ++++++++ .../DiagnosticReporterByTrackingStrategy.kt | 7 +- .../calls/tasks/AbstractTracingStrategy.java | 2 +- 6 files changed, 226 insertions(+), 14 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticFactoryForDeprecation.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticFactoryForDeprecation.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticFactoryForDeprecation.kt new file mode 100644 index 00000000000..b1d3c7afb49 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticFactoryForDeprecation.kt @@ -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>( + val deprecatingFeature: LanguageFeature, + val warningFactory: F, + val errorFactory: F +) { + fun LanguageVersionSettings.chooseFactory(): F { + return if (supportsFeature(deprecatingFeature)) errorFactory else warningFactory + } +} + +class DiagnosticFactoryForDeprecation0( + featureForError: LanguageFeature, + warningFactory: DiagnosticFactory0, + errorFactory: DiagnosticFactory0 +) : DiagnosticFactoryForDeprecation, DiagnosticFactory0>(featureForError, warningFactory, errorFactory) { + companion object { + @JvmStatic + @JvmOverloads + fun create( + featureForError: LanguageFeature, + positioningStrategy: PositioningStrategy = PositioningStrategies.DEFAULT + ): DiagnosticFactoryForDeprecation0 { + return DiagnosticFactoryForDeprecation0( + featureForError, + warningFactory = DiagnosticFactory0.create(Severity.WARNING, positioningStrategy), + errorFactory = DiagnosticFactory0.create(Severity.ERROR, positioningStrategy), + ) + } + } + + fun on(languageVersionSettings: LanguageVersionSettings, element: E): SimpleDiagnostic { + return languageVersionSettings.chooseFactory().on(element) + } +} + +class DiagnosticFactoryForDeprecation1( + featureForError: LanguageFeature, + warningFactory: DiagnosticFactory1, + errorFactory: DiagnosticFactory1 +) : DiagnosticFactoryForDeprecation, DiagnosticFactory1>(featureForError, warningFactory, errorFactory) { + companion object { + @JvmStatic + @JvmOverloads + fun create( + featureForError: LanguageFeature, + positioningStrategy: PositioningStrategy = PositioningStrategies.DEFAULT + ): DiagnosticFactoryForDeprecation1 { + return DiagnosticFactoryForDeprecation1( + featureForError, + warningFactory = DiagnosticFactory1.create(Severity.WARNING, positioningStrategy), + errorFactory = DiagnosticFactory1.create(Severity.ERROR, positioningStrategy), + ) + } + } + + fun on(languageVersionSettings: LanguageVersionSettings, element: E, a: A): ParametrizedDiagnostic { + return languageVersionSettings.chooseFactory().on(element, a) + } +} + +class DiagnosticFactoryForDeprecation2( + featureForError: LanguageFeature, + warningFactory: DiagnosticFactory2, + errorFactory: DiagnosticFactory2 +) : DiagnosticFactoryForDeprecation, DiagnosticFactory2>(featureForError, warningFactory, errorFactory) { + companion object { + @JvmStatic + @JvmOverloads + fun create( + featureForError: LanguageFeature, + positioningStrategy: PositioningStrategy = PositioningStrategies.DEFAULT + ): DiagnosticFactoryForDeprecation2 { + 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 { + return languageVersionSettings.chooseFactory().on(element, a, b) + } +} + +class DiagnosticFactoryForDeprecation3( + featureForError: LanguageFeature, + warningFactory: DiagnosticFactory3, + errorFactory: DiagnosticFactory3 +) : DiagnosticFactoryForDeprecation, DiagnosticFactory3>(featureForError, warningFactory, errorFactory) { + companion object { + @JvmStatic + @JvmOverloads + fun create( + featureForError: LanguageFeature, + positioningStrategy: PositioningStrategy = PositioningStrategies.DEFAULT + ): DiagnosticFactoryForDeprecation3 { + 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 { + return languageVersionSettings.chooseFactory().on(element, a, b, c) + } +} + +class DiagnosticFactoryForDeprecation4( + featureForError: LanguageFeature, + warningFactory: DiagnosticFactory4, + errorFactory: DiagnosticFactory4 +) : DiagnosticFactoryForDeprecation, DiagnosticFactory4>(featureForError, warningFactory, errorFactory) { + companion object { + @JvmStatic + @JvmOverloads + fun create( + featureForError: LanguageFeature, + positioningStrategy: PositioningStrategy = PositioningStrategies.DEFAULT + ): DiagnosticFactoryForDeprecation4 { + 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 { + return languageVersionSettings.chooseFactory().on(element, a, b, c, d) + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index f88023b82c9..d0b731f4725 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -824,8 +824,8 @@ public interface Errors { DiagnosticFactory1 TYPE_INFERENCE_CANNOT_CAPTURE_TYPES = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 TYPE_INFERENCE_PARAMETER_CONSTRAINT_ERROR = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 TYPE_INFERENCE_INCORPORATION_ERROR = DiagnosticFactory0.create(ERROR); - DiagnosticFactory1 TYPE_INFERENCE_ONLY_INPUT_TYPES = DiagnosticFactory1.create(ERROR); - DiagnosticFactory1 TYPE_INFERENCE_ONLY_INPUT_TYPES_WARNING = DiagnosticFactory1.create(WARNING); + + DiagnosticFactoryForDeprecation1 TYPE_INFERENCE_ONLY_INPUT_TYPES = DiagnosticFactoryForDeprecation1.create(LanguageFeature.StrictOnlyInputTypesChecks); DiagnosticFactory1 TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR); DiagnosticFactory0 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(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 6c24ae624a3..bcf4dc8b728 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -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."); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticFactoryToRendererMap.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticFactoryToRendererMap.java index cd054f58042..b364a447edb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticFactoryToRendererMap.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticFactoryToRendererMap.java @@ -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 void put(@NotNull DiagnosticFactoryForDeprecation0 factory, @NotNull String message) { + checkMutability(); + map.put(factory.getErrorFactory(), new SimpleDiagnosticRenderer(message)); + map.put(factory.getWarningFactory(), new SimpleDiagnosticRenderer(deprecationMessage(factory, message))); + } + + public void put(@NotNull DiagnosticFactoryForDeprecation1 factory, @NotNull String message, @Nullable DiagnosticParameterRenderer rendererA) { + checkMutability(); + map.put(factory.getErrorFactory(), new DiagnosticWithParameters1Renderer(message, rendererA)); + map.put(factory.getWarningFactory(), new DiagnosticWithParameters1Renderer(deprecationMessage(factory, message), rendererA)); + } + + public void put(@NotNull DiagnosticFactoryForDeprecation1 factory, @NotNull String message, @NotNull MultiRenderer rendererA) { + checkMutability(); + map.put(factory.getErrorFactory(), new DiagnosticWithParametersMultiRenderer(message, rendererA)); + map.put(factory.getWarningFactory(), new DiagnosticWithParametersMultiRenderer(deprecationMessage(factory, message), rendererA)); + } + + public void put(@NotNull DiagnosticFactoryForDeprecation2 factory, + @NotNull String message, + @Nullable DiagnosticParameterRenderer rendererA, + @Nullable DiagnosticParameterRenderer rendererB) { + checkMutability(); + map.put(factory.getErrorFactory(), new DiagnosticWithParameters2Renderer(message, rendererA, rendererB)); + map.put(factory.getWarningFactory(), new DiagnosticWithParameters2Renderer(deprecationMessage(factory, message), rendererA, rendererB)); + } + + public void put(@NotNull DiagnosticFactoryForDeprecation3 factory, + @NotNull String message, + @Nullable DiagnosticParameterRenderer rendererA, + @Nullable DiagnosticParameterRenderer rendererB, + @Nullable DiagnosticParameterRenderer rendererC) { + checkMutability(); + map.put(factory.getErrorFactory(), new DiagnosticWithParameters3Renderer(message, rendererA, rendererB, rendererC)); + map.put(factory.getWarningFactory(), new DiagnosticWithParameters3Renderer(deprecationMessage(factory, message), rendererA, rendererB, rendererC)); + } + + public void put(@NotNull DiagnosticFactoryForDeprecation4 factory, + @NotNull String message, + @Nullable DiagnosticParameterRenderer rendererA, + @Nullable DiagnosticParameterRenderer rendererB, + @Nullable DiagnosticParameterRenderer rendererC, + @Nullable DiagnosticParameterRenderer rendererD) { + checkMutability(); + map.put(factory.getErrorFactory(), new DiagnosticWithParameters4Renderer(message, rendererA, rendererB, rendererC, rendererD)); + map.put(factory.getWarningFactory(), new DiagnosticWithParameters4Renderer(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); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index 40670e8f0d7..d21eb547124 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -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) + ) } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java index bdbe0162e87..50086d0b7ce 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java @@ -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();