K2 Scripting: add FirScript to declaration checkers

This commit is contained in:
Ilya Chernikov
2023-11-21 13:47:17 +01:00
committed by Space Team
parent b7057eb22f
commit 8c30f3b2bc
8 changed files with 60 additions and 34 deletions
@@ -79,6 +79,7 @@ fun main(args: Array<String>) {
alias<FirRegularClass>("RegularClassChecker")
alias<FirConstructor>("ConstructorChecker")
alias<FirFile>("FileChecker")
alias<FirScript>("ScriptChecker")
alias<FirTypeParameter>("FirTypeParameterChecker")
alias<FirTypeAlias>("TypeAliasChecker")
alias<FirAnonymousFunction>("AnonymousFunctionChecker")
@@ -35,6 +35,8 @@ class ComposedDeclarationCheckers : DeclarationCheckers() {
get() = _constructorCheckers
override val fileCheckers: Set<FirFileChecker>
get() = _fileCheckers
override val scriptCheckers: Set<FirScriptChecker>
get() = _scriptCheckers
override val typeParameterCheckers: Set<FirTypeParameterChecker>
get() = _typeParameterCheckers
override val typeAliasCheckers: Set<FirTypeAliasChecker>
@@ -68,6 +70,7 @@ class ComposedDeclarationCheckers : DeclarationCheckers() {
private val _regularClassCheckers: MutableSet<FirRegularClassChecker> = mutableSetOf()
private val _constructorCheckers: MutableSet<FirConstructorChecker> = mutableSetOf()
private val _fileCheckers: MutableSet<FirFileChecker> = mutableSetOf()
private val _scriptCheckers: MutableSet<FirScriptChecker> = mutableSetOf()
private val _typeParameterCheckers: MutableSet<FirTypeParameterChecker> = mutableSetOf()
private val _typeAliasCheckers: MutableSet<FirTypeAliasChecker> = mutableSetOf()
private val _anonymousFunctionCheckers: MutableSet<FirAnonymousFunctionChecker> = mutableSetOf()
@@ -92,6 +95,7 @@ class ComposedDeclarationCheckers : DeclarationCheckers() {
_regularClassCheckers += checkers.regularClassCheckers
_constructorCheckers += checkers.constructorCheckers
_fileCheckers += checkers.fileCheckers
_scriptCheckers += checkers.scriptCheckers
_typeParameterCheckers += checkers.typeParameterCheckers
_typeAliasCheckers += checkers.typeAliasCheckers
_anonymousFunctionCheckers += checkers.anonymousFunctionCheckers
@@ -29,6 +29,7 @@ abstract class DeclarationCheckers {
open val regularClassCheckers: Set<FirRegularClassChecker> = emptySet()
open val constructorCheckers: Set<FirConstructorChecker> = emptySet()
open val fileCheckers: Set<FirFileChecker> = emptySet()
open val scriptCheckers: Set<FirScriptChecker> = emptySet()
open val typeParameterCheckers: Set<FirTypeParameterChecker> = emptySet()
open val typeAliasCheckers: Set<FirTypeAliasChecker> = emptySet()
open val anonymousFunctionCheckers: Set<FirAnonymousFunctionChecker> = emptySet()
@@ -52,6 +53,7 @@ abstract class DeclarationCheckers {
@CheckersComponentInternal internal val allRegularClassCheckers: Set<FirRegularClassChecker> by lazy { regularClassCheckers + classCheckers + classLikeCheckers + basicDeclarationCheckers }
@CheckersComponentInternal internal val allConstructorCheckers: Set<FirConstructorChecker> by lazy { constructorCheckers + functionCheckers + callableDeclarationCheckers + basicDeclarationCheckers }
@CheckersComponentInternal internal val allFileCheckers: Set<FirFileChecker> by lazy { fileCheckers + basicDeclarationCheckers }
@CheckersComponentInternal internal val allScriptCheckers: Set<FirScriptChecker> by lazy { scriptCheckers + basicDeclarationCheckers }
@CheckersComponentInternal internal val allTypeParameterCheckers: Set<FirTypeParameterChecker> by lazy { typeParameterCheckers + basicDeclarationCheckers }
@CheckersComponentInternal internal val allTypeAliasCheckers: Set<FirTypeAliasChecker> by lazy { typeAliasCheckers + classLikeCheckers + basicDeclarationCheckers }
@CheckersComponentInternal internal val allAnonymousFunctionCheckers: Set<FirAnonymousFunctionChecker> by lazy { anonymousFunctionCheckers + functionCheckers + callableDeclarationCheckers + basicDeclarationCheckers }
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirScript
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
@@ -40,6 +41,7 @@ typealias FirClassChecker = FirDeclarationChecker<FirClass>
typealias FirRegularClassChecker = FirDeclarationChecker<FirRegularClass>
typealias FirConstructorChecker = FirDeclarationChecker<FirConstructor>
typealias FirFileChecker = FirDeclarationChecker<FirFile>
typealias FirScriptChecker = FirDeclarationChecker<FirScript>
typealias FirTypeParameterChecker = FirDeclarationChecker<FirTypeParameter>
typealias FirTypeAliasChecker = FirDeclarationChecker<FirTypeAlias>
typealias FirAnonymousFunctionChecker = FirDeclarationChecker<FirAnonymousFunction>
@@ -157,6 +157,11 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirTopLevelPropertiesChecker,
)
override val scriptCheckers: Set<FirScriptChecker>
get() = setOf(
FirScriptPropertiesChecker
)
override val controlFlowAnalyserCheckers: Set<FirControlFlowChecker>
get() = setOf(
FirCallsEffectAnalyzer,
@@ -34,44 +34,50 @@ import org.jetbrains.kotlin.lexer.KtTokens
// See old FE's [DeclarationsChecker]
object FirTopLevelPropertiesChecker : FirFileChecker() {
override fun check(declaration: FirFile, context: CheckerContext, reporter: DiagnosticReporter) {
val topLevelProperties = declaration.effectiveTopLevelProperties
val info = declaration.collectionInitializationInfo(topLevelProperties, context, reporter)
for (topLevelProperty in topLevelProperties) {
val symbol = topLevelProperty.symbol
val isDefinitelyAssigned = info?.get(symbol)?.isDefinitelyVisited() == true
checkProperty(containingDeclaration = null, topLevelProperty, isDefinitelyAssigned, context, reporter, reachable = true)
}
val topLevelProperties = declaration.declarations.filterIsInstance<FirProperty>()
checkFileLikeDeclaration(declaration, topLevelProperties, context, reporter)
}
}
/**
* Scripts are nested as a single declaration under [FirFile]s and contain their own statements. To properly check all "top-level"
* properties, script statements need to be unwrapped.
*/
private val FirFile.effectiveTopLevelProperties: List<FirProperty>
get() = when (val script = declarations.singleOrNull()) {
is FirScript -> script.statements.filterIsInstance<FirProperty>()
else -> declarations.filterIsInstance<FirProperty>()
}
private fun FirFile.collectionInitializationInfo(
topLevelProperties: List<FirProperty>,
context: CheckerContext,
reporter: DiagnosticReporter,
): PropertyInitializationInfo? {
val graph = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return null
val propertySymbols = topLevelProperties.mapNotNullTo(mutableSetOf()) { declaration ->
declaration.symbol.takeIf { it.requiresInitialization(isForInitialization = true) }
}
if (propertySymbols.isEmpty()) return null
// TODO, KT-59803: merge with `FirPropertyInitializationAnalyzer` for fewer passes.
val data = PropertyInitializationInfoData(propertySymbols, receiver = null, graph)
data.checkPropertyAccesses(isForInitialization = true, context, reporter)
return data.getValue(graph.exitNode)[NormalPath]
object FirScriptPropertiesChecker : FirScriptChecker() {
override fun check(declaration: FirScript, context: CheckerContext, reporter: DiagnosticReporter) {
val topLevelProperties = declaration.statements.filterIsInstance<FirProperty>()
checkFileLikeDeclaration(declaration, topLevelProperties, context, reporter)
}
}
private fun checkFileLikeDeclaration(
declaration: FirDeclaration,
topLevelProperties: List<FirProperty>,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val info = declaration.collectionInitializationInfo(topLevelProperties, context, reporter)
for (topLevelProperty in topLevelProperties) {
val symbol = topLevelProperty.symbol
val isDefinitelyAssigned = info?.get(symbol)?.isDefinitelyVisited() == true
checkProperty(containingDeclaration = null, topLevelProperty, isDefinitelyAssigned, context, reporter, reachable = true)
}
}
private fun FirDeclaration.collectionInitializationInfo(
topLevelProperties: List<FirProperty>,
context: CheckerContext,
reporter: DiagnosticReporter,
): PropertyInitializationInfo? {
val graph = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return null
val propertySymbols = topLevelProperties.mapNotNullTo(mutableSetOf()) { declaration ->
declaration.symbol.takeIf { it.requiresInitialization(isForInitialization = true) }
}
if (propertySymbols.isEmpty()) return null
// TODO, KT-59803: merge with `FirPropertyInitializationAnalyzer` for fewer passes.
val data = PropertyInitializationInfoData(propertySymbols, receiver = null, graph)
data.checkPropertyAccesses(isForInitialization = true, context, reporter)
return data.getValue(graph.exitNode)[NormalPath]
}
// Matched FE 1.0's [DeclarationsChecker#checkPropertyInitializer].
internal fun checkPropertyInitializer(
containingClass: FirClass?,
@@ -44,6 +44,12 @@ class ControlFlowAnalysisDiagnosticComponent(
analyze(file, data)
}
// ------------------------------- File -------------------------------
override fun visitScript(script: FirScript, data: CheckerContext) {
analyze(script, data)
}
// ------------------------------- Class initializer -------------------------------
override fun visitRegularClass(regularClass: FirRegularClass, data: CheckerContext) {
@@ -108,7 +108,7 @@ class DeclarationCheckersDiagnosticComponent(
}
override fun visitScript(script: FirScript, data: CheckerContext) {
checkers.allBasicDeclarationCheckers.check(script, data)
checkers.allScriptCheckers.check(script, data)
}
override fun visitCodeFragment(codeFragment: FirCodeFragment, data: CheckerContext) {