[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
This commit is contained in:
Dmitriy Novozhilov
2021-07-30 18:06:59 +03:00
parent d17f984edf
commit adfeab1bd0
11 changed files with 440 additions and 53 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model package org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.PrivateForInline
import kotlin.properties.PropertyDelegateProvider import kotlin.properties.PropertyDelegateProvider
@@ -24,24 +25,31 @@ abstract class AbstractDiagnosticGroup @PrivateForInline constructor(val name: S
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
internal inline fun <reified P : PsiElement> error( internal inline fun <reified P : PsiElement> error(
positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT, positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT,
crossinline init: DiagnosticBuilder.() -> Unit = {} crossinline init: DiagnosticBuilder.Regular.() -> Unit = {}
) = diagnosticDelegateProvider<P>(Severity.ERROR, positioningStrategy, init) ) = diagnosticDelegateProvider<P>(Severity.ERROR, positioningStrategy, init)
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
internal inline fun <reified P : PsiElement> warning( internal inline fun <reified P : PsiElement> warning(
positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT, positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT,
crossinline init: DiagnosticBuilder.() -> Unit = {} crossinline init: DiagnosticBuilder.Regular.() -> Unit = {}
) = diagnosticDelegateProvider<P>(Severity.WARNING, positioningStrategy, init) ) = diagnosticDelegateProvider<P>(Severity.WARNING, positioningStrategy, init)
@OptIn(PrivateForInline::class)
internal inline fun <reified P : PsiElement> deprecationError(
featureForError: LanguageFeature,
positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT,
crossinline init: DiagnosticBuilder.Deprecation.() -> Unit = {}
) = deprecationDiagnosticDelegateProvider<P>(featureForError, positioningStrategy, init)
@PrivateForInline @PrivateForInline
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
internal inline fun <reified P : PsiElement> diagnosticDelegateProvider( internal inline fun <reified P : PsiElement> diagnosticDelegateProvider(
severity: Severity, severity: Severity,
positioningStrategy: PositioningStrategy, positioningStrategy: PositioningStrategy,
crossinline init: DiagnosticBuilder.() -> Unit = {} crossinline init: DiagnosticBuilder.Regular.() -> Unit = {}
) = PropertyDelegateProvider<Any?, ReadOnlyProperty<AbstractDiagnosticGroup, DiagnosticData>> { _, property -> ) = PropertyDelegateProvider<Any?, ReadOnlyProperty<AbstractDiagnosticGroup, RegularDiagnosticData>> { _, property ->
val diagnostic = DiagnosticBuilder( val diagnostic = DiagnosticBuilder.Regular(
containingObjectName, containingObjectName,
severity, severity,
name = property.name, name = property.name,
@@ -52,6 +60,24 @@ abstract class AbstractDiagnosticGroup @PrivateForInline constructor(val name: S
ReadOnlyProperty { _, _ -> diagnostic } ReadOnlyProperty { _, _ -> diagnostic }
} }
@PrivateForInline
@OptIn(ExperimentalStdlibApi::class)
internal inline fun <reified P : PsiElement> deprecationDiagnosticDelegateProvider(
featureForError: LanguageFeature,
positioningStrategy: PositioningStrategy,
crossinline init: DiagnosticBuilder.Deprecation.() -> Unit = {}
) = PropertyDelegateProvider<Any?, ReadOnlyProperty<AbstractDiagnosticGroup, DeprecationDiagnosticData>> { _, property ->
val diagnostic = DiagnosticBuilder.Deprecation(
containingObjectName,
featureForError,
name = property.name,
psiType = typeOf<P>(),
positioningStrategy,
).apply(init).build()
_diagnostics += diagnostic
ReadOnlyProperty { _, _ -> diagnostic }
}
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
operator fun plus(other: AbstractDiagnosticGroup): AbstractDiagnosticGroup { operator fun plus(other: AbstractDiagnosticGroup): AbstractDiagnosticGroup {
require(name == other.name) require(name == other.name)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model 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.diagnostics.Severity
import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.PrivateForInline
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
@@ -65,13 +66,52 @@ abstract class DiagnosticList(internal val objectName: String) {
abstract inner class DiagnosticGroup(name: String) : AbstractDiagnosticGroup(name, objectName) abstract inner class DiagnosticGroup(name: String) : AbstractDiagnosticGroup(name, objectName)
} }
class DiagnosticBuilder( sealed class DiagnosticBuilder(
private val containingObjectName: String, protected val containingObjectName: String,
private val severity: Severity, protected val name: String,
private val name: String, protected val psiType: KType,
private val psiType: KType, protected val positioningStrategy: PositioningStrategy,
private 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 @PrivateForInline
val parameters = mutableListOf<DiagnosticParameter>() val parameters = mutableListOf<DiagnosticParameter>()
@@ -86,15 +126,7 @@ class DiagnosticBuilder(
) )
} }
@OptIn(PrivateForInline::class) abstract fun build(): DiagnosticData
fun build() = DiagnosticData(
containingObjectName,
severity,
name,
psiType,
parameters,
positioningStrategy,
)
companion object { companion object {
const val MAX_DIAGNOSTIC_PARAMETER_COUNT = 4 const val MAX_DIAGNOSTIC_PARAMETER_COUNT = 4
@@ -55,16 +55,21 @@ object ErrorListDiagnosticListRenderer : DiagnosticListRenderer() {
private fun SmartPrinter.printDiagnostic(diagnostic: DiagnosticData) { private fun SmartPrinter.printDiagnostic(diagnostic: DiagnosticData) {
print("val ${diagnostic.name} by ${diagnostic.getFactoryFunction()}") print("val ${diagnostic.name} by ${diagnostic.getFactoryFunction()}")
printTypeArguments(diagnostic.getAllTypeArguments()) printTypeArguments(diagnostic.getAllTypeArguments())
printPositioningStrategy(diagnostic) printPositioningStrategyAndLanguageFeature(diagnostic)
println() println()
} }
private fun SmartPrinter.printPositioningStrategy(diagnostic: DiagnosticData) { @OptIn(ExperimentalStdlibApi::class)
print("(") private fun SmartPrinter.printPositioningStrategyAndLanguageFeature(diagnostic: DiagnosticData) {
if (!diagnostic.hasDefaultPositioningStrategy()) { val argumentsList = buildList {
print(diagnostic.positioningStrategy.expressionToCreate) 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) add(PositioningStrategy.importToAdd)
} }
} }
for (deprecationDiagnostic in diagnosticList.allDiagnostics.filterIsInstance<DeprecationDiagnosticData>()) {
add("org.jetbrains.kotlin.config.LanguageFeature.${deprecationDiagnostic.featureForError.name}")
}
} }
private val KType.kClass: KClass<*> private val KType.kClass: KClass<*>
get() = classifier as KClass<*> get() = classifier as KClass<*>
private fun DiagnosticData.getFactoryFunction(): String = private fun DiagnosticData.getFactoryFunction(): String = when (this) {
severity.name.lowercase() + parameters.size is RegularDiagnosticData -> severity.name.lowercase()
is DeprecationDiagnosticData -> "deprecationError"
} + parameters.size
} }
private inline fun <T> SmartPrinter.printSeparatedWithComma(list: List<T>, printItem: (T) -> Unit) { private inline fun <T> SmartPrinter.printSeparatedWithComma(list: List<T>, printItem: (T) -> Unit) {
@@ -5,17 +5,35 @@
package org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model 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.diagnostics.Severity
import kotlin.reflect.KType import kotlin.reflect.KType
data class DiagnosticData( sealed class DiagnosticData {
val containingObjectName: String, abstract val containingObjectName: String
abstract val name: String
abstract val psiType: KType
abstract val parameters: List<DiagnosticParameter>
abstract val positioningStrategy: PositioningStrategy
}
data class RegularDiagnosticData(
override val containingObjectName: String,
val severity: Severity, val severity: Severity,
val name: String, override val name: String,
val psiType: KType, override val psiType: KType,
val parameters: List<DiagnosticParameter>, override val parameters: List<DiagnosticParameter>,
val positioningStrategy: PositioningStrategy, 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<DiagnosticParameter>,
override val positioningStrategy: PositioningStrategy,
) : DiagnosticData()
data class DiagnosticParameter( data class DiagnosticParameter(
val name: String, val name: String,
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
import org.jetbrains.kotlin.fir.languageVersionSettings
abstract class DiagnosticReporter { abstract class DiagnosticReporter {
abstract fun report(diagnostic: FirDiagnostic?, context: CheckerContext) abstract fun report(diagnostic: FirDiagnostic?, context: CheckerContext)
@@ -75,6 +76,74 @@ fun <A : Any, B : Any, C : Any, D : Any> DiagnosticReporter.reportOn(
source?.let { report(factory.on(it, a, b, c, d, positioningStrategy), context) } 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 <A : Any> DiagnosticReporter.reportOn(
source: FirSourceElement?,
factory: FirDiagnosticFactoryForDeprecation1<A>,
a: A,
context: CheckerContext,
positioningStrategy: SourceElementPositioningStrategy? = null
) {
reportOn(source, factory.chooseFactory(context), a, context, positioningStrategy)
}
@OptIn(InternalDiagnosticFactoryMethod::class)
fun <A : Any, B : Any> DiagnosticReporter.reportOn(
source: FirSourceElement?,
factory: FirDiagnosticFactoryForDeprecation2<A, B>,
a: A,
b: B,
context: CheckerContext,
positioningStrategy: SourceElementPositioningStrategy? = null
) {
reportOn(source, factory.chooseFactory(context), a, b, context, positioningStrategy)
}
@OptIn(InternalDiagnosticFactoryMethod::class)
fun <A : Any, B : Any, C : Any> DiagnosticReporter.reportOn(
source: FirSourceElement?,
factory: FirDiagnosticFactoryForDeprecation3<A, B, C>,
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 <A : Any, B : Any, C : Any, D : Any> DiagnosticReporter.reportOn(
source: FirSourceElement?,
factory: FirDiagnosticFactoryForDeprecation4<A, B, C, D>,
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 <F : AbstractFirDiagnosticFactory> FirDiagnosticFactoryForDeprecation<F>.chooseFactory(context: CheckerContext): F {
return if (context.session.languageVersionSettings.supportsFeature(deprecatingFeature)) {
errorFactory
} else {
warningFactory
}
}
inline fun withSuppressedDiagnostics( inline fun withSuppressedDiagnostics(
element: FirElement, element: FirElement,
context: CheckerContext, context: CheckerContext,
@@ -2,6 +2,7 @@
package org.jetbrains.kotlin.fir.analysis.diagnostics package org.jetbrains.kotlin.fir.analysis.diagnostics
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.fir.FirLightSourceElement import org.jetbrains.kotlin.fir.FirLightSourceElement
import org.jetbrains.kotlin.fir.FirPsiSourceElement import org.jetbrains.kotlin.fir.FirPsiSourceElement
@@ -200,3 +201,41 @@ class FirDiagnosticFactory4<A, B, C, D>(
private fun incorrectElement(element: FirSourceElement): Nothing { private fun incorrectElement(element: FirSourceElement): Nothing {
throw IllegalArgumentException("Unknown element type: ${element::class}") throw IllegalArgumentException("Unknown element type: ${element::class}")
} }
// ------------------------------ factories for deprecation ------------------------------
sealed class FirDiagnosticFactoryForDeprecation<F : AbstractFirDiagnosticFactory>(
val deprecatingFeature: LanguageFeature,
val warningFactory: F,
val errorFactory: F
)
class FirDiagnosticFactoryForDeprecation0(
featureForError: LanguageFeature,
warningFactory: FirDiagnosticFactory0,
errorFactory: FirDiagnosticFactory0
) : FirDiagnosticFactoryForDeprecation<FirDiagnosticFactory0>(featureForError, warningFactory, errorFactory)
class FirDiagnosticFactoryForDeprecation1<A>(
featureForError: LanguageFeature,
warningFactory: FirDiagnosticFactory1<A>,
errorFactory: FirDiagnosticFactory1<A>
) : FirDiagnosticFactoryForDeprecation<FirDiagnosticFactory1<A>>(featureForError, warningFactory, errorFactory)
class FirDiagnosticFactoryForDeprecation2<A, B>(
featureForError: LanguageFeature,
warningFactory: FirDiagnosticFactory2<A, B>,
errorFactory: FirDiagnosticFactory2<A, B>
) : FirDiagnosticFactoryForDeprecation<FirDiagnosticFactory2<A, B>>(featureForError, warningFactory, errorFactory)
class FirDiagnosticFactoryForDeprecation3<A, B, C>(
featureForError: LanguageFeature,
warningFactory: FirDiagnosticFactory3<A, B, C>,
errorFactory: FirDiagnosticFactory3<A, B, C>
) : FirDiagnosticFactoryForDeprecation<FirDiagnosticFactory3<A, B, C>>(featureForError, warningFactory, errorFactory)
class FirDiagnosticFactoryForDeprecation4<A, B, C, D>(
featureForError: LanguageFeature,
warningFactory: FirDiagnosticFactory4<A, B, C, D>,
errorFactory: FirDiagnosticFactory4<A, B, C, D>
) : FirDiagnosticFactoryForDeprecation<FirDiagnosticFactory4<A, B, C, D>>(featureForError, warningFactory, errorFactory)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.analysis.diagnostics package org.jetbrains.kotlin.fir.analysis.diagnostics
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.Severity
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KClass import kotlin.reflect.KClass
@@ -65,6 +66,41 @@ inline fun <reified P : PsiElement, A, B, C, D> error4(
return DiagnosticFactory4DelegateProvider(Severity.ERROR, positioningStrategy, P::class) return DiagnosticFactory4DelegateProvider(Severity.ERROR, positioningStrategy, P::class)
} }
inline fun <reified P : PsiElement> deprecationError0(
featureForError: LanguageFeature,
positioningStrategy: SourceElementPositioningStrategy = SourceElementPositioningStrategy.DEFAULT
): DeprecationDiagnosticFactory0DelegateProvider {
return DeprecationDiagnosticFactory0DelegateProvider(featureForError, positioningStrategy, P::class)
}
inline fun <reified P : PsiElement, A> deprecationError1(
featureForError: LanguageFeature,
positioningStrategy: SourceElementPositioningStrategy = SourceElementPositioningStrategy.DEFAULT
): DeprecationDiagnosticFactory1DelegateProvider<A> {
return DeprecationDiagnosticFactory1DelegateProvider(featureForError, positioningStrategy, P::class)
}
inline fun <reified P : PsiElement, A, B> deprecationError2(
featureForError: LanguageFeature,
positioningStrategy: SourceElementPositioningStrategy = SourceElementPositioningStrategy.DEFAULT
): DeprecationDiagnosticFactory2DelegateProvider<A, B> {
return DeprecationDiagnosticFactory2DelegateProvider(featureForError, positioningStrategy, P::class)
}
inline fun <reified P : PsiElement, A, B, C> deprecationError3(
featureForError: LanguageFeature,
positioningStrategy: SourceElementPositioningStrategy = SourceElementPositioningStrategy.DEFAULT
): DeprecationDiagnosticFactory3DelegateProvider<A, B, C> {
return DeprecationDiagnosticFactory3DelegateProvider(featureForError, positioningStrategy, P::class)
}
inline fun <reified P : PsiElement, A, B, C, D> deprecationError4(
featureForError: LanguageFeature,
positioningStrategy: SourceElementPositioningStrategy = SourceElementPositioningStrategy.DEFAULT
): DeprecationDiagnosticFactory4DelegateProvider<A, B, C, D> {
return DeprecationDiagnosticFactory4DelegateProvider(featureForError, positioningStrategy, P::class)
}
// ------------------------------ Providers ------------------------------ // ------------------------------ Providers ------------------------------
class DiagnosticFactory0DelegateProvider( class DiagnosticFactory0DelegateProvider(
@@ -117,6 +153,69 @@ class DiagnosticFactory4DelegateProvider<A, B, C, D>(
} }
} }
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<Any?, FirDiagnosticFactoryForDeprecation0> {
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<A>(
private val featureForError: LanguageFeature,
private val positioningStrategy: SourceElementPositioningStrategy,
private val psiType: KClass<*>
) {
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadOnlyProperty<Any?, FirDiagnosticFactoryForDeprecation1<A>> {
val errorFactory = FirDiagnosticFactory1<A>("${prop.name}$ERROR", Severity.ERROR, positioningStrategy, psiType)
val warningFactory = FirDiagnosticFactory1<A>("${prop.name}$WARNING", Severity.WARNING, positioningStrategy, psiType)
return DummyDelegate(FirDiagnosticFactoryForDeprecation1(featureForError, warningFactory, errorFactory))
}
}
class DeprecationDiagnosticFactory2DelegateProvider<A, B>(
private val featureForError: LanguageFeature,
private val positioningStrategy: SourceElementPositioningStrategy,
private val psiType: KClass<*>
) {
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadOnlyProperty<Any?, FirDiagnosticFactoryForDeprecation2<A, B>> {
val errorFactory = FirDiagnosticFactory2<A, B>("${prop.name}$ERROR", Severity.ERROR, positioningStrategy, psiType)
val warningFactory = FirDiagnosticFactory2<A, B>("${prop.name}$WARNING", Severity.WARNING, positioningStrategy, psiType)
return DummyDelegate(FirDiagnosticFactoryForDeprecation2(featureForError, warningFactory, errorFactory))
}
}
class DeprecationDiagnosticFactory3DelegateProvider<A, B, C>(
private val featureForError: LanguageFeature,
private val positioningStrategy: SourceElementPositioningStrategy,
private val psiType: KClass<*>
) {
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadOnlyProperty<Any?, FirDiagnosticFactoryForDeprecation3<A, B, C>> {
val errorFactory = FirDiagnosticFactory3<A, B, C>("${prop.name}$ERROR", Severity.ERROR, positioningStrategy, psiType)
val warningFactory = FirDiagnosticFactory3<A, B, C>("${prop.name}$WARNING", Severity.WARNING, positioningStrategy, psiType)
return DummyDelegate(FirDiagnosticFactoryForDeprecation3(featureForError, warningFactory, errorFactory))
}
}
class DeprecationDiagnosticFactory4DelegateProvider<A, B, C, D>(
private val featureForError: LanguageFeature,
private val positioningStrategy: SourceElementPositioningStrategy,
private val psiType: KClass<*>
) {
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadOnlyProperty<Any?, FirDiagnosticFactoryForDeprecation4<A, B, C, D>> {
val errorFactory = FirDiagnosticFactory4<A, B, C, D>("${prop.name}$ERROR", Severity.ERROR, positioningStrategy, psiType)
val warningFactory = FirDiagnosticFactory4<A, B, C, D>("${prop.name}$WARNING", Severity.WARNING, positioningStrategy, psiType)
return DummyDelegate(FirDiagnosticFactoryForDeprecation4(featureForError, warningFactory, errorFactory))
}
}
private class DummyDelegate<T>(val value: T) : ReadOnlyProperty<Any?, T> { private class DummyDelegate<T>(val value: T) : ReadOnlyProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T { override fun getValue(thisRef: Any?, property: KProperty<*>): T {
@@ -54,7 +54,68 @@ class FirDiagnosticFactoryToRendererMap(val name: String) {
put(factory, FirDiagnosticWithParameters4Renderer(message, rendererA, rendererB, rendererC, rendererD)) 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 <A> put(
factory: FirDiagnosticFactoryForDeprecation1<A>,
message: String,
rendererA: DiagnosticParameterRenderer<A>?
) {
put(factory.errorFactory, FirDiagnosticWithParameters1Renderer(message, rendererA))
put(factory.warningFactory, FirDiagnosticWithParameters1Renderer(factory.warningMessage(message), rendererA))
}
fun <A, B> put(
factory: FirDiagnosticFactoryForDeprecation2<A, B>,
message: String,
rendererA: DiagnosticParameterRenderer<A>?,
rendererB: DiagnosticParameterRenderer<B>?
) {
put(factory.errorFactory, FirDiagnosticWithParameters2Renderer(message, rendererA, rendererB))
put(factory.warningFactory, FirDiagnosticWithParameters2Renderer(factory.warningMessage(message), rendererA, rendererB))
}
fun <A, B, C> put(
factory: FirDiagnosticFactoryForDeprecation3<A, B, C>,
message: String,
rendererA: DiagnosticParameterRenderer<A>?,
rendererB: DiagnosticParameterRenderer<B>?,
rendererC: DiagnosticParameterRenderer<C>?
) {
put(factory.errorFactory, FirDiagnosticWithParameters3Renderer(message, rendererA, rendererB, rendererC))
put(factory.warningFactory, FirDiagnosticWithParameters3Renderer(factory.warningMessage(message), rendererA, rendererB, rendererC))
}
fun <A, B, C, D> put(
factory: FirDiagnosticFactoryForDeprecation4<A, B, C, D>,
message: String,
rendererA: DiagnosticParameterRenderer<A>?,
rendererB: DiagnosticParameterRenderer<B>?,
rendererC: DiagnosticParameterRenderer<C>?,
rendererD: DiagnosticParameterRenderer<D>?
) {
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) { private fun put(factory: AbstractFirDiagnosticFactory, renderer: FirDiagnosticRenderer) {
renderersMap[factory] = renderer 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")
}
}
}
} }
@@ -5,12 +5,14 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.generator 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.DiagnosticData
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticParameter import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticParameter
import kotlin.reflect.KType import kotlin.reflect.KType
data class HLDiagnostic( data class HLDiagnostic(
val original: DiagnosticData, val original: DiagnosticData,
val severity: Severity?,
val className: String, val className: String,
val implClassName: String, val implClassName: String,
val parameters: List<HLDiagnosticParameter>, val parameters: List<HLDiagnosticParameter>,
@@ -11,11 +11,10 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirModuleData 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.*
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.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
@@ -39,16 +38,33 @@ import kotlin.reflect.full.createType
import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSubclassOf
object HLDiagnosticConverter { object HLDiagnosticConverter {
fun convert(diagnosticList: DiagnosticList): HLDiagnosticList = @OptIn(ExperimentalStdlibApi::class)
HLDiagnosticList(diagnosticList.allDiagnostics.map(::convertDiagnostic)) fun convert(diagnosticList: DiagnosticList): HLDiagnosticList {
return HLDiagnosticList(diagnosticList.allDiagnostics.flatMap(::convertDiagnostic))
}
private fun convertDiagnostic(diagnostic: DiagnosticData): HLDiagnostic = private fun convertDiagnostic(diagnostic: DiagnosticData): List<HLDiagnostic> {
HLDiagnostic( return when (diagnostic){
original = diagnostic, is RegularDiagnosticData -> listOf(
className = diagnostic.getHLDiagnosticClassName(), HLDiagnostic(
implClassName = diagnostic.getHLDiagnosticImplClassName(), original = diagnostic,
parameters = diagnostic.parameters.mapIndexed(::convertParameter) 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 { private fun convertParameter(index: Int, diagnosticParameter: DiagnosticParameter): HLDiagnosticParameter {
val conversion = FirToKtConversionCreator.createConversion(diagnosticParameter.type) val conversion = FirToKtConversionCreator.createConversion(diagnosticParameter.type)
@@ -63,16 +79,27 @@ object HLDiagnosticConverter {
) )
} }
private fun DiagnosticData.getHLDiagnosticClassName() = private fun RegularDiagnosticData.getHLDiagnosticClassName(): String = name.sanitizeName()
name.lowercase()
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('_') .split('_')
.joinToString(separator = "") { .joinToString(separator = "") {
it.replaceFirstChar(Char::uppercaseChar) it.replaceFirstChar(Char::uppercaseChar)
} }
private fun DiagnosticData.getHLDiagnosticImplClassName() =
"${getHLDiagnosticClassName()}Impl"
} }
@@ -28,7 +28,11 @@ object FirDiagnosticToKtDiagnosticConverterRenderer : AbstractDiagnosticsDataCla
} }
private fun SmartPrinter.printConverter(diagnostic: HLDiagnostic) { 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 { withIndent {
println("${diagnostic.implClassName}(") println("${diagnostic.implClassName}(")
withIndent { withIndent {