From adfeab1bd09ddfcdf0a77d1740e660a21d8e58fe Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 30 Jul 2021 18:06:59 +0300 Subject: [PATCH] [FIR] Add ability to generate special diagnostic factories for deprecation warnings This kind of diagnostic factory has two different factories with warning and error severity and language feature with turns on reporting of error --- .../diagnostics/model/DiagnosticGroup.kt | 36 ++++++- .../diagnostics/model/DiagnosticList.kt | 62 +++++++++--- .../model/ErrorListDiagnosticListRenderer.kt | 26 +++-- ...nosticData.kt => RegularDiagnosticData.kt} | 32 ++++-- .../diagnostics/DiagnosticReporter.kt | 69 +++++++++++++ .../diagnostics/FirDiagnosticFactory.kt | 39 ++++++++ .../diagnostics/FirDiagnosticFactoryDsl.kt | 99 +++++++++++++++++++ .../FirDiagnosticFactoryToRendererMap.kt | 61 ++++++++++++ .../api/fir/generator/HLDiagnostic.kt | 2 + .../fir/generator/HLDiagnosticConverter.kt | 61 ++++++++---- ...agnosticToKtDiagnosticConverterRenderer.kt | 6 +- 11 files changed, 440 insertions(+), 53 deletions(-) rename compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/{DiagnosticData.kt => RegularDiagnosticData.kt} (70%) diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticGroup.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticGroup.kt index 3c003a905ad..41707a1da6f 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticGroup.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticGroup.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.fir.PrivateForInline import kotlin.properties.PropertyDelegateProvider @@ -24,24 +25,31 @@ abstract class AbstractDiagnosticGroup @PrivateForInline constructor(val name: S @OptIn(PrivateForInline::class) internal inline fun error( positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT, - crossinline init: DiagnosticBuilder.() -> Unit = {} + crossinline init: DiagnosticBuilder.Regular.() -> Unit = {} ) = diagnosticDelegateProvider

(Severity.ERROR, positioningStrategy, init) @OptIn(PrivateForInline::class) internal inline fun warning( positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT, - crossinline init: DiagnosticBuilder.() -> Unit = {} + crossinline init: DiagnosticBuilder.Regular.() -> Unit = {} ) = diagnosticDelegateProvider

(Severity.WARNING, positioningStrategy, init) + @OptIn(PrivateForInline::class) + internal inline fun deprecationError( + featureForError: LanguageFeature, + positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT, + crossinline init: DiagnosticBuilder.Deprecation.() -> Unit = {} + ) = deprecationDiagnosticDelegateProvider

(featureForError, positioningStrategy, init) + @PrivateForInline @OptIn(ExperimentalStdlibApi::class) internal inline fun diagnosticDelegateProvider( severity: Severity, positioningStrategy: PositioningStrategy, - crossinline init: DiagnosticBuilder.() -> Unit = {} - ) = PropertyDelegateProvider> { _, property -> - val diagnostic = DiagnosticBuilder( + crossinline init: DiagnosticBuilder.Regular.() -> Unit = {} + ) = PropertyDelegateProvider> { _, property -> + val diagnostic = DiagnosticBuilder.Regular( containingObjectName, severity, name = property.name, @@ -52,6 +60,24 @@ abstract class AbstractDiagnosticGroup @PrivateForInline constructor(val name: S ReadOnlyProperty { _, _ -> diagnostic } } + @PrivateForInline + @OptIn(ExperimentalStdlibApi::class) + internal inline fun deprecationDiagnosticDelegateProvider( + featureForError: LanguageFeature, + positioningStrategy: PositioningStrategy, + crossinline init: DiagnosticBuilder.Deprecation.() -> Unit = {} + ) = PropertyDelegateProvider> { _, property -> + val diagnostic = DiagnosticBuilder.Deprecation( + containingObjectName, + featureForError, + name = property.name, + psiType = typeOf

(), + positioningStrategy, + ).apply(init).build() + _diagnostics += diagnostic + ReadOnlyProperty { _, _ -> diagnostic } + } + @OptIn(PrivateForInline::class) operator fun plus(other: AbstractDiagnosticGroup): AbstractDiagnosticGroup { require(name == other.name) diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticList.kt index 86325b200f0..913aafb0303 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticList.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.fir.PrivateForInline import kotlin.properties.ReadOnlyProperty @@ -65,13 +66,52 @@ abstract class DiagnosticList(internal val objectName: String) { abstract inner class DiagnosticGroup(name: String) : AbstractDiagnosticGroup(name, objectName) } -class DiagnosticBuilder( - private val containingObjectName: String, - private val severity: Severity, - private val name: String, - private val psiType: KType, - private val positioningStrategy: PositioningStrategy, +sealed class DiagnosticBuilder( + protected val containingObjectName: String, + protected val name: String, + protected val psiType: KType, + protected val positioningStrategy: PositioningStrategy, ) { + class Regular( + containingObjectName: String, + private val severity: Severity, + name: String, + psiType: KType, + positioningStrategy: PositioningStrategy, + ) : DiagnosticBuilder(containingObjectName, name, psiType, positioningStrategy) { + @OptIn(PrivateForInline::class) + override fun build(): RegularDiagnosticData { + return RegularDiagnosticData( + containingObjectName, + severity, + name, + psiType, + parameters, + positioningStrategy, + ) + } + } + + class Deprecation( + containingObjectName: String, + private val featureForError: LanguageFeature, + name: String, + psiType: KType, + positioningStrategy: PositioningStrategy, + ) : DiagnosticBuilder(containingObjectName, name, psiType, positioningStrategy) { + @OptIn(PrivateForInline::class) + override fun build(): DeprecationDiagnosticData { + return DeprecationDiagnosticData( + containingObjectName, + featureForError, + name, + psiType, + parameters, + positioningStrategy, + ) + } + } + @PrivateForInline val parameters = mutableListOf() @@ -86,15 +126,7 @@ class DiagnosticBuilder( ) } - @OptIn(PrivateForInline::class) - fun build() = DiagnosticData( - containingObjectName, - severity, - name, - psiType, - parameters, - positioningStrategy, - ) + abstract fun build(): DiagnosticData companion object { const val MAX_DIAGNOSTIC_PARAMETER_COUNT = 4 diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/ErrorListDiagnosticListRenderer.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/ErrorListDiagnosticListRenderer.kt index b5b965013d2..8dc6d7bf966 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/ErrorListDiagnosticListRenderer.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/ErrorListDiagnosticListRenderer.kt @@ -55,16 +55,21 @@ object ErrorListDiagnosticListRenderer : DiagnosticListRenderer() { private fun SmartPrinter.printDiagnostic(diagnostic: DiagnosticData) { print("val ${diagnostic.name} by ${diagnostic.getFactoryFunction()}") printTypeArguments(diagnostic.getAllTypeArguments()) - printPositioningStrategy(diagnostic) + printPositioningStrategyAndLanguageFeature(diagnostic) println() } - private fun SmartPrinter.printPositioningStrategy(diagnostic: DiagnosticData) { - print("(") - if (!diagnostic.hasDefaultPositioningStrategy()) { - print(diagnostic.positioningStrategy.expressionToCreate) + @OptIn(ExperimentalStdlibApi::class) + private fun SmartPrinter.printPositioningStrategyAndLanguageFeature(diagnostic: DiagnosticData) { + val argumentsList = buildList { + if (diagnostic is DeprecationDiagnosticData) { + add(diagnostic.featureForError.name) + } + if (!diagnostic.hasDefaultPositioningStrategy()) { + add(diagnostic.positioningStrategy.expressionToCreate) + } } - print(")") + print(argumentsList.joinToString(", ", prefix = "(", postfix = ")")) } @@ -124,14 +129,19 @@ object ErrorListDiagnosticListRenderer : DiagnosticListRenderer() { add(PositioningStrategy.importToAdd) } } + for (deprecationDiagnostic in diagnosticList.allDiagnostics.filterIsInstance()) { + add("org.jetbrains.kotlin.config.LanguageFeature.${deprecationDiagnostic.featureForError.name}") + } } private val KType.kClass: KClass<*> get() = classifier as KClass<*> - private fun DiagnosticData.getFactoryFunction(): String = - severity.name.lowercase() + parameters.size + private fun DiagnosticData.getFactoryFunction(): String = when (this) { + is RegularDiagnosticData -> severity.name.lowercase() + is DeprecationDiagnosticData -> "deprecationError" + } + parameters.size } private inline fun SmartPrinter.printSeparatedWithComma(list: List, printItem: (T) -> Unit) { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt similarity index 70% rename from compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt rename to compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt index 04669d7403d..000b36744da 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt @@ -5,17 +5,35 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.diagnostics.Severity import kotlin.reflect.KType -data class DiagnosticData( - val containingObjectName: String, +sealed class DiagnosticData { + abstract val containingObjectName: String + abstract val name: String + abstract val psiType: KType + abstract val parameters: List + abstract val positioningStrategy: PositioningStrategy +} + +data class RegularDiagnosticData( + override val containingObjectName: String, val severity: Severity, - val name: String, - val psiType: KType, - val parameters: List, - val positioningStrategy: PositioningStrategy, -) + override val name: String, + override val psiType: KType, + override val parameters: List, + override val positioningStrategy: PositioningStrategy, +) : DiagnosticData() + +data class DeprecationDiagnosticData( + override val containingObjectName: String, + val featureForError: LanguageFeature, + override val name: String, + override val psiType: KType, + override val parameters: List, + override val positioningStrategy: PositioningStrategy, +) : DiagnosticData() data class DiagnosticParameter( val name: String, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/DiagnosticReporter.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/DiagnosticReporter.kt index db13a8fd0b2..ca227578891 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/DiagnosticReporter.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/DiagnosticReporter.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector +import org.jetbrains.kotlin.fir.languageVersionSettings abstract class DiagnosticReporter { abstract fun report(diagnostic: FirDiagnostic?, context: CheckerContext) @@ -75,6 +76,74 @@ fun DiagnosticReporter.reportOn( source?.let { report(factory.on(it, a, b, c, d, positioningStrategy), context) } } +@OptIn(InternalDiagnosticFactoryMethod::class) +fun DiagnosticReporter.reportOn( + source: FirSourceElement?, + factory: FirDiagnosticFactoryForDeprecation0, + context: CheckerContext, + positioningStrategy: SourceElementPositioningStrategy? = null +) { + reportOn(source, factory.chooseFactory(context), context, positioningStrategy) +} + +@OptIn(InternalDiagnosticFactoryMethod::class) +fun DiagnosticReporter.reportOn( + source: FirSourceElement?, + factory: FirDiagnosticFactoryForDeprecation1, + a: A, + context: CheckerContext, + positioningStrategy: SourceElementPositioningStrategy? = null +) { + reportOn(source, factory.chooseFactory(context), a, context, positioningStrategy) +} + +@OptIn(InternalDiagnosticFactoryMethod::class) +fun DiagnosticReporter.reportOn( + source: FirSourceElement?, + factory: FirDiagnosticFactoryForDeprecation2, + a: A, + b: B, + context: CheckerContext, + positioningStrategy: SourceElementPositioningStrategy? = null +) { + reportOn(source, factory.chooseFactory(context), a, b, context, positioningStrategy) +} + +@OptIn(InternalDiagnosticFactoryMethod::class) +fun DiagnosticReporter.reportOn( + source: FirSourceElement?, + factory: FirDiagnosticFactoryForDeprecation3, + a: A, + b: B, + c: C, + context: CheckerContext, + positioningStrategy: SourceElementPositioningStrategy? = null +) { + reportOn(source, factory.chooseFactory(context), a, b, c, context, positioningStrategy) +} + +@OptIn(InternalDiagnosticFactoryMethod::class) +fun DiagnosticReporter.reportOn( + source: FirSourceElement?, + factory: FirDiagnosticFactoryForDeprecation4, + a: A, + b: B, + c: C, + d: D, + context: CheckerContext, + positioningStrategy: SourceElementPositioningStrategy? = null +) { + reportOn(source, factory.chooseFactory(context), a, b, c, d, context, positioningStrategy) +} + +fun FirDiagnosticFactoryForDeprecation.chooseFactory(context: CheckerContext): F { + return if (context.session.languageVersionSettings.supportsFeature(deprecatingFeature)) { + errorFactory + } else { + warningFactory + } +} + inline fun withSuppressedDiagnostics( element: FirElement, context: CheckerContext, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactory.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactory.kt index 261f6268f61..315549792bc 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactory.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactory.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.fir.FirLightSourceElement import org.jetbrains.kotlin.fir.FirPsiSourceElement @@ -200,3 +201,41 @@ class FirDiagnosticFactory4( private fun incorrectElement(element: FirSourceElement): Nothing { throw IllegalArgumentException("Unknown element type: ${element::class}") } + +// ------------------------------ factories for deprecation ------------------------------ + +sealed class FirDiagnosticFactoryForDeprecation( + val deprecatingFeature: LanguageFeature, + val warningFactory: F, + val errorFactory: F +) + +class FirDiagnosticFactoryForDeprecation0( + featureForError: LanguageFeature, + warningFactory: FirDiagnosticFactory0, + errorFactory: FirDiagnosticFactory0 +) : FirDiagnosticFactoryForDeprecation(featureForError, warningFactory, errorFactory) + +class FirDiagnosticFactoryForDeprecation1( + featureForError: LanguageFeature, + warningFactory: FirDiagnosticFactory1, + errorFactory: FirDiagnosticFactory1 +) : FirDiagnosticFactoryForDeprecation>(featureForError, warningFactory, errorFactory) + +class FirDiagnosticFactoryForDeprecation2( + featureForError: LanguageFeature, + warningFactory: FirDiagnosticFactory2, + errorFactory: FirDiagnosticFactory2 +) : FirDiagnosticFactoryForDeprecation>(featureForError, warningFactory, errorFactory) + +class FirDiagnosticFactoryForDeprecation3( + featureForError: LanguageFeature, + warningFactory: FirDiagnosticFactory3, + errorFactory: FirDiagnosticFactory3 +) : FirDiagnosticFactoryForDeprecation>(featureForError, warningFactory, errorFactory) + +class FirDiagnosticFactoryForDeprecation4( + featureForError: LanguageFeature, + warningFactory: FirDiagnosticFactory4, + errorFactory: FirDiagnosticFactory4 +) : FirDiagnosticFactoryForDeprecation>(featureForError, warningFactory, errorFactory) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactoryDsl.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactoryDsl.kt index da4ab3407eb..70c61070682 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactoryDsl.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactoryDsl.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.diagnostics.Severity import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KClass @@ -65,6 +66,41 @@ inline fun error4( return DiagnosticFactory4DelegateProvider(Severity.ERROR, positioningStrategy, P::class) } +inline fun deprecationError0( + featureForError: LanguageFeature, + positioningStrategy: SourceElementPositioningStrategy = SourceElementPositioningStrategy.DEFAULT +): DeprecationDiagnosticFactory0DelegateProvider { + return DeprecationDiagnosticFactory0DelegateProvider(featureForError, positioningStrategy, P::class) +} + +inline fun deprecationError1( + featureForError: LanguageFeature, + positioningStrategy: SourceElementPositioningStrategy = SourceElementPositioningStrategy.DEFAULT +): DeprecationDiagnosticFactory1DelegateProvider { + return DeprecationDiagnosticFactory1DelegateProvider(featureForError, positioningStrategy, P::class) +} + +inline fun deprecationError2( + featureForError: LanguageFeature, + positioningStrategy: SourceElementPositioningStrategy = SourceElementPositioningStrategy.DEFAULT +): DeprecationDiagnosticFactory2DelegateProvider { + return DeprecationDiagnosticFactory2DelegateProvider(featureForError, positioningStrategy, P::class) +} + +inline fun deprecationError3( + featureForError: LanguageFeature, + positioningStrategy: SourceElementPositioningStrategy = SourceElementPositioningStrategy.DEFAULT +): DeprecationDiagnosticFactory3DelegateProvider { + return DeprecationDiagnosticFactory3DelegateProvider(featureForError, positioningStrategy, P::class) +} + +inline fun deprecationError4( + featureForError: LanguageFeature, + positioningStrategy: SourceElementPositioningStrategy = SourceElementPositioningStrategy.DEFAULT +): DeprecationDiagnosticFactory4DelegateProvider { + return DeprecationDiagnosticFactory4DelegateProvider(featureForError, positioningStrategy, P::class) +} + // ------------------------------ Providers ------------------------------ class DiagnosticFactory0DelegateProvider( @@ -117,6 +153,69 @@ class DiagnosticFactory4DelegateProvider( } } +private const val WARNING = "_WARNING" +private const val ERROR = "_ERROR" + +class DeprecationDiagnosticFactory0DelegateProvider( + private val featureForError: LanguageFeature, + private val positioningStrategy: SourceElementPositioningStrategy, + private val psiType: KClass<*> +) { + operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadOnlyProperty { + val errorFactory = FirDiagnosticFactory0("${prop.name}$ERROR", Severity.ERROR, positioningStrategy, psiType) + val warningFactory = FirDiagnosticFactory0("${prop.name}$WARNING", Severity.WARNING, positioningStrategy, psiType) + return DummyDelegate(FirDiagnosticFactoryForDeprecation0(featureForError, warningFactory, errorFactory)) + } +} + +class DeprecationDiagnosticFactory1DelegateProvider( + private val featureForError: LanguageFeature, + private val positioningStrategy: SourceElementPositioningStrategy, + private val psiType: KClass<*> +) { + operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadOnlyProperty> { + val errorFactory = FirDiagnosticFactory1("${prop.name}$ERROR", Severity.ERROR, positioningStrategy, psiType) + val warningFactory = FirDiagnosticFactory1("${prop.name}$WARNING", Severity.WARNING, positioningStrategy, psiType) + return DummyDelegate(FirDiagnosticFactoryForDeprecation1(featureForError, warningFactory, errorFactory)) + } +} + +class DeprecationDiagnosticFactory2DelegateProvider( + private val featureForError: LanguageFeature, + private val positioningStrategy: SourceElementPositioningStrategy, + private val psiType: KClass<*> +) { + operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadOnlyProperty> { + val errorFactory = FirDiagnosticFactory2("${prop.name}$ERROR", Severity.ERROR, positioningStrategy, psiType) + val warningFactory = FirDiagnosticFactory2("${prop.name}$WARNING", Severity.WARNING, positioningStrategy, psiType) + return DummyDelegate(FirDiagnosticFactoryForDeprecation2(featureForError, warningFactory, errorFactory)) + } +} + +class DeprecationDiagnosticFactory3DelegateProvider( + private val featureForError: LanguageFeature, + private val positioningStrategy: SourceElementPositioningStrategy, + private val psiType: KClass<*> +) { + operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadOnlyProperty> { + val errorFactory = FirDiagnosticFactory3("${prop.name}$ERROR", Severity.ERROR, positioningStrategy, psiType) + val warningFactory = FirDiagnosticFactory3("${prop.name}$WARNING", Severity.WARNING, positioningStrategy, psiType) + return DummyDelegate(FirDiagnosticFactoryForDeprecation3(featureForError, warningFactory, errorFactory)) + } +} + +class DeprecationDiagnosticFactory4DelegateProvider( + private val featureForError: LanguageFeature, + private val positioningStrategy: SourceElementPositioningStrategy, + private val psiType: KClass<*> +) { + operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadOnlyProperty> { + val errorFactory = FirDiagnosticFactory4("${prop.name}$ERROR", Severity.ERROR, positioningStrategy, psiType) + val warningFactory = FirDiagnosticFactory4("${prop.name}$WARNING", Severity.WARNING, positioningStrategy, psiType) + return DummyDelegate(FirDiagnosticFactoryForDeprecation4(featureForError, warningFactory, errorFactory)) + } +} + private class DummyDelegate(val value: T) : ReadOnlyProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): T { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactoryToRendererMap.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactoryToRendererMap.kt index 1ca294c6282..22cac3df39f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactoryToRendererMap.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactoryToRendererMap.kt @@ -54,7 +54,68 @@ class FirDiagnosticFactoryToRendererMap(val name: String) { put(factory, FirDiagnosticWithParameters4Renderer(message, rendererA, rendererB, rendererC, rendererD)) } + fun put(factory: FirDiagnosticFactoryForDeprecation0, message: String) { + put(factory.errorFactory, SimpleFirDiagnosticRenderer(message)) + put(factory.warningFactory, SimpleFirDiagnosticRenderer(factory.warningMessage(message))) + } + + fun put( + factory: FirDiagnosticFactoryForDeprecation1, + message: String, + rendererA: DiagnosticParameterRenderer? + ) { + put(factory.errorFactory, FirDiagnosticWithParameters1Renderer(message, rendererA)) + put(factory.warningFactory, FirDiagnosticWithParameters1Renderer(factory.warningMessage(message), rendererA)) + } + + fun put( + factory: FirDiagnosticFactoryForDeprecation2, + message: String, + rendererA: DiagnosticParameterRenderer?, + rendererB: DiagnosticParameterRenderer? + ) { + put(factory.errorFactory, FirDiagnosticWithParameters2Renderer(message, rendererA, rendererB)) + put(factory.warningFactory, FirDiagnosticWithParameters2Renderer(factory.warningMessage(message), rendererA, rendererB)) + } + + fun put( + factory: FirDiagnosticFactoryForDeprecation3, + message: String, + rendererA: DiagnosticParameterRenderer?, + rendererB: DiagnosticParameterRenderer?, + rendererC: DiagnosticParameterRenderer? + ) { + put(factory.errorFactory, FirDiagnosticWithParameters3Renderer(message, rendererA, rendererB, rendererC)) + put(factory.warningFactory, FirDiagnosticWithParameters3Renderer(factory.warningMessage(message), rendererA, rendererB, rendererC)) + } + + fun put( + factory: FirDiagnosticFactoryForDeprecation4, + message: String, + rendererA: DiagnosticParameterRenderer?, + rendererB: DiagnosticParameterRenderer?, + rendererC: DiagnosticParameterRenderer?, + rendererD: DiagnosticParameterRenderer? + ) { + put(factory.errorFactory, FirDiagnosticWithParameters4Renderer(message, rendererA, rendererB, rendererC, rendererD)) + put(factory.warningFactory, FirDiagnosticWithParameters4Renderer(factory.warningMessage(message), rendererA, rendererB, rendererC, rendererD)) + } + private fun put(factory: AbstractFirDiagnosticFactory, renderer: FirDiagnosticRenderer) { renderersMap[factory] = renderer } + + private fun FirDiagnosticFactoryForDeprecation<*>.warningMessage(errorMessage: String): String { + return buildString { + append(errorMessage) + append(". This will become an error") + val sinceVersion = deprecatingFeature.sinceVersion + if (sinceVersion != null) { + append(" in Kotlin ") + append(sinceVersion.versionString) + } else { + append(" in a future release") + } + } + } } diff --git a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnostic.kt b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnostic.kt index 01ec68456f0..0a5b26b3807 100644 --- a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnostic.kt +++ b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnostic.kt @@ -5,12 +5,14 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.generator +import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticData import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticParameter import kotlin.reflect.KType data class HLDiagnostic( val original: DiagnosticData, + val severity: Severity?, val className: String, val implClassName: String, val parameters: List, diff --git a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt index 8ec61cff58c..03e84f05aa2 100644 --- a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt +++ b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt @@ -11,11 +11,10 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.fir.FirModuleData -import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticData -import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList -import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticParameter +import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess @@ -39,16 +38,33 @@ import kotlin.reflect.full.createType import kotlin.reflect.full.isSubclassOf object HLDiagnosticConverter { - fun convert(diagnosticList: DiagnosticList): HLDiagnosticList = - HLDiagnosticList(diagnosticList.allDiagnostics.map(::convertDiagnostic)) + @OptIn(ExperimentalStdlibApi::class) + fun convert(diagnosticList: DiagnosticList): HLDiagnosticList { + return HLDiagnosticList(diagnosticList.allDiagnostics.flatMap(::convertDiagnostic)) + } - private fun convertDiagnostic(diagnostic: DiagnosticData): HLDiagnostic = - HLDiagnostic( - original = diagnostic, - className = diagnostic.getHLDiagnosticClassName(), - implClassName = diagnostic.getHLDiagnosticImplClassName(), - parameters = diagnostic.parameters.mapIndexed(::convertParameter) - ) + private fun convertDiagnostic(diagnostic: DiagnosticData): List { + return when (diagnostic){ + is RegularDiagnosticData -> listOf( + HLDiagnostic( + original = diagnostic, + severity = null, + className = diagnostic.getHLDiagnosticClassName(), + implClassName = diagnostic.getHLDiagnosticImplClassName(), + parameters = diagnostic.parameters.mapIndexed(::convertParameter) + ) + ) + is DeprecationDiagnosticData -> listOf(Severity.ERROR, Severity.WARNING).map { + HLDiagnostic( + original = diagnostic, + severity = it, + className = diagnostic.getHLDiagnosticClassName(it), + implClassName = diagnostic.getHLDiagnosticImplClassName(it), + parameters = diagnostic.parameters.mapIndexed(::convertParameter) + ) + } + } + } private fun convertParameter(index: Int, diagnosticParameter: DiagnosticParameter): HLDiagnosticParameter { val conversion = FirToKtConversionCreator.createConversion(diagnosticParameter.type) @@ -63,16 +79,27 @@ object HLDiagnosticConverter { ) } - private fun DiagnosticData.getHLDiagnosticClassName() = - name.lowercase() + private fun RegularDiagnosticData.getHLDiagnosticClassName(): String = name.sanitizeName() + + private fun RegularDiagnosticData.getHLDiagnosticImplClassName(): String = + "${getHLDiagnosticClassName()}Impl" + + private fun DeprecationDiagnosticData.getHLDiagnosticClassName(severity: Severity): String { + val diagnosticName = "${name}_${severity.name}" + return diagnosticName.sanitizeName() + } + + private fun DeprecationDiagnosticData.getHLDiagnosticImplClassName(severity: Severity): String { + return "${getHLDiagnosticClassName(severity)}Impl" + } + + private fun String.sanitizeName(): String = + lowercase() .split('_') .joinToString(separator = "") { it.replaceFirstChar(Char::uppercaseChar) } - private fun DiagnosticData.getHLDiagnosticImplClassName() = - "${getHLDiagnosticClassName()}Impl" - } diff --git a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/rendererrs/FirDiagnosticToKtDiagnosticConverterRenderer.kt b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/rendererrs/FirDiagnosticToKtDiagnosticConverterRenderer.kt index 716d339bb3d..a2559876c35 100644 --- a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/rendererrs/FirDiagnosticToKtDiagnosticConverterRenderer.kt +++ b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/rendererrs/FirDiagnosticToKtDiagnosticConverterRenderer.kt @@ -28,7 +28,11 @@ object FirDiagnosticToKtDiagnosticConverterRenderer : AbstractDiagnosticsDataCla } private fun SmartPrinter.printConverter(diagnostic: HLDiagnostic) { - println("add(${diagnostic.original.containingObjectName}.${diagnostic.original.name}) { firDiagnostic ->") + print("add(${diagnostic.original.containingObjectName}.${diagnostic.original.name}") + if (diagnostic.severity != null) { + print(".${diagnostic.severity.name.lowercase()}Factory") + } + println(") { firDiagnostic ->") withIndent { println("${diagnostic.implClassName}(") withIndent {