[FIR] Properly generate IDE diagnostics from multiple diagnostics lists

This commit is contained in:
Dmitriy Novozhilov
2021-05-20 13:00:51 +03:00
committed by teamcityserver
parent 09e3629d5d
commit a1ae108ce5
25 changed files with 107 additions and 52 deletions
+1
View File
@@ -297,6 +297,7 @@ extra["compilerModules"] = arrayOf(
":compiler:fir:java",
":compiler:fir:jvm",
":compiler:fir:checkers",
":compiler:fir:checkers:checkers.jvm",
":compiler:fir:entrypoint",
":compiler:fir:analysis-tests",
":compiler:fir:analysis-tests:legacy-fir-tests",
+1
View File
@@ -30,6 +30,7 @@ dependencies {
compile(project(":compiler:fir:fir2ir"))
compile(project(":compiler:fir:fir2ir:jvm-backend"))
compile(project(":compiler:fir:checkers"))
compile(project(":compiler:fir:checkers:checkers.jvm"))
compile(project(":kotlin-util-klib"))
compile(project(":kotlin-util-io"))
@@ -20,6 +20,7 @@ dependencies {
testApi(projectTests(":compiler:tests-common-new"))
testApi(project(":compiler:cli"))
testApi(project(":compiler:fir:checkers"))
testApi(project(":compiler:fir:checkers:checkers.jvm"))
testApi(project(":compiler:fir:fir-serialization"))
testApi(project(":compiler:fir:entrypoint"))
testApi(project(":compiler:frontend"))
@@ -20,6 +20,7 @@ dependencies {
testCompileOnly(project(":kotlin-test:kotlin-test-junit"))
testApi(projectTests(":compiler:tests-common"))
testApi(project(":compiler:fir:checkers"))
testApi(project(":compiler:fir:checkers:checkers.jvm"))
testApi(project(":compiler:fir:entrypoint"))
testApi(project(":compiler:frontend"))
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.fir.checkers.generator
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.DIAGNOSTICS_LIST
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.FIR_JVM_DIAGNOSTICS_LIST
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.JVM_DIAGNOSTICS_LIST
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.generateDiagnostics
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
@@ -75,8 +75,8 @@ fun main(args: Array<String>) {
val jvmGenerationPath = File(arguments.getOrElse(1) { "compiler/fir/checkers/checkers.jvm/gen" })
generateDiagnostics(generationPath, "$basePackage.diagnostics", "FirErrors", DIAGNOSTICS_LIST)
generateDiagnostics(jvmGenerationPath, "$basePackage.diagnostics.jvm", "FirJvmErrors", FIR_JVM_DIAGNOSTICS_LIST)
generateDiagnostics(generationPath, "$basePackage.diagnostics", DIAGNOSTICS_LIST)
generateDiagnostics(jvmGenerationPath, "$basePackage.diagnostics.jvm", JVM_DIAGNOSTICS_LIST)
}
/*
@@ -30,7 +30,7 @@ import kotlin.properties.ReadOnlyProperty
@Suppress("UNUSED_VARIABLE", "LocalVariableName", "ClassName", "unused")
@OptIn(PrivateForInline::class)
object DIAGNOSTICS_LIST : DiagnosticList() {
object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val MetaErrors by object : DiagnosticGroup("Meta-errors") {
val UNSUPPORTED by error<PsiElement> {
parameter<String>("unsupported")
@@ -908,15 +908,15 @@ private val exposedVisibilityDiagnosticInit: DiagnosticBuilder.() -> Unit = {
parameter<EffectiveVisibility>("restrictingVisibility")
}
private inline fun <reified P : PsiElement> DiagnosticGroup.exposedVisibilityError(
private inline fun <reified P : PsiElement> AbstractDiagnosticGroup.exposedVisibilityError(
positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT
): PropertyDelegateProvider<Any?, ReadOnlyProperty<DiagnosticGroup, DiagnosticData>> {
): PropertyDelegateProvider<Any?, ReadOnlyProperty<AbstractDiagnosticGroup, DiagnosticData>> {
return error<P>(positioningStrategy, exposedVisibilityDiagnosticInit)
}
private inline fun <reified P : PsiElement> DiagnosticGroup.exposedVisibilityWarning(
private inline fun <reified P : PsiElement> AbstractDiagnosticGroup.exposedVisibilityWarning(
positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT
): PropertyDelegateProvider<Any?, ReadOnlyProperty<DiagnosticGroup, DiagnosticData>> {
): PropertyDelegateProvider<Any?, ReadOnlyProperty<AbstractDiagnosticGroup, DiagnosticData>> {
return warning<P>(positioningStrategy, exposedVisibilityDiagnosticInit)
}
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.*
@Suppress("UNUSED_VARIABLE", "LocalVariableName", "ClassName", "unused")
@OptIn(PrivateForInline::class)
object FIR_JVM_DIAGNOSTICS_LIST : DiagnosticList() {
object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") {
val DECLARATIONS by object : DiagnosticGroup("Declarations") {
val CONFLICTING_JVM_DECLARATIONS by error<PsiElement>()
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.diagnostics.Severity
import kotlin.reflect.KType
data class DiagnosticData(
val containingObjectName: String,
val severity: Severity,
val name: String,
val psiType: KType,
@@ -12,7 +12,7 @@ import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.typeOf
abstract class DiagnosticGroup @PrivateForInline constructor(val name: String) {
abstract class AbstractDiagnosticGroup @PrivateForInline constructor(val name: String, internal val containingObjectName: String) {
@Suppress("PropertyName")
@PrivateForInline
val _diagnostics = mutableListOf<DiagnosticData>()
@@ -22,26 +22,27 @@ abstract class DiagnosticGroup @PrivateForInline constructor(val name: String) {
get() = _diagnostics
@OptIn(PrivateForInline::class)
inline fun <reified P : PsiElement> error(
internal inline fun <reified P : PsiElement> error(
positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT,
crossinline init: DiagnosticBuilder.() -> Unit = {}
) = diagnosticDelegateProvider<P>(Severity.ERROR, positioningStrategy, init)
@OptIn(PrivateForInline::class)
inline fun <reified P : PsiElement> warning(
internal inline fun <reified P : PsiElement> warning(
positioningStrategy: PositioningStrategy = PositioningStrategy.DEFAULT,
crossinline init: DiagnosticBuilder.() -> Unit = {}
) = diagnosticDelegateProvider<P>(Severity.WARNING, positioningStrategy, init)
@PrivateForInline
@OptIn(ExperimentalStdlibApi::class)
inline fun <reified P : PsiElement> diagnosticDelegateProvider(
internal inline fun <reified P : PsiElement> diagnosticDelegateProvider(
severity: Severity,
positioningStrategy: PositioningStrategy,
crossinline init: DiagnosticBuilder.() -> Unit = {}
) = PropertyDelegateProvider<Any?, ReadOnlyProperty<DiagnosticGroup, DiagnosticData>> { _, property ->
) = PropertyDelegateProvider<Any?, ReadOnlyProperty<AbstractDiagnosticGroup, DiagnosticData>> { _, property ->
val diagnostic = DiagnosticBuilder(
containingObjectName,
severity,
name = property.name,
psiType = typeOf<P>(),
@@ -50,4 +51,15 @@ abstract class DiagnosticGroup @PrivateForInline constructor(val name: String) {
_diagnostics += diagnostic
ReadOnlyProperty { _, _ -> diagnostic }
}
@OptIn(PrivateForInline::class)
operator fun plus(other: AbstractDiagnosticGroup): AbstractDiagnosticGroup {
require(name == other.name)
return object : AbstractDiagnosticGroup(name, "#Stub") {
init {
_diagnostics.addAll(this.diagnostics)
_diagnostics.addAll(other.diagnostics)
}
}
}
}
@@ -12,15 +12,16 @@ import kotlin.reflect.KProperty
import kotlin.reflect.KType
import kotlin.reflect.typeOf
abstract class DiagnosticList {
abstract class DiagnosticList(internal val objectName: String) {
@Suppress("PropertyName")
@PrivateForInline
val _groups = mutableListOf<DiagnosticGroup>()
val _groups = mutableListOf<AbstractDiagnosticGroup>()
@OptIn(PrivateForInline::class)
val groups: List<DiagnosticGroup>
val groups: List<AbstractDiagnosticGroup>
get() = _groups
@OptIn(PrivateForInline::class)
val allDiagnostics: List<DiagnosticData>
get() = groups.flatMap { it.diagnostics }
@@ -34,9 +35,38 @@ abstract class DiagnosticList {
_groups += group
return ReadOnlyProperty { _, _ -> group }
}
@OptIn(PrivateForInline::class)
operator fun plus(other: DiagnosticList): DiagnosticList {
val groupsByName = mutableMapOf<String, MutableList<AbstractDiagnosticGroup>>()
fun collect(groups: List<AbstractDiagnosticGroup>) {
for (group in groups) {
val list = groupsByName.getOrPut(group.name) { mutableListOf() }
list += group
}
}
collect(groups)
collect(other.groups)
val resultingGroups = groupsByName.values.map {
it.reduce { acc, group -> acc + group }
}
return object : DiagnosticList("#Stub") {
init {
_groups.addAll(resultingGroups)
}
}
}
@PrivateForInline
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,
@@ -58,6 +88,7 @@ class DiagnosticBuilder(
@OptIn(PrivateForInline::class)
fun build() = DiagnosticData(
containingObjectName,
severity,
name,
psiType,
@@ -8,12 +8,11 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model
import org.jetbrains.kotlin.fir.checkers.generator.getGenerationPath
import java.io.File
fun generateDiagnostics(rootPath: File, packageName: String, containingObjectName: String, diagnosticList: DiagnosticList) {
fun generateDiagnostics(rootPath: File, packageName: String, diagnosticList: DiagnosticList) {
val generationPath = getGenerationPath(rootPath, packageName)
ErrorListDiagnosticListRenderer.render(
generationPath.resolve("$containingObjectName.kt"),
generationPath.resolve("${diagnosticList.objectName}.kt"),
diagnosticList,
packageName,
containingObjectName
packageName
)
}
@@ -8,5 +8,5 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model
import java.io.File
abstract class DiagnosticListRenderer {
abstract fun render(file: File, diagnosticList: DiagnosticList, packageName: String, containingObjectName: String)
abstract fun render(file: File, diagnosticList: DiagnosticList, packageName: String)
}
@@ -18,23 +18,23 @@ import kotlin.reflect.KTypeProjection
object ErrorListDiagnosticListRenderer : DiagnosticListRenderer() {
private const val BASE_PACKAGE = "org.jetbrains.kotlin.fir.analysis.diagnostics"
override fun render(file: File, diagnosticList: DiagnosticList, packageName: String, containingObjectName: String) {
override fun render(file: File, diagnosticList: DiagnosticList, packageName: String) {
file.writeToFileUsingSmartPrinterIfFileContentChanged {
render(diagnosticList, packageName, containingObjectName)
render(diagnosticList, packageName)
}
}
private fun SmartPrinter.render(diagnosticList: DiagnosticList, packageName: String, containingObjectName: String) {
private fun SmartPrinter.render(diagnosticList: DiagnosticList, packageName: String) {
printCopyright()
println("package $packageName")
println()
collectAndPrintImports(diagnosticList, packageName)
printGeneratedMessage()
printErrorsObject(diagnosticList, containingObjectName)
printErrorsObject(diagnosticList)
}
private fun SmartPrinter.printErrorsObject(diagnosticList: DiagnosticList, containingObjectName: String) {
inBracketsWithIndent("object $containingObjectName") {
private fun SmartPrinter.printErrorsObject(diagnosticList: DiagnosticList) {
inBracketsWithIndent("object ${diagnosticList.objectName}") {
for (group in diagnosticList.groups) {
printDiagnosticGroup(group.name, group.diagnostics)
println()
+1
View File
@@ -25,6 +25,7 @@ dependencies {
testCompile(project(":compiler:fir:cones"))
testCompile(project(":compiler:fir:resolve"))
testCompile(project(":compiler:fir:checkers"))
testCompile(project(":compiler:fir:checkers:checkers.jvm"))
testCompile(project(":compiler:fir:java"))
testCompile(project(":compiler:fir:entrypoint"))
testCompile(project(":compiler:ir.ir2cfg"))
+1
View File
@@ -69,6 +69,7 @@ dependencies {
compile(project(":compiler:fir:fir2ir"))
compile(project(":compiler:fir:resolve"))
compile(project(":compiler:fir:checkers"))
compile(project(":compiler:fir:checkers:checkers.jvm"))
compile(project(":compiler:fir:java"))
compile(project(":compiler:fir:jvm"))
compile(project(":idea:idea-core"))
+1
View File
@@ -24,6 +24,7 @@ dependencies {
compile(project(":compiler:fir:fir2ir"))
compile(project(":compiler:fir:resolve"))
compile(project(":compiler:fir:checkers"))
compile(project(":compiler:fir:checkers:checkers.jvm"))
compile(project(":compiler:fir:java"))
compile(project(":compiler:fir:jvm"))
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
@@ -15,25 +15,8 @@ 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,
"KtFirDiagnostics"
)
KtDiagnosticClassImplementationRenderer.render(
path.resolve("KtFirDiagnosticsImpl.kt"),
diagnosticList,
packageName,
"KtFirDiagnosticsImpl"
)
FirDiagnosticToKtDiagnosticConverterRenderer.render(
path.resolve("KtFirDataClassConverters.kt"),
diagnosticList,
packageName,
"KtFirDataClassConverters"
)
KtDiagnosticClassRenderer.render(path.resolve("KtFirDiagnostics.kt"), diagnosticList, packageName)
KtDiagnosticClassImplementationRenderer.render(path.resolve("KtFirDiagnosticsImpl.kt"), diagnosticList, packageName)
FirDiagnosticToKtDiagnosticConverterRenderer.render(path.resolve("KtFirDataClassConverters.kt"), diagnosticList, packageName)
}
}
@@ -6,10 +6,11 @@
package org.jetbrains.kotlin.idea.frontend.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.idea.frontend.api.fir.diagnostics"
DiagnosticClassGenerator.generate(rootPath, DIAGNOSTICS_LIST, packageName)
}
DiagnosticClassGenerator.generate(rootPath, DIAGNOSTICS_LIST + JVM_DIAGNOSTICS_LIST, packageName)
}
@@ -19,7 +19,7 @@ import org.jetbrains.kotlin.util.SmartPrinter
import java.io.File
abstract class AbstractDiagnosticsDataClassRenderer : DiagnosticListRenderer() {
override fun render(file: File, diagnosticList: DiagnosticList, packageName: String, containingObjectName: String) {
override fun render(file: File, diagnosticList: DiagnosticList, packageName: String) {
val hlDiagnosticsList = HLDiagnosticConverter.convert(diagnosticList)
file.writeToFileUsingSmartPrinterIfFileContentChanged { render(hlDiagnosticsList, packageName) }
}
@@ -25,7 +25,7 @@ object FirDiagnosticToKtDiagnosticConverterRenderer : AbstractDiagnosticsDataCla
}
private fun SmartPrinter.printConverter(diagnostic: HLDiagnostic) {
println("add(FirErrors.${diagnostic.original.name}) { firDiagnostic ->")
println("add(${diagnostic.original.containingObjectName}.${diagnostic.original.name}) { firDiagnostic ->")
withIndent {
println("${diagnostic.implClassName}(")
withIndent {
@@ -63,5 +63,6 @@ object FirDiagnosticToKtDiagnosticConverterRenderer : AbstractDiagnosticsDataCla
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",
)
}
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiTypeElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
@@ -2490,4 +2491,10 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirJvmErrors.CONFLICTING_JVM_DECLARATIONS) { firDiagnostic ->
ConflictingJvmDeclarationsImpl(
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
}
@@ -1743,4 +1743,8 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val symbol: KtSymbol
}
abstract class ConflictingJvmDeclarations : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = ConflictingJvmDeclarations::class
}
}
@@ -2833,3 +2833,10 @@ internal class SuperCallFromPublicInlineImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class ConflictingJvmDeclarationsImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.ConflictingJvmDeclarations(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
@@ -20,6 +20,7 @@ dependencies {
testCompileOnly(project(":kotlin-test:kotlin-test-junit"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(project(":compiler:fir:checkers"))
testCompile(project(":compiler:fir:checkers:checkers.jvm"))
testCompile(projectTests(":compiler:fir:analysis-tests:legacy-fir-tests"))
testCompile(project(":compiler:frontend"))
+1
View File
@@ -46,6 +46,7 @@ val projectsToShadow by extra(listOf(
":compiler:psi",
":compiler:fir:cones",
":compiler:fir:checkers",
":compiler:fir:checkers:checkers.jvm",
":compiler:fir:entrypoint",
":compiler:fir:resolve",
":compiler:fir:fir-serialization",