K2: Extract CheckerContextForProvider from CheckerContext

So, the idea is that CheckerContext should remain just read-only
while the one who runs the checkers might build/modify the context.

^KT-56460 Related
This commit is contained in:
Denis.Zharkov
2023-02-07 11:28:22 +01:00
committed by Space Team
parent d24092e467
commit a311d6eea0
11 changed files with 116 additions and 82 deletions
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveCompone
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.fir.PersistenceContextCollector
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.fir.PersistentCheckerContextFactory
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContextForProvider
import org.jetbrains.kotlin.fir.analysis.collectors.DiagnosticCollectorComponents
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.SessionHolderImpl
@@ -46,7 +46,7 @@ internal class SingleNonLocalDeclarationDiagnosticRetriever(
private class Visitor(
private val structureElementDeclaration: FirDeclaration,
context: CheckerContext,
context: CheckerContextForProvider,
components: DiagnosticCollectorComponents
) : LLFirDiagnosticVisitor(context, components) {
private var insideAlwaysVisitableDeclarations = 0
@@ -5,15 +5,15 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkCanceled
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContextForProvider
import org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.analysis.collectors.DiagnosticCollectorComponents
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
internal open class LLFirDiagnosticVisitor(
context: CheckerContext,
context: CheckerContextForProvider,
components: DiagnosticCollectorComponents,
) : CheckerRunningDiagnosticCollectorVisitor(context, components) {
private val beforeElementDiagnosticCollectionHandler = context.session.beforeElementDiagnosticCollectionHandler
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithFil
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignation
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirElementWithResolvePhase
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContextForProvider
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.*
@@ -23,8 +23,8 @@ private class ContextCollectingDiagnosticCollectorVisitor private constructor(
) : AbstractDiagnosticCollectorVisitor(
PersistentCheckerContextFactory.createEmptyPersistenceCheckerContext(sessionHolder)
) {
private val contextCollector = object : ContextByDesignationCollector<CheckerContext>(designation) {
override fun getCurrentContext(): CheckerContext = context
private val contextCollector = object : ContextByDesignationCollector<CheckerContextForProvider>(designation) {
override fun getCurrentContext(): CheckerContextForProvider = context
override fun goToNestedDeclaration(target: FirElementWithResolvePhase) {
target.accept(this@ContextCollectingDiagnosticCollectorVisitor, null)
@@ -42,7 +42,7 @@ private class ContextCollectingDiagnosticCollectorVisitor private constructor(
override fun checkElement(element: FirElement) {}
companion object {
fun collect(sessionHolder: SessionHolder, designation: FirDesignationWithFile): CheckerContext {
fun collect(sessionHolder: SessionHolder, designation: FirDesignationWithFile): CheckerContextForProvider {
val visitor = ContextCollectingDiagnosticCollectorVisitor(sessionHolder, designation)
designation.firFile.accept(visitor, null)
return visitor.contextCollector.getCollectedContext()
@@ -55,7 +55,7 @@ internal object PersistenceContextCollector {
sessionHolder: SessionHolder,
firFile: FirFile,
declaration: FirDeclaration,
): CheckerContext {
): CheckerContextForProvider {
val isLocal = when (declaration) {
is FirClassLikeDeclaration -> declaration.symbol.classId.isLocal
is FirCallableDeclaration -> declaration.symbol.callableId.isLocal
@@ -6,8 +6,8 @@
package org.jetbrains.kotlin.fir.analysis.checkers.context
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticContext
import org.jetbrains.kotlin.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.diagnostics.MutableDiagnosticContext
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
@@ -19,11 +19,12 @@ import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStack
import org.jetbrains.kotlin.fir.resolve.SessionHolder
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.name.Name
abstract class CheckerContext : MutableDiagnosticContext() {
/**
* This class is assumed to be read-only (all the modifications are assumed to happen on CheckerContextForProvider side)
*/
abstract class CheckerContext : DiagnosticContext {
// Services
abstract val sessionHolder: SessionHolder
abstract val returnTypeCalculator: ReturnTypeCalculator
@@ -45,37 +46,6 @@ abstract class CheckerContext : MutableDiagnosticContext() {
val session: FirSession
get() = sessionHolder.session
abstract fun addImplicitReceiver(name: Name?, value: ImplicitReceiverValue<*>): CheckerContext
abstract fun addDeclaration(declaration: FirDeclaration): CheckerContext
abstract fun dropDeclaration()
fun <T> withDeclaration(declaration: FirDeclaration, f: (CheckerContext) -> T): T {
val newContext = addDeclaration(declaration)
try {
return f(newContext)
} finally {
newContext.dropDeclaration()
}
}
abstract fun addQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall: FirStatement): CheckerContext
abstract fun dropQualifiedAccessOrAnnotationCall()
abstract fun addGetClassCall(getClassCall: FirGetClassCall): CheckerContext
abstract fun dropGetClassCall()
abstract fun addAnnotationContainer(annotationContainer: FirAnnotationContainer): CheckerContext
abstract fun dropAnnotationContainer()
abstract fun enterContractBody(): CheckerContext
abstract fun exitContractBody(): CheckerContext
override fun isDiagnosticSuppressed(diagnostic: KtDiagnostic): Boolean {
val factory = diagnostic.factory
val name = factory.name
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2023 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.checkers.context
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.SessionHolder
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.name.Name
/**
* This class should be invisible for checkers, but should be only used by DiagnosticCollectorVisitor who runs those checkers
* It contains all context-modification-related methods unlike CheckerContext that is assumed to be read-only
*/
abstract class CheckerContextForProvider(
sessionHolder: SessionHolder,
returnTypeCalculator: ReturnTypeCalculator,
allInfosSuppressed: Boolean,
allWarningsSuppressed: Boolean,
allErrorsSuppressed: Boolean
) : AbstractCheckerContext(sessionHolder, returnTypeCalculator, allInfosSuppressed, allWarningsSuppressed, allErrorsSuppressed) {
abstract fun addSuppressedDiagnostics(
diagnosticNames: Collection<String>,
allInfosSuppressed: Boolean,
allWarningsSuppressed: Boolean,
allErrorsSuppressed: Boolean
): CheckerContextForProvider
abstract fun addImplicitReceiver(name: Name?, value: ImplicitReceiverValue<*>): CheckerContextForProvider
abstract fun addDeclaration(declaration: FirDeclaration): CheckerContextForProvider
abstract fun dropDeclaration()
fun <T> withDeclaration(declaration: FirDeclaration, f: (CheckerContextForProvider) -> T): T {
val newContext = addDeclaration(declaration)
try {
return f(newContext)
} finally {
newContext.dropDeclaration()
}
}
abstract fun addQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall: FirStatement): CheckerContextForProvider
abstract fun dropQualifiedAccessOrAnnotationCall()
abstract fun addGetClassCall(getClassCall: FirGetClassCall): CheckerContextForProvider
abstract fun dropGetClassCall()
abstract fun addAnnotationContainer(annotationContainer: FirAnnotationContainer): CheckerContextForProvider
abstract fun dropAnnotationContainer()
abstract fun enterContractBody(): CheckerContextForProvider
abstract fun exitContractBody(): CheckerContextForProvider
}
@@ -30,7 +30,7 @@ class MutableCheckerContext private constructor(
allInfosSuppressed: Boolean,
allWarningsSuppressed: Boolean,
allErrorsSuppressed: Boolean
) : AbstractCheckerContext(sessionHolder, returnTypeCalculator, allInfosSuppressed, allWarningsSuppressed, allErrorsSuppressed) {
) : CheckerContextForProvider(sessionHolder, returnTypeCalculator, allInfosSuppressed, allWarningsSuppressed, allErrorsSuppressed) {
constructor(sessionHolder: SessionHolder, returnTypeCalculator: ReturnTypeCalculator) : this(
PersistentImplicitReceiverStack(),
mutableListOf(),
@@ -90,7 +90,7 @@ class MutableCheckerContext private constructor(
getClassCalls.removeAt(getClassCalls.size - 1)
}
override fun addAnnotationContainer(annotationContainer: FirAnnotationContainer): CheckerContext {
override fun addAnnotationContainer(annotationContainer: FirAnnotationContainer): CheckerContextForProvider {
annotationContainers.add(annotationContainer)
return this
}
@@ -104,7 +104,7 @@ class MutableCheckerContext private constructor(
allInfosSuppressed: Boolean,
allWarningsSuppressed: Boolean,
allErrorsSuppressed: Boolean
): MutableCheckerContext {
): CheckerContextForProvider {
if (diagnosticNames.isEmpty()) return this
return MutableCheckerContext(
implicitReceiverStack,
@@ -122,13 +122,13 @@ class MutableCheckerContext private constructor(
)
}
override fun enterContractBody(): CheckerContext {
override fun enterContractBody(): CheckerContextForProvider {
check(!isContractBody)
isContractBody = true
return this
}
override fun exitContractBody(): CheckerContext {
override fun exitContractBody(): CheckerContextForProvider {
check(isContractBody)
isContractBody = false
return this
@@ -32,7 +32,7 @@ class PersistentCheckerContext private constructor(
allInfosSuppressed: Boolean,
allWarningsSuppressed: Boolean,
allErrorsSuppressed: Boolean
) : AbstractCheckerContext(sessionHolder, returnTypeCalculator, allInfosSuppressed, allWarningsSuppressed, allErrorsSuppressed) {
) : CheckerContextForProvider(sessionHolder, returnTypeCalculator, allInfosSuppressed, allWarningsSuppressed, allErrorsSuppressed) {
constructor(sessionHolder: SessionHolder, returnTypeCalculator: ReturnTypeCalculator) : this(
PersistentImplicitReceiverStack(),
persistentListOf(),
@@ -150,7 +150,7 @@ class PersistentCheckerContext private constructor(
allInfosSuppressed: Boolean,
allWarningsSuppressed: Boolean,
allErrorsSuppressed: Boolean
): PersistentCheckerContext {
): CheckerContextForProvider {
if (diagnosticNames.isEmpty()) return this
return PersistentCheckerContext(
implicitReceiverStack,
@@ -168,7 +168,7 @@ class PersistentCheckerContext private constructor(
)
}
private fun toggleContractBody(newValue: Boolean): CheckerContext {
private fun toggleContractBody(newValue: Boolean): CheckerContextForProvider {
check(isContractBody != newValue)
return PersistentCheckerContext(
@@ -187,11 +187,11 @@ class PersistentCheckerContext private constructor(
)
}
override fun enterContractBody(): CheckerContext {
override fun enterContractBody(): CheckerContextForProvider {
return toggleContractBody(newValue = true)
}
override fun exitContractBody(): CheckerContext {
override fun exitContractBody(): CheckerContextForProvider {
return toggleContractBody(newValue = false)
}
}
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContextForProvider
import org.jetbrains.kotlin.fir.analysis.checkers.getModifier
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineOnly
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
@@ -72,9 +73,7 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
context.session,
reporter
)
context.withDeclaration(function) {
body.checkChildrenWithCustomVisitor(it, visitor)
}
body.checkChildrenWithCustomVisitor(context, visitor, function)
}
open val inlineVisitor get() = ::BasicInlineVisitor
@@ -538,13 +537,21 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
private fun FirElement.checkChildrenWithCustomVisitor(
parentContext: CheckerContext,
visitorVoid: FirVisitor<Unit, CheckerContext>
visitorVoid: FirVisitor<Unit, CheckerContext>,
rootFunction: FirFunction,
) {
val collectingVisitor = object : AbstractDiagnosticCollectorVisitor(parentContext) {
override fun checkElement(element: FirElement) {
element.accept(visitorVoid, context)
}
// TODO: Get rid of this cast and the following context modification as it looks like a leaking abstraction (see KT-56460)
require(parentContext is CheckerContextForProvider) {
"This checked violates the contract for read-only checkers"
}
parentContext.withDeclaration(rootFunction) {
val collectingVisitor = object : AbstractDiagnosticCollectorVisitor(it) {
override fun checkElement(element: FirElement) {
element.accept(visitorVoid, context)
}
}
this.accept(collectingVisitor, null)
}
this.accept(collectingVisitor, null)
}
}
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContextForProvider
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildReceiverParameter
import org.jetbrains.kotlin.fir.expressions.*
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.fir.whileAnalysing
import org.jetbrains.kotlin.name.Name
abstract class AbstractDiagnosticCollectorVisitor(
@set:PrivateForInline var context: CheckerContext,
@set:PrivateForInline var context: CheckerContextForProvider,
) : FirDefaultVisitor<Unit, Nothing?>() {
protected open fun shouldVisitDeclaration(declaration: FirDeclaration) = true
@@ -393,6 +393,6 @@ abstract class AbstractDiagnosticCollectorVisitor(
allInfosSuppressed = AbstractDiagnosticCollector.SUPPRESS_ALL_INFOS in arguments,
allWarningsSuppressed = AbstractDiagnosticCollector.SUPPRESS_ALL_WARNINGS in arguments,
allErrorsSuppressed = AbstractDiagnosticCollector.SUPPRESS_ALL_ERRORS in arguments
) as CheckerContext
)
}
}
@@ -6,13 +6,12 @@
package org.jetbrains.kotlin.fir.analysis.collectors
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.components.AbstractDiagnosticCollectorComponent
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContextForProvider
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
open class CheckerRunningDiagnosticCollectorVisitor(
context: CheckerContext,
context: CheckerContextForProvider,
protected val components: DiagnosticCollectorComponents
) : AbstractDiagnosticCollectorVisitor(context) {
@@ -16,15 +16,6 @@ interface DiagnosticContext {
val languageVersionSettings: LanguageVersionSettings
}
abstract class MutableDiagnosticContext : DiagnosticContext {
abstract fun addSuppressedDiagnostics(
diagnosticNames: Collection<String>,
allInfosSuppressed: Boolean,
allWarningsSuppressed: Boolean,
allErrorsSuppressed: Boolean
): DiagnosticContext
}
abstract class DiagnosticReporter {
abstract fun report(diagnostic: KtDiagnostic?, context: DiagnosticContext)
@@ -97,4 +88,4 @@ open class KtDiagnosticReporterWithContext(
return result
}
}
}
}