FIR IDE: move analysis api fir generator to the analysis directory

This commit is contained in:
Ilya Kirillov
2021-09-14 17:21:29 +02:00
parent b43e664985
commit 4d52b354af
12 changed files with 36 additions and 36 deletions
@@ -0,0 +1,44 @@
plugins {
kotlin("jvm")
id("jps-compatible")
application
}
dependencies {
implementation(project(":compiler:resolution.common"))
implementation(project(":compiler:fir:tree"))
implementation(project(":compiler:fir:tree:tree-generator"))
implementation(project(":compiler:fir:checkers:checkers-component-generator"))
implementation(project(":analysis:analysis-api"))
implementation(project(":kotlin-reflect"))
implementation(project(":kotlin-reflect-api"))
/*
We do not need guava in the generator, but because of a bug in the IJ project importing, we need to have a dependency on intellijCoreDep
the same as it is in `:fir:tree:tree-generator` module to the project be imported correctly
*/
compileOnly(intellijCoreDep()) { includeJars("intellij-core", "guava", rootProject = rootProject) }
implementation(project(":compiler:psi"))
}
val writeCopyright by task<tasks.WriteCopyrightToFile> {
outputFile.set(file("$buildDir/copyright/notice.txt"))
commented.set(true)
}
application {
mainClassName = "org.jetbrains.kotlin.analysis.api.fir.generator.MainKt"
}
val processResources by tasks
processResources.dependsOn(writeCopyright)
sourceSets {
"main" {
projectDefault()
resources.srcDir("$buildDir/copyright")
}
"test" {}
}
@@ -0,0 +1,22 @@
/*
* 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.analysis.api.fir.generator
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
import org.jetbrains.kotlin.fir.checkers.generator.getGenerationPath
import org.jetbrains.kotlin.analysis.api.fir.generator.rendererrs.FirDiagnosticToKtDiagnosticConverterRenderer
import org.jetbrains.kotlin.analysis.api.fir.generator.rendererrs.KtDiagnosticClassImplementationRenderer
import org.jetbrains.kotlin.analysis.api.fir.generator.rendererrs.KtDiagnosticClassRenderer
import java.nio.file.Path
object DiagnosticClassGenerator {
fun generate(rootPath: Path, diagnosticList: DiagnosticList, packageName: String) {
val path = getGenerationPath(rootPath.toFile(), packageName)
KtDiagnosticClassRenderer.render(path.resolve("KtFirDiagnostics.kt"), diagnosticList, packageName)
KtDiagnosticClassImplementationRenderer.render(path.resolve("KtFirDiagnosticsImpl.kt"), diagnosticList, packageName)
FirDiagnosticToKtDiagnosticConverterRenderer.render(path.resolve("KtFirDataClassConverters.kt"), diagnosticList, packageName)
}
}
@@ -0,0 +1,30 @@
/*
* 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.analysis.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<HLDiagnosticParameter>,
)
data class HLDiagnosticList(val diagnostics: List<HLDiagnostic>)
data class HLDiagnosticParameter(
val original: DiagnosticParameter,
val name: String,
val type: KType,
val originalParameterName: String,
val conversion: HLParameterConversion,
val importsToAdd: List<String>
)
@@ -0,0 +1,347 @@
/*
* 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.analysis.api.fir.generator
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
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.FirSourceElement
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
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.lexer.KtKeywordToken
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.types.Variance
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.full.createType
import kotlin.reflect.full.isSubclassOf
object HLDiagnosticConverter {
@OptIn(ExperimentalStdlibApi::class)
fun convert(diagnosticList: DiagnosticList): HLDiagnosticList {
return HLDiagnosticList(diagnosticList.allDiagnostics.flatMap(::convertDiagnostic))
}
private fun convertDiagnostic(diagnostic: DiagnosticData): List<HLDiagnostic> {
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)
val convertedType = conversion.convertType(diagnosticParameter.type)
return HLDiagnosticParameter(
name = diagnosticParameter.name,
conversion = conversion,
originalParameterName = ('a' + index).toString(),
type = convertedType,
original = diagnosticParameter,
importsToAdd = conversion.importsToAdd
)
}
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 object FirToKtConversionCreator {
fun createConversion(type: KType): HLParameterConversion {
val nullable = type.isMarkedNullable
val kClass = type.classifier as KClass<*>
return tryMapAllowedType(kClass)
?: tryMapPsiElementType(kClass)
?: tryMapFirTypeToKtType(kClass, nullable)
?: tryMapPlatformType(type, kClass)
?: error("Unsupported type $type, consider add corresponding mapping")
}
private fun tryMapFirTypeToKtType(kClass: KClass<*>, nullable: Boolean): HLParameterConversion? {
return if (nullable) {
nullableTypeMapping[kClass] ?: typeMapping[kClass]
} else {
typeMapping[kClass]
}
}
private fun tryMapAllowedType(kClass: KClass<*>): HLParameterConversion? {
if (kClass in allowedTypesWithoutTypeParams) return HLIdParameterConversion
return null
}
private fun KType.toParameterName(): String {
return kClass.simpleName!!.replaceFirstChar(Char::lowercaseChar)
}
private fun tryMapPlatformType(type: KType, kClass: KClass<*>): HLParameterConversion? {
if (kClass.isSubclassOf(Collection::class)) {
val elementType = type.arguments.single().type ?: return HLIdParameterConversion
return HLCollectionParameterConversion(
parameterName = elementType.toParameterName(),
mappingConversion = createConversion(elementType)
)
}
if (kClass.isSubclassOf(Map::class)) {
val keyType = type.arguments.getOrNull(0)?.type
val valueType = type.arguments.getOrNull(1)?.type
val keyConversion = keyType?.let { createConversion(it) } ?: HLIdParameterConversion
val valueConversion = valueType?.let { createConversion(it) } ?: HLIdParameterConversion
if (keyConversion.isTrivial && valueConversion.isTrivial) return HLIdParameterConversion
return HLMapParameterConversion(
keyName = keyType?.toParameterName() ?: "key",
valueName = valueType?.toParameterName() ?: "value",
mappingConversionForKeys = keyConversion,
mappingConversionForValues = valueConversion
)
}
if (kClass.isSubclassOf(Pair::class)) {
val first = type.arguments.getOrNull(0)?.type ?: return HLIdParameterConversion
val second = type.arguments.getOrNull(1)?.type ?: return HLIdParameterConversion
return HLPairParameterConversion(
mappingConversionFirst = createConversion(first),
mappingConversionSecond = createConversion(second)
)
}
return null
}
private fun tryMapPsiElementType(kClass: KClass<*>): HLParameterConversion? {
if (kClass.isSubclassOf(PsiElement::class)) {
return HLIdParameterConversion
}
return null
}
private val nullableTypeMapping: Map<KClass<*>, HLFunctionCallConversion> = mapOf(
FirExpression::class to HLFunctionCallConversion(
"{0}?.source?.psi as? KtExpression",
KtExpression::class.createType(nullable = true),
importsToAdd = listOf(
"org.jetbrains.kotlin.psi.KtExpression",
"org.jetbrains.kotlin.fir.psi"
)
),
)
private val typeMapping: Map<KClass<*>, HLFunctionCallConversion> = mapOf(
FirBasedSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.buildSymbol({0}.fir)",
KtSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirDeclaration")
),
FirClass::class to HLFunctionCallConversion(
"firSymbolBuilder.classifierBuilder.buildClassLikeSymbol({0})",
KtClassLikeSymbol::class.createType()
),
FirClassSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.classifierBuilder.buildClassLikeSymbol({0}.fir)",
KtClassLikeSymbol::class.createType()
),
FirRegularClass::class to HLFunctionCallConversion(
"firSymbolBuilder.classifierBuilder.buildClassLikeSymbol({0}) as KtNamedClassOrObjectSymbol",
KtNamedClassOrObjectSymbol::class.createType(),
importsToAdd = listOf(
"org.jetbrains.kotlin.fir.declarations.FirRegularClass",
"org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol"
)
),
FirExpression::class to HLFunctionCallConversion(
"{0}.source!!.psi as KtExpression",
KtExpression::class.createType(),
importsToAdd = listOf(
"org.jetbrains.kotlin.psi.KtExpression",
"org.jetbrains.kotlin.fir.psi"
)
),
FirQualifiedAccess::class to HLFunctionCallConversion(
"{0}.source!!.psi as KtExpression",
KtExpression::class.createType(),
importsToAdd = listOf(
"org.jetbrains.kotlin.psi.KtExpression",
"org.jetbrains.kotlin.fir.psi"
)
),
FirValueParameter::class to HLFunctionCallConversion(
"firSymbolBuilder.buildSymbol({0})",
KtSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirDeclaration")
),
FirValueParameterSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.buildSymbol({0}.fir)",
KtSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirDeclaration")
),
FirEnumEntrySymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.buildSymbol({0}.fir)",
KtSymbol::class.createType(),
),
FirClassLikeSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.classifierBuilder.buildClassLikeSymbol({0}.fir as FirClass)",
KtClassLikeSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirClass")
),
FirRegularClassSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.classifierBuilder.buildClassLikeSymbol({0}.fir)",
KtClassLikeSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirRegularClass")
),
FirMemberDeclaration::class to HLFunctionCallConversion(
"firSymbolBuilder.buildSymbol({0} as FirDeclaration)",
KtSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirDeclaration")
),
FirCallableDeclaration::class to HLFunctionCallConversion(
"firSymbolBuilder.callableBuilder.buildCallableSymbol({0} as FirCallableDeclaration)",
KtCallableSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration")
),
FirCallableSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.callableBuilder.buildCallableSymbol({0}.fir)",
KtCallableSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration")
),
FirTypeParameterSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.classifierBuilder.buildTypeParameterSymbol({0}.fir)",
KtTypeParameterSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirTypeParameter")
),
FirTypeParameter::class to HLFunctionCallConversion(
"firSymbolBuilder.classifierBuilder.buildTypeParameterSymbol({0}.fir)",
KtTypeParameterSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirTypeParameter")
),
ConeKotlinType::class to HLFunctionCallConversion(
"firSymbolBuilder.typeBuilder.buildKtType({0})",
KtType::class.createType()
),
FirTypeRef::class to HLFunctionCallConversion(
"firSymbolBuilder.typeBuilder.buildKtType({0})",
KtType::class.createType()
),
FirPropertySymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.variableLikeBuilder.buildVariableSymbol({0}.fir)",
KtVariableSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirProperty")
),
FirBackingFieldSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.variableLikeBuilder.buildVariableSymbol({0}.fir.propertySymbol.fir)",
KtVariableSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirProperty")
),
FirVariableSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.variableLikeBuilder.buildVariableLikeSymbol({0}.fir)",
KtVariableLikeSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirVariable")
),
FirDeclaration::class to HLFunctionCallConversion(
"firSymbolBuilder.buildSymbol({0})",
KtSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirDeclaration")
),
FirSimpleFunction::class to HLFunctionCallConversion(
"firSymbolBuilder.buildSymbol({0})",
KtSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirSimpleFunction")
),
FirNamedFunctionSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol({0}.fir)",
KtFunctionLikeSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirSimpleFunction")
),
FirSourceElement::class to HLFunctionCallConversion(
"({0} as FirPsiSourceElement).psi",
PsiElement::class.createType(),
importsToAdd = listOf(
"org.jetbrains.kotlin.fir.psi",
"org.jetbrains.kotlin.fir.FirPsiSourceElement"
)
)
)
private val allowedTypesWithoutTypeParams = setOf(
Boolean::class,
String::class,
Int::class,
Name::class,
EventOccurrencesRange::class,
KtKeywordToken::class,
KtModifierKeywordToken::class,
Visibility::class,
EffectiveVisibility::class,
WhenMissingCase::class,
ForbiddenNamedArgumentsTarget::class,
LanguageFeature::class,
LanguageVersionSettings::class,
Variance::class,
FqName::class,
ClassId::class,
FirModuleData::class,
ExpectActualCompatibility.Incompatible::class,
DeprecationInfo::class,
)
private val KType.kClass: KClass<*>
get() = classifier as KClass<*>
}
@@ -0,0 +1,144 @@
/*
* 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.analysis.api.fir.generator
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
import kotlin.reflect.KVariance
import kotlin.reflect.full.createType
sealed class HLParameterConversion {
abstract fun convertExpression(expression: String, context: ConversionContext): String
abstract fun convertType(type: KType): KType
open val importsToAdd: List<String> get() = emptyList()
}
object HLIdParameterConversion : HLParameterConversion() {
override fun convertExpression(expression: String, context: ConversionContext) = expression
override fun convertType(type: KType): KType = type
}
class HLCollectionParameterConversion(
private val parameterName: String,
private val mappingConversion: HLParameterConversion,
) : HLParameterConversion() {
override fun convertExpression(expression: String, context: ConversionContext): String {
val innerExpression = mappingConversion.convertExpression(parameterName, context.increaseIndent())
return buildString {
appendLine("$expression.map { $parameterName ->")
appendLine(innerExpression.withIndent(context.increaseIndent()))
append("}".withIndent(context))
}
}
override fun convertType(type: KType): KType =
List::class.createType(
arguments = listOf(
KTypeProjection(
variance = KVariance.INVARIANT,
type = type.arguments.single().type?.let(mappingConversion::convertType)
)
)
)
override val importsToAdd get() = mappingConversion.importsToAdd
}
class HLMapParameterConversion(
private val keyName: String,
private val valueName: String,
private val mappingConversionForKeys: HLParameterConversion,
private val mappingConversionForValues: HLParameterConversion,
) : HLParameterConversion() {
override fun convertExpression(expression: String, context: ConversionContext): String {
val keyTransformation = mappingConversionForKeys.convertExpression(keyName, context.increaseIndent())
val valueTransformation = mappingConversionForValues.convertExpression(valueName, context.increaseIndent())
return buildString {
appendLine("$expression.mapKeys { ($keyName, _) ->")
appendLine(keyTransformation.withIndent(context.increaseIndent()))
appendLine("}.mapValues { (_, $valueName) -> ".withIndent(context))
appendLine(valueTransformation.withIndent(context.increaseIndent()))
append("}".withIndent(context))
}
}
override fun convertType(type: KType): KType {
val keyArgument = type.arguments[0]
val valueArgument = type.arguments[1]
return Map::class.createType(
arguments = listOf(
KTypeProjection(
variance = KVariance.INVARIANT,
type = keyArgument.type?.let(mappingConversionForKeys::convertType)
),
KTypeProjection(
variance = KVariance.INVARIANT,
type = valueArgument.type?.let(mappingConversionForValues::convertType)
)
)
)
}
override val importsToAdd: List<String>
get() = (mappingConversionForKeys.importsToAdd + mappingConversionForValues.importsToAdd).distinct()
}
class HLPairParameterConversion(
private val mappingConversionFirst: HLParameterConversion,
private val mappingConversionSecond: HLParameterConversion,
) : HLParameterConversion() {
override fun convertExpression(expression: String, context: ConversionContext): String {
if (mappingConversionFirst.isTrivial && mappingConversionSecond.isTrivial) {
return expression
}
val first = mappingConversionFirst.convertExpression("$expression.first", context)
val second = mappingConversionSecond.convertExpression("$expression.second", context)
return "$first to $second"
}
override fun convertType(type: KType): KType {
val first = type.arguments.getOrNull(0)?.type ?: return type
val second = type.arguments.getOrNull(1)?.type ?: return type
return Pair::class.createType(
arguments = listOf(
KTypeProjection(
variance = KVariance.INVARIANT,
type = mappingConversionFirst.convertType(first)
),
KTypeProjection(
variance = KVariance.INVARIANT,
type = mappingConversionSecond.convertType(second)
)
)
)
}
override val importsToAdd
get() = mappingConversionFirst.importsToAdd + mappingConversionSecond.importsToAdd
}
class HLFunctionCallConversion(
private val callTemplate: String,
private val callType: KType,
override val importsToAdd: List<String> = emptyList()
) : HLParameterConversion() {
override fun convertExpression(expression: String, context: ConversionContext) =
callTemplate.replace("{0}", expression)
override fun convertType(type: KType): KType = callType
}
data class ConversionContext(val currentIndent: Int, val indentUnitValue: Int) {
fun increaseIndent() = copy(currentIndent = currentIndent + 1)
}
private fun String.withIndent(context: ConversionContext): String {
val newIndent = " ".repeat(context.currentIndent * context.indentUnitValue)
return replaceIndent(newIndent)
}
val HLParameterConversion.isTrivial: Boolean
get() = this is HLIdParameterConversion
@@ -0,0 +1,16 @@
/*
* 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.analysis.api.fir.generator
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.DIAGNOSTICS_LIST
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.JVM_DIAGNOSTICS_LIST
import java.nio.file.Paths
fun main() {
val rootPath = Paths.get("idea/idea-frontend-fir/src").toAbsolutePath()
val packageName = "org.jetbrains.kotlin.analysis.api.fir.diagnostics"
DiagnosticClassGenerator.generate(rootPath, DIAGNOSTICS_LIST + JVM_DIAGNOSTICS_LIST, packageName)
}
@@ -0,0 +1,35 @@
/*
* 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.analysis.api.fir.generator
import org.jetbrains.kotlin.util.SmartPrinter
import kotlin.reflect.KClass
import kotlin.reflect.KType
internal fun SmartPrinter.printTypeWithShortNames(type: KType, shouldRenderFqName: (KType) -> Boolean = { false }) {
fun typeConversion(type: KType): String {
val nullableSuffix = if (type.isMarkedNullable) "?" else ""
val simpleName = if (shouldRenderFqName(type)) {
type.qualifiedName
} else {
type.simpleName
}
return if (type.arguments.isEmpty()) simpleName + nullableSuffix
else simpleName + type.arguments.joinToString(separator = ", ", prefix = "<", postfix = ">") {
when (val typeArgument = it.type) {
null -> "*"
else -> typeConversion(typeArgument)
} + nullableSuffix
}
}
print(typeConversion(type))
}
val KType.simpleName: String
get() = (classifier as KClass<*>).simpleName!!
val KType.qualifiedName: String
get() = (classifier as KClass<*>).qualifiedName!!
@@ -0,0 +1,63 @@
/*
* 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.analysis.api.fir.generator.rendererrs
import org.jetbrains.kotlin.fir.checkers.generator.collectClassNamesTo
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticListRenderer
import org.jetbrains.kotlin.fir.checkers.generator.printImports
import org.jetbrains.kotlin.fir.tree.generator.printer.printCopyright
import org.jetbrains.kotlin.fir.tree.generator.printer.printGeneratedMessage
import org.jetbrains.kotlin.fir.tree.generator.util.writeToFileUsingSmartPrinterIfFileContentChanged
import org.jetbrains.kotlin.analysis.api.fir.generator.HLDiagnosticConverter
import org.jetbrains.kotlin.analysis.api.fir.generator.HLDiagnosticList
import org.jetbrains.kotlin.analysis.api.fir.generator.HLDiagnosticParameter
import org.jetbrains.kotlin.analysis.api.fir.generator.simpleName
import org.jetbrains.kotlin.util.SmartPrinter
import java.io.File
import kotlin.reflect.KType
abstract class AbstractDiagnosticsDataClassRenderer : DiagnosticListRenderer() {
override fun render(file: File, diagnosticList: DiagnosticList, packageName: String) {
val hlDiagnosticsList = HLDiagnosticConverter.convert(diagnosticList)
file.writeToFileUsingSmartPrinterIfFileContentChanged { render(hlDiagnosticsList, packageName) }
}
private fun SmartPrinter.collectAndPrintImports(diagnosticList: HLDiagnosticList) {
val imports = collectImports(diagnosticList)
printImports(imports)
}
protected fun SmartPrinter.printHeader(packageName: String, diagnosticList: HLDiagnosticList) {
printCopyright()
println("package $packageName")
println()
collectAndPrintImports(diagnosticList)
println()
printGeneratedMessage()
}
@OptIn(ExperimentalStdlibApi::class)
protected fun collectImports(diagnosticList: HLDiagnosticList): Collection<String> = buildSet {
addAll(defaultImports)
for (diagnostic in diagnosticList.diagnostics) {
diagnostic.original.psiType.collectClassNamesTo(this)
diagnostic.parameters.forEach { diagnosticParameter ->
addAll(collectImportsForDiagnosticParameter(diagnosticParameter))
}
}
}
protected fun HLDiagnosticList.containsClashingBySimpleNameType(type: KType): Boolean {
return diagnostics.any { it.className == type.simpleName }
}
protected abstract fun collectImportsForDiagnosticParameter(diagnosticParameter: HLDiagnosticParameter): Collection<String>
protected abstract fun SmartPrinter.render(diagnosticList: HLDiagnosticList, packageName: String)
protected abstract val defaultImports: Collection<String>
}
@@ -0,0 +1,75 @@
/*
* 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.analysis.api.fir.generator.rendererrs
import org.jetbrains.kotlin.fir.checkers.generator.inBracketsWithIndent
import org.jetbrains.kotlin.analysis.api.fir.generator.ConversionContext
import org.jetbrains.kotlin.analysis.api.fir.generator.HLDiagnostic
import org.jetbrains.kotlin.analysis.api.fir.generator.HLDiagnosticList
import org.jetbrains.kotlin.analysis.api.fir.generator.HLDiagnosticParameter
import org.jetbrains.kotlin.util.SmartPrinter
import org.jetbrains.kotlin.util.withIndent
object FirDiagnosticToKtDiagnosticConverterRenderer : AbstractDiagnosticsDataClassRenderer() {
override fun SmartPrinter.render(diagnosticList: HLDiagnosticList, packageName: String) {
printHeader(packageName, diagnosticList)
printDiagnosticConverter(diagnosticList)
}
private fun SmartPrinter.printDiagnosticConverter(diagnosticList: HLDiagnosticList) {
inBracketsWithIndent("internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConverter") {
for (diagnostic in diagnosticList.diagnostics) {
printConverter(diagnostic)
}
}
}
private fun SmartPrinter.printConverter(diagnostic: HLDiagnostic) {
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 {
printDiagnosticParameters(diagnostic)
}
println(")")
}
println("}")
}
private fun SmartPrinter.printDiagnosticParameters(diagnostic: HLDiagnostic) {
printCustomParameters(diagnostic)
println("firDiagnostic as FirPsiDiagnostic,")
println("token,")
}
private fun SmartPrinter.printCustomParameters(diagnostic: HLDiagnostic) {
diagnostic.parameters.forEach { parameter ->
printParameter(parameter)
}
}
private fun SmartPrinter.printParameter(parameter: HLDiagnosticParameter) {
val expression = parameter.conversion.convertExpression(
"firDiagnostic.${parameter.originalParameterName}",
ConversionContext(getCurrentIndentInUnits(), getIndentUnit())
)
println("$expression,")
}
override fun collectImportsForDiagnosticParameter(diagnosticParameter: HLDiagnosticParameter): Collection<String> =
diagnosticParameter.importsToAdd
override val defaultImports = listOf(
"org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic",
"org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors",
"org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors",
)
}
@@ -0,0 +1,69 @@
/*
* 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.analysis.api.fir.generator.rendererrs
import org.jetbrains.kotlin.fir.checkers.generator.collectClassNamesTo
import org.jetbrains.kotlin.fir.checkers.generator.inBracketsWithIndent
import org.jetbrains.kotlin.analysis.api.fir.generator.HLDiagnostic
import org.jetbrains.kotlin.analysis.api.fir.generator.HLDiagnosticList
import org.jetbrains.kotlin.analysis.api.fir.generator.HLDiagnosticParameter
import org.jetbrains.kotlin.analysis.api.fir.generator.printTypeWithShortNames
import org.jetbrains.kotlin.util.SmartPrinter
import org.jetbrains.kotlin.util.withIndent
object KtDiagnosticClassImplementationRenderer : AbstractDiagnosticsDataClassRenderer() {
override fun SmartPrinter.render(diagnosticList: HLDiagnosticList, packageName: String) {
printHeader(packageName, diagnosticList)
printDiagnosticClassesImplementation(diagnosticList)
}
private fun SmartPrinter.printDiagnosticClassesImplementation(diagnosticList: HLDiagnosticList) {
for (diagnostic in diagnosticList.diagnostics) {
printDiagnosticImplementation(diagnostic, diagnosticList)
println()
}
}
private fun SmartPrinter.printDiagnosticImplementation(diagnostic: HLDiagnostic, diagnosticList: HLDiagnosticList) {
println("internal class ${diagnostic.implClassName}(")
withIndent {
printParameters(diagnostic, diagnosticList)
}
print(") : KtFirDiagnostic.${diagnostic.className}(), KtAbstractFirDiagnostic<")
printTypeWithShortNames(diagnostic.original.psiType)
print(">")
inBracketsWithIndent {
println("override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)")
}
}
private fun SmartPrinter.printParameters(diagnostic: HLDiagnostic, diagnosticList: HLDiagnosticList) {
for (parameter in diagnostic.parameters) {
printParameter(parameter, diagnosticList)
}
println("firDiagnostic: FirPsiDiagnostic,")
println("override val token: ValidityToken,")
}
private fun SmartPrinter.printParameter(parameter: HLDiagnosticParameter, diagnosticList: HLDiagnosticList) {
print("override val ${parameter.name}: ")
printTypeWithShortNames(parameter.type) {
diagnosticList.containsClashingBySimpleNameType(it)
}
println(",")
}
@OptIn(ExperimentalStdlibApi::class)
override fun collectImportsForDiagnosticParameter(diagnosticParameter: HLDiagnosticParameter): Collection<String> = buildSet {
diagnosticParameter.type.collectClassNamesTo(this)
}
override val defaultImports = listOf(
"org.jetbrains.kotlin.analysis.api.fir.utils.weakRef",
"org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic",
"org.jetbrains.kotlin.analysis.api.tokens.ValidityToken",
)
}
@@ -0,0 +1,58 @@
/*
* 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.analysis.api.fir.generator.rendererrs
import org.jetbrains.kotlin.fir.checkers.generator.collectClassNamesTo
import org.jetbrains.kotlin.fir.checkers.generator.inBracketsWithIndent
import org.jetbrains.kotlin.analysis.api.fir.generator.*
import org.jetbrains.kotlin.analysis.api.fir.generator.printTypeWithShortNames
import org.jetbrains.kotlin.util.SmartPrinter
object KtDiagnosticClassRenderer : AbstractDiagnosticsDataClassRenderer() {
override fun SmartPrinter.render(diagnosticList: HLDiagnosticList, packageName: String) {
printHeader(packageName, diagnosticList)
printDiagnosticClasses(diagnosticList)
}
private fun SmartPrinter.printDiagnosticClasses(diagnosticList: HLDiagnosticList) {
inBracketsWithIndent("sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI>") {
for (diagnostic in diagnosticList.diagnostics) {
printDiagnosticClass(diagnostic, diagnosticList)
println()
}
}
}
private fun SmartPrinter.printDiagnosticClass(diagnostic: HLDiagnostic, diagnosticList: HLDiagnosticList) {
print("abstract class ${diagnostic.className} : KtFirDiagnostic<")
printTypeWithShortNames(diagnostic.original.psiType)
print(">()")
inBracketsWithIndent {
println("override val diagnosticClass get() = ${diagnostic.className}::class")
printDiagnosticParameters(diagnostic, diagnosticList)
}
}
private fun SmartPrinter.printDiagnosticParameters(diagnostic: HLDiagnostic, diagnosticList: HLDiagnosticList) {
diagnostic.parameters.forEach { parameter ->
print("abstract val ${parameter.name}: ")
printTypeWithShortNames(parameter.type) { type ->
diagnosticList.containsClashingBySimpleNameType(type)
}
println()
}
}
@OptIn(ExperimentalStdlibApi::class)
override fun collectImportsForDiagnosticParameter(diagnosticParameter: HLDiagnosticParameter): Collection<String> = buildSet {
diagnosticParameter.type.collectClassNamesTo(this)
}
override val defaultImports = listOf(
"org.jetbrains.kotlin.analysis.api.diagnostics.KtDiagnosticWithPsi",
"com.intellij.psi.PsiElement",
)
}