FIR: Decouple AbstractDiagnosticCollectorComponent and AbstractDiagnosticCollector

This commit is contained in:
Ilya Kirillov
2021-04-26 17:44:18 +02:00
parent e43835c9f5
commit 81a7271009
18 changed files with 221 additions and 189 deletions
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
import org.jetbrains.kotlin.fir.analysis.collectors.FirDiagnosticsCollector
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.resolve.ScopeSession
@@ -19,7 +20,7 @@ class FirCheckersResolveProcessor(
session: FirSession,
scopeSession: ScopeSession
) : FirTransformerBasedResolveProcessor(session, scopeSession) {
val diagnosticCollector = FirDiagnosticsCollector.create(session, scopeSession)
val diagnosticCollector: AbstractDiagnosticCollector = FirDiagnosticsCollector.create(session, scopeSession)
override val transformer: FirTransformer<Nothing?> = FirCheckersRunnerTransformer(diagnosticCollector)
}
@@ -30,7 +31,8 @@ class FirCheckersRunnerTransformer(private val diagnosticCollector: AbstractDiag
}
override fun transformFile(file: FirFile, data: Nothing?): FirDeclaration {
diagnosticCollector.collectDiagnostics(file)
val reporter = DiagnosticReporterFactory.createReporter()
diagnosticCollector.collectDiagnostics(file, reporter)
return file
}
}
@@ -19,33 +19,15 @@ import org.jetbrains.kotlin.fir.types.*
abstract class AbstractDiagnosticCollector(
override val session: FirSession,
override val scopeSession: ScopeSession = ScopeSession(),
protected val createComponents: (DiagnosticReporter) -> List<AbstractDiagnosticCollectorComponent>,
) : SessionHolder {
fun collectDiagnostics(firDeclaration: FirDeclaration): List<FirDiagnostic<*>> {
if (!componentsInitialized) {
throw IllegalStateException("Components are not initialized")
}
initializeCollector()
fun collectDiagnostics(firDeclaration: FirDeclaration, reporter: DiagnosticReporter) {
val components = createComponents(reporter)
val visitor = createVisitor(components)
firDeclaration.accept(visitor, null)
return getCollectedDiagnostics()
}
protected abstract fun initializeCollector()
protected abstract fun getCollectedDiagnostics(): List<FirDiagnostic<*>>
abstract val reporter: DiagnosticReporter
protected val components: MutableList<AbstractDiagnosticCollectorComponent> = mutableListOf()
private var componentsInitialized = false
protected abstract val visitor: CheckerRunningDiagnosticCollectorVisitor
fun initializeComponents(vararg components: AbstractDiagnosticCollectorComponent) {
if (componentsInitialized) {
throw IllegalStateException()
}
this.components += components
componentsInitialized = true
}
protected abstract fun createVisitor(components: List<AbstractDiagnosticCollectorComponent>): CheckerRunningDiagnosticCollectorVisitor
companion object {
const val SUPPRESS_ALL_INFOS = "infos"
@@ -66,14 +48,3 @@ abstract class AbstractDiagnosticCollector(
}
}
}
fun AbstractDiagnosticCollector.registerAllComponents() {
initializeComponents(
DeclarationCheckersDiagnosticComponent(this),
ExpressionCheckersDiagnosticComponent(this),
TypeCheckersDiagnosticComponent(this),
ErrorNodeDiagnosticCollectorComponent(this),
ControlFlowAnalysisDiagnosticComponent(this),
)
}
@@ -6,12 +6,20 @@
package org.jetbrains.kotlin.fir.analysis.collectors
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.collectors.components.DiagnosticComponentsFactory
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
import org.jetbrains.kotlin.fir.analysis.diagnostics.impl.DiagnosticReporterWithSuppress
import org.jetbrains.kotlin.fir.analysis.diagnostics.impl.SimpleDiagnosticReporter
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.resolve.ScopeSession
object FirDiagnosticsCollector {
fun create(session: FirSession, scopeSession: ScopeSession): SimpleDiagnosticsCollector {
val collector = SimpleDiagnosticsCollector(session, scopeSession)
collector.registerAllComponents()
return collector
fun create(
session: FirSession,
scopeSession: ScopeSession,
): SimpleDiagnosticsCollector {
return SimpleDiagnosticsCollector(session, scopeSession) { reporter ->
DiagnosticComponentsFactory.createAllDiagnosticComponents(session, reporter)
}
}
}
@@ -7,42 +7,29 @@ package org.jetbrains.kotlin.fir.analysis.collectors
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.PersistentCheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.components.AbstractDiagnosticCollectorComponent
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
import org.jetbrains.kotlin.fir.analysis.diagnostics.impl.BaseDiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.impl.DiagnosticReporterWithSuppress
import org.jetbrains.kotlin.fir.analysis.diagnostics.impl.SimpleDiagnosticReporter
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.SessionHolder
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorForFullBodyResolve
class SimpleDiagnosticsCollector(
session: FirSession,
scopeSession: ScopeSession,
private val disableSuppress: Boolean = false
) : AbstractDiagnosticCollector(session, scopeSession) {
override val visitor: CheckerRunningDiagnosticCollectorVisitor = CheckerRunningDiagnosticCollectorVisitor(
PersistentCheckerContext(
this,
ReturnTypeCalculatorForFullBodyResolve()
),
components
)
override var reporter = createDiagnosticReporter()
private set
private fun createDiagnosticReporter(): BaseDiagnosticReporter {
return if (disableSuppress) {
SimpleDiagnosticReporter()
} else {
DiagnosticReporterWithSuppress()
}
}
override fun initializeCollector() {
reporter = createDiagnosticReporter()
}
override fun getCollectedDiagnostics(): List<FirDiagnostic<*>> {
return reporter.diagnostics
createComponents: (DiagnosticReporter) -> List<AbstractDiagnosticCollectorComponent>,
) : AbstractDiagnosticCollector(session, scopeSession, createComponents) {
override fun createVisitor(components: List<AbstractDiagnosticCollectorComponent>): CheckerRunningDiagnosticCollectorVisitor {
return CheckerRunningDiagnosticCollectorVisitor(
PersistentCheckerContext(
this,
ReturnTypeCalculatorForFullBodyResolve()
),
components
)
}
}
@@ -12,11 +12,9 @@ import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.visitors.FirVisitor
abstract class AbstractDiagnosticCollectorComponent(private val collector: AbstractDiagnosticCollector) : FirVisitor<Unit, CheckerContext>() {
protected val session: FirSession = collector.session
abstract class AbstractDiagnosticCollectorComponent(
protected val session: FirSession,
protected val reporter: DiagnosticReporter,
) : FirVisitor<Unit, CheckerContext>() {
override fun visitElement(element: FirElement, data: CheckerContext) {}
protected val reporter: DiagnosticReporter
get() = collector.reporter
}
@@ -5,19 +5,22 @@
package org.jetbrains.kotlin.fir.analysis.collectors.components
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.cfa.FirControlFlowAnalyzer
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkersComponent
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
class ControlFlowAnalysisDiagnosticComponent(
collector: AbstractDiagnosticCollector,
declarationCheckers: DeclarationCheckers = collector.session.checkersComponent.declarationCheckers,
) : AbstractDiagnosticCollectorComponent(collector) {
session: FirSession,
reporter: DiagnosticReporter,
declarationCheckers: DeclarationCheckers = session.checkersComponent.declarationCheckers,
) : AbstractDiagnosticCollectorComponent(session, reporter) {
private val controlFlowAnalyzer = FirControlFlowAnalyzer(session, declarationCheckers)
// ------------------------------- Class initializer -------------------------------
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.collectors.components
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.CheckersComponentInternal
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
@@ -16,9 +17,10 @@ import org.jetbrains.kotlin.fir.declarations.*
@OptIn(CheckersComponentInternal::class)
class DeclarationCheckersDiagnosticComponent(
collector: AbstractDiagnosticCollector,
private val checkers: DeclarationCheckers = collector.session.checkersComponent.declarationCheckers,
) : AbstractDiagnosticCollectorComponent(collector) {
session: FirSession,
reporter: DiagnosticReporter,
private val checkers: DeclarationCheckers = session.checkersComponent.declarationCheckers,
) : AbstractDiagnosticCollectorComponent(session, reporter) {
override fun visitFile(file: FirFile, data: CheckerContext) {
checkers.allFileCheckers.check(file, data, reporter)
@@ -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.fir.analysis.collectors.components
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
object DiagnosticComponentsFactory {
fun createAllDiagnosticComponents(session: FirSession, reporter: DiagnosticReporter): List<AbstractDiagnosticCollectorComponent> {
return listOf(
DeclarationCheckersDiagnosticComponent(session, reporter),
ExpressionCheckersDiagnosticComponent(session, reporter),
TypeCheckersDiagnosticComponent(session, reporter),
ErrorNodeDiagnosticCollectorComponent(session, reporter),
ControlFlowAnalysisDiagnosticComponent(session, reporter),
)
}
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.collectors.components
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
@@ -23,7 +24,10 @@ import org.jetbrains.kotlin.fir.types.ConeClassErrorType
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
class ErrorNodeDiagnosticCollectorComponent(collector: AbstractDiagnosticCollector) : AbstractDiagnosticCollectorComponent(collector) {
class ErrorNodeDiagnosticCollectorComponent(
session: FirSession,
reporter: DiagnosticReporter,
) : AbstractDiagnosticCollectorComponent(session, reporter) {
override fun visitErrorLoop(errorLoop: FirErrorLoop, data: CheckerContext) {
val source = errorLoop.source ?: return
reportFirDiagnostic(errorLoop.diagnostic, source, reporter, data)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.collectors.components
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.CheckersComponentInternal
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
@@ -17,9 +18,10 @@ import org.jetbrains.kotlin.fir.expressions.*
@OptIn(CheckersComponentInternal::class)
class ExpressionCheckersDiagnosticComponent(
collector: AbstractDiagnosticCollector,
private val checkers: ExpressionCheckers = collector.session.checkersComponent.expressionCheckers,
) : AbstractDiagnosticCollectorComponent(collector) {
session: FirSession,
reporter: DiagnosticReporter,
private val checkers: ExpressionCheckers = session.checkersComponent.expressionCheckers,
) : AbstractDiagnosticCollectorComponent(session, reporter) {
override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: CheckerContext) {
checkers.allAnonymousFunctionAsExpressionCheckers.check(anonymousFunction, data, reporter)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.collectors.components
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.CheckersComponentInternal
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.type.FirTypeChecker
@@ -16,9 +17,10 @@ import org.jetbrains.kotlin.fir.types.*
@OptIn(CheckersComponentInternal::class)
class TypeCheckersDiagnosticComponent(
collector: AbstractDiagnosticCollector,
private val checkers: TypeCheckers = collector.session.checkersComponent.typeCheckers,
) : AbstractDiagnosticCollectorComponent(collector) {
session: FirSession,
reporter: DiagnosticReporter,
private val checkers: TypeCheckers = session.checkersComponent.typeCheckers,
) : AbstractDiagnosticCollectorComponent(session, reporter) {
override fun visitDynamicTypeRef(dynamicTypeRef: FirDynamicTypeRef, data: CheckerContext) {
checkers.allTypeRefCheckers.check(dynamicTypeRef, data, reporter)
@@ -0,0 +1,20 @@
/*
* 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.fir.analysis.diagnostics
import org.jetbrains.kotlin.fir.analysis.diagnostics.impl.BaseDiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.impl.DiagnosticReporterWithSuppress
import org.jetbrains.kotlin.fir.analysis.diagnostics.impl.SimpleDiagnosticReporter
object DiagnosticReporterFactory {
fun createReporter(disableSuppress: Boolean = false): BaseDiagnosticReporter {
return if (disableSuppress) {
SimpleDiagnosticReporter()
} else {
DiagnosticReporterWithSuppress()
}
}
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.collectors.FirDiagnosticsCollector
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
import org.jetbrains.kotlin.fir.backend.Fir2IrConverter
import org.jetbrains.kotlin.fir.backend.Fir2IrResult
@@ -78,7 +79,9 @@ class FirAnalyzerFacade(
val collector = FirDiagnosticsCollector.create(session, scopeSession)
collectedDiagnostics = buildMap {
for (file in firFiles!!) {
put(file, collector.collectDiagnostics(file))
val reporter = DiagnosticReporterFactory.createReporter()
collector.collectDiagnostics(file, reporter)
put(file, reporter.diagnostics)
}
}
return collectedDiagnostics!!
@@ -268,7 +268,9 @@ abstract class AbstractFirDiagnosticsTest : AbstractFirBaseDiagnosticsTest() {
for (firFile in firFiles) {
val session = firFile.declarationSiteSession
val collector = collectors.computeIfAbsent(session) { createCollector(session) }
result[firFile] = collector.collectDiagnostics(firFile).toList()
val reporter = DiagnosticReporterFactory.createReporter()
collector.collectDiagnostics(firFile, reporter)
result[firFile] = reporter.diagnostics
}
return result
}
@@ -5,122 +5,72 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.SessionConfiguration
import org.jetbrains.kotlin.fir.analysis.CheckersComponent
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.type.TypeCheckers
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
import org.jetbrains.kotlin.fir.analysis.collectors.components.*
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.checkers.*
internal abstract class AbstractFirIdeDiagnosticsCollector(
session: FirSession,
useExtendedCheckers: Boolean,
) : AbstractDiagnosticCollector(
session
) {
init {
val declarationCheckers = CheckersFactory.createDeclarationCheckers(useExtendedCheckers)
val expressionCheckers = CheckersFactory.createExpressionCheckers(useExtendedCheckers)
val typeCheckers = CheckersFactory.createTypeCheckers(useExtendedCheckers)
@Suppress("LeakingThis")
initializeComponents(
DeclarationCheckersDiagnosticComponent(this, declarationCheckers),
ExpressionCheckersDiagnosticComponent(this, expressionCheckers),
TypeCheckersDiagnosticComponent(this, typeCheckers),
ErrorNodeDiagnosticCollectorComponent(this),
ControlFlowAnalysisDiagnosticComponent(this, declarationCheckers),
)
session,
createComponents = { reporter ->
CheckersFactory.createComponents(session, reporter, useExtendedCheckers)
}
protected abstract fun onDiagnostic(diagnostic: FirPsiDiagnostic<*>)
)
private inner class Reporter : DiagnosticReporter() {
override fun report(diagnostic: FirDiagnostic<*>?, context: CheckerContext) {
if (diagnostic == null) return
if (context.isDiagnosticSuppressed(diagnostic)) return
private object CheckersFactory {
fun createComponents(
session: FirSession,
reporter: DiagnosticReporter,
useExtendedCheckers: Boolean
): List<AbstractDiagnosticCollectorComponent> {
val declarationCheckers = createDeclarationCheckers(useExtendedCheckers)
val expressionCheckers = createExpressionCheckers(useExtendedCheckers)
val typeCheckers = createTypeCheckers(useExtendedCheckers)
val psiDiagnostic = when (diagnostic) {
is FirPsiDiagnostic<*> -> diagnostic
is FirLightDiagnostic -> diagnostic.toPsiDiagnostic()
else -> error("Unknown diagnostic type ${diagnostic::class.simpleName}")
}
onDiagnostic(psiDiagnostic)
return if (useExtendedCheckers) {
listOf(
DeclarationCheckersDiagnosticComponent(session, reporter, declarationCheckers),
ExpressionCheckersDiagnosticComponent(session, reporter, expressionCheckers),
TypeCheckersDiagnosticComponent(session, reporter, typeCheckers),
ControlFlowAnalysisDiagnosticComponent(session, reporter, declarationCheckers),
)
} else {
listOf(
DeclarationCheckersDiagnosticComponent(session, reporter, declarationCheckers),
ExpressionCheckersDiagnosticComponent(session, reporter, expressionCheckers),
TypeCheckersDiagnosticComponent(session, reporter, typeCheckers),
ErrorNodeDiagnosticCollectorComponent(session, reporter),
ControlFlowAnalysisDiagnosticComponent(session, reporter, declarationCheckers),
)
}
}
override var reporter: DiagnosticReporter = Reporter()
override fun initializeCollector() {
reporter = Reporter()
}
override fun getCollectedDiagnostics(): List<FirDiagnostic<*>> {
// Not necessary in IDE
return emptyList()
}
}
private fun FirLightDiagnostic.toPsiDiagnostic(): FirPsiDiagnostic<*> {
val psiSourceElement = element.unwrapToFirPsiSourceElement()
?: error("Diagnostic should be created from PSI in IDE")
@Suppress("UNCHECKED_CAST")
return when (this) {
is FirLightSimpleDiagnostic -> FirPsiSimpleDiagnostic(
psiSourceElement,
severity,
factory as FirDiagnosticFactory0<PsiElement>
)
is FirLightDiagnosticWithParameters1<*> -> FirPsiDiagnosticWithParameters1(
psiSourceElement,
a,
severity,
factory as FirDiagnosticFactory1<PsiElement, Any>
)
is FirLightDiagnosticWithParameters2<*, *> -> FirPsiDiagnosticWithParameters2(
psiSourceElement,
a, b,
severity,
factory as FirDiagnosticFactory2<PsiElement, Any, Any>
)
is FirLightDiagnosticWithParameters3<*, *, *> -> FirPsiDiagnosticWithParameters3(
psiSourceElement,
a, b, c,
severity,
factory as FirDiagnosticFactory3<PsiElement, Any, Any, Any>
)
else -> error("Unknown diagnostic type ${this::class.simpleName}")
}
}
private object CheckersFactory {
private val extendedDeclarationCheckers = createDeclarationCheckers(ExtendedDeclarationCheckers)
private val commonDeclarationCheckers = createDeclarationCheckers(CommonDeclarationCheckers)
fun createDeclarationCheckers(useExtendedCheckers: Boolean): DeclarationCheckers =
private val commonDeclarationCheckers = createDeclarationCheckers(CommonDeclarationCheckers,)
private fun createDeclarationCheckers(useExtendedCheckers: Boolean): DeclarationCheckers =
if (useExtendedCheckers) extendedDeclarationCheckers else commonDeclarationCheckers
fun createExpressionCheckers(useExtendedCheckers: Boolean): ExpressionCheckers =
private fun createExpressionCheckers(useExtendedCheckers: Boolean): ExpressionCheckers =
if (useExtendedCheckers) ExtendedExpressionCheckers else CommonExpressionCheckers
fun createTypeCheckers(useExtendedCheckers: Boolean): TypeCheckers = CommonTypeCheckers
private fun createTypeCheckers(useExtendedCheckers: Boolean): TypeCheckers = CommonTypeCheckers
// TODO hack to have all checkers present in DeclarationCheckers.memberDeclarationCheckers and similar
// If use ExtendedDeclarationCheckers directly when DeclarationCheckers.memberDeclarationCheckers will not contain basicDeclarationCheckers
@OptIn(SessionConfiguration::class)
private fun createDeclarationCheckers(declarationCheckers: DeclarationCheckers): DeclarationCheckers =
CheckersComponent().apply { register(declarationCheckers) }.declarationCheckers
private fun createDeclarationCheckers(vararg declarationCheckers: DeclarationCheckers): DeclarationCheckers =
CheckersComponent().apply { declarationCheckers.forEach(::register) }.declarationCheckers
}
@@ -19,13 +19,14 @@ internal class FileStructureElementDiagnosticsCollector private constructor(priv
fun collectForStructureElement(
firDeclaration: FirDeclaration,
createVisitor: (components: List<AbstractDiagnosticCollectorComponent>) -> CheckerRunningDiagnosticCollectorVisitor,
): FileStructureElementDiagnosticList =
FirIdeStructureElementDiagnosticsCollector(
): FileStructureElementDiagnosticList {
val reporter = FirIdeDiagnosticReporter()
val collector = FirIdeStructureElementDiagnosticsCollector(
firDeclaration.declarationSiteSession,
createVisitor,
useExtendedCheckers,
).let { collector ->
collector.collectDiagnostics(firDeclaration)
FileStructureElementDiagnosticList(collector.result)
}
)
collector.collectDiagnostics(firDeclaration, reporter)
return FileStructureElementDiagnosticList(reporter.diagnostics)
}
}
@@ -0,0 +1,62 @@
/*
* 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.idea.fir.low.level.api.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.idea.fir.low.level.api.util.addValueFor
internal class FirIdeDiagnosticReporter : DiagnosticReporter() {
val diagnostics = mutableMapOf<PsiElement, MutableList<FirPsiDiagnostic<*>>>()
override fun report(diagnostic: FirDiagnostic<*>?, context: CheckerContext) {
if (diagnostic == null) return
if (context.isDiagnosticSuppressed(diagnostic)) return
val psiDiagnostic = when (diagnostic) {
is FirPsiDiagnostic<*> -> diagnostic
is FirLightDiagnostic -> diagnostic.toPsiDiagnostic()
else -> error("Unknown diagnostic type ${diagnostic::class.simpleName}")
}
diagnostics.addValueFor(psiDiagnostic.psiElement, psiDiagnostic)
}
}
private fun FirLightDiagnostic.toPsiDiagnostic(): FirPsiDiagnostic<*> {
val psiSourceElement = element.unwrapToFirPsiSourceElement()
?: error("Diagnostic should be created from PSI in IDE")
@Suppress("UNCHECKED_CAST")
return when (this) {
is FirLightSimpleDiagnostic -> FirPsiSimpleDiagnostic(
psiSourceElement,
severity,
factory as FirDiagnosticFactory0<PsiElement>
)
is FirLightDiagnosticWithParameters1<*> -> FirPsiDiagnosticWithParameters1(
psiSourceElement,
a,
severity,
factory as FirDiagnosticFactory1<PsiElement, Any>
)
is FirLightDiagnosticWithParameters2<*, *> -> FirPsiDiagnosticWithParameters2(
psiSourceElement,
a, b,
severity,
factory as FirDiagnosticFactory2<PsiElement, Any, Any>
)
is FirLightDiagnosticWithParameters3<*, *, *> -> FirPsiDiagnosticWithParameters3(
psiSourceElement,
a, b, c,
severity,
factory as FirDiagnosticFactory3<PsiElement, Any, Any, Any>
)
else -> error("Unknown diagnostic type ${this::class.simpleName}")
}
}
@@ -5,27 +5,20 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.fir
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.analysis.collectors.components.AbstractDiagnosticCollectorComponent
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.AbstractFirIdeDiagnosticsCollector
import org.jetbrains.kotlin.idea.fir.low.level.api.util.addValueFor
internal class FirIdeStructureElementDiagnosticsCollector(
session: FirSession,
createVisitor: (components: List<AbstractDiagnosticCollectorComponent>) -> CheckerRunningDiagnosticCollectorVisitor,
private val doCreateVisitor: (components: List<AbstractDiagnosticCollectorComponent>) -> CheckerRunningDiagnosticCollectorVisitor,
useExtendedCheckers: Boolean,
) : AbstractFirIdeDiagnosticsCollector(
session,
useExtendedCheckers,
) {
override val visitor: CheckerRunningDiagnosticCollectorVisitor = createVisitor(components)
val result = mutableMapOf<PsiElement, MutableList<FirPsiDiagnostic<*>>>()
override fun onDiagnostic(diagnostic: FirPsiDiagnostic<*>) {
result.addValueFor(diagnostic.psiElement, diagnostic)
override fun createVisitor(components: List<AbstractDiagnosticCollectorComponent>): CheckerRunningDiagnosticCollectorVisitor {
return doCreateVisitor(components)
}
}