[FIR] Split :compiler:fir:resolve module into three different modules
Those modules are:
- :compiler:fir:providers, which contains Fir and Symbol providers,
scopes, and different utilities used by them
- :compiler:fir:semantics, which contains different abstractions and
entities which are used in resolution and in checkers
- :compiler:fir:resolve, which contains all stuff related to resolution
and inference
There are two pros of this change:
1. It may increase gradle build, because it allows to compile :fir:resolve
and :fir:checkers modules in parallel
2. Logic of working FIR (scopes, providers, DFA logic system, etc) is
now separated from logic of resolution phases, so for example checkers,
which are depend on scopes physically will not be able to run resolve
in any way
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":compiler:fir:providers"))
|
||||
implementation(project(":core:util.runtime"))
|
||||
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
compileOnly(intellijCoreDep()) { includeJars("guava", rootProject = rootProject) }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
|
||||
fun Visibility.toEffectiveVisibility(
|
||||
ownerSymbol: FirClassLikeSymbol<*>?,
|
||||
forClass: Boolean = false,
|
||||
checkPublishedApi: Boolean = false
|
||||
): EffectiveVisibility {
|
||||
return toEffectiveVisibility(ownerSymbol?.toLookupTag(), forClass, checkPublishedApi)
|
||||
}
|
||||
|
||||
fun Visibility.toEffectiveVisibility(
|
||||
owner: ConeClassLikeLookupTag?,
|
||||
forClass: Boolean = false,
|
||||
ownerIsPublishedApi: Boolean = false
|
||||
): EffectiveVisibility {
|
||||
customEffectiveVisibility()?.let { return it }
|
||||
return when (this.normalize()) {
|
||||
Visibilities.PrivateToThis, Visibilities.InvisibleFake -> EffectiveVisibility.PrivateInClass
|
||||
Visibilities.Private -> if (owner == null && forClass) EffectiveVisibility.PrivateInFile else EffectiveVisibility.PrivateInClass
|
||||
Visibilities.Protected -> EffectiveVisibility.Protected(owner)
|
||||
Visibilities.Internal -> when (ownerIsPublishedApi) {
|
||||
true -> EffectiveVisibility.Public
|
||||
false -> EffectiveVisibility.Internal
|
||||
}
|
||||
Visibilities.Public -> EffectiveVisibility.Public
|
||||
Visibilities.Local -> EffectiveVisibility.Local
|
||||
else -> error("Unknown visibility: $this")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCallInfo
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
|
||||
abstract class FirLookupTrackerComponent : FirSessionComponent {
|
||||
|
||||
abstract fun recordLookup(name: Name, inScopes: List<String>, source: FirSourceElement?, fileSource: FirSourceElement?)
|
||||
|
||||
abstract fun recordLookup(name: Name, inScope: String, source: FirSourceElement?, fileSource: FirSourceElement?)
|
||||
}
|
||||
|
||||
fun FirLookupTrackerComponent.recordCallLookup(callInfo: AbstractCallInfo, inType: ConeKotlinType) {
|
||||
if (inType.classId?.isLocal == true) return
|
||||
val scopes = SmartList(inType.render().replace('/', '.'))
|
||||
if (inType.classId?.shortClassName?.asString() == "Companion") {
|
||||
scopes.add(inType.classId!!.outerClassId!!.asString().replace('/', '.'))
|
||||
}
|
||||
recordLookup(callInfo.name, scopes, callInfo.callSite.source, callInfo.containingFile.source)
|
||||
}
|
||||
|
||||
fun FirLookupTrackerComponent.recordCallLookup(callInfo: AbstractCallInfo, inScopes: List<String>) {
|
||||
recordLookup(callInfo.name, inScopes, callInfo.callSite.source, callInfo.containingFile.source)
|
||||
}
|
||||
|
||||
fun FirLookupTrackerComponent.recordTypeLookup(typeRef: FirTypeRef, inScopes: List<String>, fileSource: FirSourceElement?) {
|
||||
if (typeRef is FirUserTypeRef) recordLookup(typeRef.qualifier.first().name, inScopes, typeRef.source, fileSource)
|
||||
}
|
||||
|
||||
fun FirLookupTrackerComponent.recordTypeResolveAsLookup(typeRef: FirTypeRef, source: FirSourceElement?, fileSource: FirSourceElement?) {
|
||||
if (typeRef !is FirResolvedTypeRef) return // TODO: check if this is the correct behavior
|
||||
if (source == null && fileSource == null) return // TODO: investigate all cases
|
||||
|
||||
fun recordIfValid(type: ConeKotlinType) {
|
||||
if (type is ConeKotlinErrorType) return // TODO: investigate whether some cases should be recorded, e.g. unresolved
|
||||
type.classId?.let {
|
||||
if (!it.isLocal) {
|
||||
if (it.shortClassName.asString() != "Companion") {
|
||||
recordLookup(it.shortClassName, it.packageFqName.asString(), source, fileSource)
|
||||
} else {
|
||||
recordLookup(it.outerClassId!!.shortClassName, it.outerClassId!!.packageFqName.asString(), source, fileSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
type.typeArguments.forEach {
|
||||
if (it is ConeKotlinType) recordIfValid(it)
|
||||
}
|
||||
}
|
||||
|
||||
recordIfValid(typeRef.type)
|
||||
}
|
||||
|
||||
|
||||
val FirSession.lookupTracker: FirLookupTrackerComponent? by FirSession.nullableSessionComponentAccessor()
|
||||
+20
@@ -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
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
abstract class FirNameConflictsTrackerComponent : FirSessionComponent {
|
||||
abstract fun registerClassifierRedeclaration(
|
||||
classId: ClassId,
|
||||
newSymbol: FirClassLikeSymbol<*>, newSymbolFile: FirFile,
|
||||
prevSymbol: FirClassLikeSymbol<*>, prevSymbolFile: FirFile?
|
||||
)
|
||||
}
|
||||
|
||||
val FirSession.nameConflictsTracker: FirNameConflictsTrackerComponent? by FirSession.nullableSessionComponentAccessor()
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.visibility
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
|
||||
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
|
||||
|
||||
/**
|
||||
* Returns the list of functions that overridden by given
|
||||
*/
|
||||
fun FirSimpleFunction.lowestVisibilityAmongOverrides(
|
||||
containingClass: FirClass,
|
||||
session: FirSession,
|
||||
scopeSession: ScopeSession
|
||||
): Visibility {
|
||||
val firTypeScope = containingClass.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false)
|
||||
var visibility = visibility
|
||||
|
||||
// required; otherwise processOverriddenFunctions()
|
||||
// will process nothing
|
||||
firTypeScope.processFunctionsByName(symbol.fir.name) { }
|
||||
|
||||
firTypeScope.processOverriddenFunctions(symbol) {
|
||||
visibility = it.fir.visibility
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
|
||||
return visibility
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.declarations
|
||||
|
||||
import org.jetbrains.kotlin.metadata.deserialization.VersionRequirementTable
|
||||
|
||||
object FirVersionRequirementsTableKey : FirDeclarationDataKey()
|
||||
|
||||
var FirDeclaration.versionRequirementsTable: VersionRequirementTable? by FirDeclarationDataRegistry.data(FirVersionRequirementsTableKey)
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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.declarations
|
||||
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import org.jetbrains.kotlin.fir.resolve.*
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.wrapNestedClassifierScopeWithSubstitutionForSuperType
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
fun SessionHolder.collectImplicitReceivers(
|
||||
type: ConeKotlinType?,
|
||||
owner: FirDeclaration
|
||||
): ImplicitReceivers {
|
||||
if (type == null) return ImplicitReceivers(null, emptyList())
|
||||
|
||||
val implicitCompanionValues = mutableListOf<ImplicitReceiverValue<*>>()
|
||||
val implicitReceiverValue = when (owner) {
|
||||
is FirClass -> {
|
||||
val towerElementsForClass = collectTowerDataElementsForClass(owner, type)
|
||||
implicitCompanionValues.addAll(towerElementsForClass.implicitCompanionValues)
|
||||
|
||||
towerElementsForClass.thisReceiver
|
||||
}
|
||||
is FirFunction -> {
|
||||
ImplicitExtensionReceiverValue(owner.symbol, type, session, scopeSession)
|
||||
}
|
||||
is FirVariable -> {
|
||||
ImplicitExtensionReceiverValue(owner.symbol, type, session, scopeSession)
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Incorrect label & receiver owner: ${owner.javaClass}")
|
||||
}
|
||||
}
|
||||
return ImplicitReceivers(implicitReceiverValue, implicitCompanionValues)
|
||||
}
|
||||
|
||||
data class ImplicitReceivers(
|
||||
val implicitReceiverValue: ImplicitReceiverValue<*>?,
|
||||
val implicitCompanionValues: List<ImplicitReceiverValue<*>>
|
||||
)
|
||||
|
||||
fun SessionHolder.collectTowerDataElementsForClass(owner: FirClass, defaultType: ConeKotlinType): TowerElementsForClass {
|
||||
val allImplicitCompanionValues = mutableListOf<ImplicitReceiverValue<*>>()
|
||||
|
||||
val companionObject = (owner as? FirRegularClass)?.companionObject
|
||||
val companionReceiver = companionObject?.let { companion ->
|
||||
ImplicitDispatchReceiverValue(
|
||||
companion.symbol, session, scopeSession
|
||||
)
|
||||
}
|
||||
allImplicitCompanionValues.addIfNotNull(companionReceiver)
|
||||
|
||||
val superClassesStaticsAndCompanionReceivers = mutableListOf<FirTowerDataElement>()
|
||||
for (superType in lookupSuperTypes(owner, lookupInterfaces = false, deep = true, useSiteSession = session)) {
|
||||
val expandedType = superType.fullyExpandedType(session)
|
||||
val superClass = expandedType.lookupTag.toSymbol(session)?.fir as? FirRegularClass ?: continue
|
||||
|
||||
superClass.staticScope(this)
|
||||
?.wrapNestedClassifierScopeWithSubstitutionForSuperType(expandedType, session)
|
||||
?.asTowerDataElement(isLocal = false)
|
||||
?.let(superClassesStaticsAndCompanionReceivers::add)
|
||||
|
||||
(superClass as? FirRegularClass)?.companionObject?.let { companion ->
|
||||
val superCompanionReceiver = ImplicitDispatchReceiverValue(
|
||||
companion.symbol, session, scopeSession
|
||||
)
|
||||
|
||||
superClassesStaticsAndCompanionReceivers += superCompanionReceiver.asTowerDataElement()
|
||||
allImplicitCompanionValues += superCompanionReceiver
|
||||
}
|
||||
}
|
||||
|
||||
val thisReceiver = ImplicitDispatchReceiverValue(owner.symbol, defaultType, session, scopeSession)
|
||||
|
||||
return TowerElementsForClass(
|
||||
thisReceiver,
|
||||
owner.staticScope(this),
|
||||
companionReceiver,
|
||||
companionObject?.staticScope(this),
|
||||
superClassesStaticsAndCompanionReceivers.asReversed(),
|
||||
allImplicitCompanionValues.asReversed()
|
||||
)
|
||||
}
|
||||
|
||||
class TowerElementsForClass(
|
||||
val thisReceiver: ImplicitReceiverValue<*>,
|
||||
val staticScope: FirScope?,
|
||||
val companionReceiver: ImplicitReceiverValue<*>?,
|
||||
val companionStaticScope: FirScope?,
|
||||
// Ordered from inner scopes to outer scopes.
|
||||
val superClassesStaticsAndCompanionReceivers: List<FirTowerDataElement>,
|
||||
// Ordered from inner scopes to outer scopes.
|
||||
val implicitCompanionValues: List<ImplicitReceiverValue<*>>
|
||||
)
|
||||
|
||||
class FirTowerDataContext private constructor(
|
||||
val towerDataElements: PersistentList<FirTowerDataElement>,
|
||||
// These properties are effectively redundant, their content should be consistent with `towerDataElements`,
|
||||
// i.e. implicitReceiverStack == towerDataElements.mapNotNull { it.receiver }
|
||||
// i.e. localScopes == towerDataElements.mapNotNull { it.scope?.takeIf { it.isLocal } }
|
||||
val implicitReceiverStack: PersistentImplicitReceiverStack,
|
||||
val localScopes: FirLocalScopes,
|
||||
val nonLocalTowerDataElements: PersistentList<FirTowerDataElement>
|
||||
) {
|
||||
|
||||
constructor() : this(
|
||||
persistentListOf(),
|
||||
PersistentImplicitReceiverStack(),
|
||||
persistentListOf(),
|
||||
persistentListOf()
|
||||
)
|
||||
|
||||
fun setLastLocalScope(newLastScope: FirLocalScope): FirTowerDataContext {
|
||||
val oldLastScope = localScopes.last()
|
||||
val indexOfLastLocalScope = towerDataElements.indexOfLast { it.scope === oldLastScope }
|
||||
|
||||
return FirTowerDataContext(
|
||||
towerDataElements.set(indexOfLastLocalScope, newLastScope.asTowerDataElement(isLocal = true)),
|
||||
implicitReceiverStack,
|
||||
localScopes.set(localScopes.lastIndex, newLastScope),
|
||||
nonLocalTowerDataElements
|
||||
)
|
||||
}
|
||||
|
||||
fun addNonLocalTowerDataElements(newElements: List<FirTowerDataElement>): FirTowerDataContext {
|
||||
return FirTowerDataContext(
|
||||
towerDataElements.addAll(newElements),
|
||||
implicitReceiverStack.addAll(newElements.mapNotNull { it.implicitReceiver }),
|
||||
localScopes,
|
||||
nonLocalTowerDataElements.addAll(newElements)
|
||||
)
|
||||
}
|
||||
|
||||
fun addLocalScope(localScope: FirLocalScope): FirTowerDataContext {
|
||||
return FirTowerDataContext(
|
||||
towerDataElements.add(localScope.asTowerDataElement(isLocal = true)),
|
||||
implicitReceiverStack,
|
||||
localScopes.add(localScope),
|
||||
nonLocalTowerDataElements
|
||||
)
|
||||
}
|
||||
|
||||
fun addReceiver(name: Name?, implicitReceiverValue: ImplicitReceiverValue<*>): FirTowerDataContext {
|
||||
val element = implicitReceiverValue.asTowerDataElement()
|
||||
return FirTowerDataContext(
|
||||
towerDataElements.add(element),
|
||||
implicitReceiverStack.add(name, implicitReceiverValue),
|
||||
localScopes,
|
||||
nonLocalTowerDataElements.add(element)
|
||||
)
|
||||
}
|
||||
|
||||
fun addNonLocalScopeIfNotNull(scope: FirScope?): FirTowerDataContext {
|
||||
if (scope == null) return this
|
||||
return addNonLocalScope(scope)
|
||||
}
|
||||
|
||||
private fun addNonLocalScope(scope: FirScope): FirTowerDataContext {
|
||||
val element = scope.asTowerDataElement(isLocal = false)
|
||||
return FirTowerDataContext(
|
||||
towerDataElements.add(element),
|
||||
implicitReceiverStack,
|
||||
localScopes,
|
||||
nonLocalTowerDataElements.add(element)
|
||||
)
|
||||
}
|
||||
|
||||
fun createSnapshot(): FirTowerDataContext {
|
||||
return FirTowerDataContext(
|
||||
towerDataElements.map { FirTowerDataElement(it.scope, it.implicitReceiver?.createSnapshot(), it.isLocal) }.toPersistentList(),
|
||||
implicitReceiverStack.createSnapshot(),
|
||||
localScopes.toPersistentList(),
|
||||
nonLocalTowerDataElements.map { FirTowerDataElement(it.scope, it.implicitReceiver?.createSnapshot(), it.isLocal) }
|
||||
.toPersistentList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FirTowerDataElement(val scope: FirScope?, val implicitReceiver: ImplicitReceiverValue<*>?, val isLocal: Boolean)
|
||||
|
||||
fun ImplicitReceiverValue<*>.asTowerDataElement(): FirTowerDataElement =
|
||||
FirTowerDataElement(scope = null, this, isLocal = false)
|
||||
|
||||
fun FirScope.asTowerDataElement(isLocal: Boolean): FirTowerDataElement =
|
||||
FirTowerDataElement(this, implicitReceiver = null, isLocal)
|
||||
|
||||
private fun FirClass.staticScope(sessionHolder: SessionHolder) =
|
||||
scopeProvider.getStaticScope(this, sessionHolder.session, sessionHolder.scopeSession)
|
||||
typealias FirLocalScopes = PersistentList<FirLocalScope>
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.resolve
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.getSymbolByTypeRef
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
|
||||
fun CallableId.isInvoke(): Boolean =
|
||||
isKFunctionInvoke()
|
||||
|| callableName.asString() == "invoke"
|
||||
&& className?.asString()?.startsWith("Function") == true
|
||||
&& packageName == StandardClassIds.BASE_KOTLIN_PACKAGE
|
||||
|
||||
fun CallableId.isKFunctionInvoke(): Boolean =
|
||||
callableName.asString() == "invoke"
|
||||
&& className?.asString()?.startsWith("KFunction") == true
|
||||
&& packageName.asString() == "kotlin.reflect"
|
||||
|
||||
fun CallableId.isIteratorNext(): Boolean =
|
||||
callableName.asString() == "next" && className?.asString()?.endsWith("Iterator") == true
|
||||
&& packageName.asString() == "kotlin.collections"
|
||||
|
||||
fun CallableId.isIteratorHasNext(): Boolean =
|
||||
callableName.asString() == "hasNext" && className?.asString()?.endsWith("Iterator") == true
|
||||
&& packageName.asString() == "kotlin.collections"
|
||||
|
||||
fun CallableId.isIterator(): Boolean =
|
||||
callableName.asString() == "iterator" && packageName.asString() in arrayOf("kotlin.collections", "kotlin.ranges")
|
||||
|
||||
fun FirAnnotation.fqName(session: FirSession): FqName? {
|
||||
val symbol = session.symbolProvider.getSymbolByTypeRef<FirRegularClassSymbol>(annotationTypeRef) ?: return null
|
||||
return symbol.classId.asSingleFqName()
|
||||
}
|
||||
@@ -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.fir.resolve
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.containingClassForLocalAttr
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.LookupTagInternals
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
fun FirClassLikeDeclaration.getContainingDeclaration(session: FirSession): FirClassLikeDeclaration? {
|
||||
if (isLocal) {
|
||||
@OptIn(LookupTagInternals::class)
|
||||
return (this as? FirRegularClass)?.containingClassForLocalAttr?.toFirRegularClass(session)
|
||||
} else {
|
||||
val classId = symbol.classId
|
||||
val parentId = classId.relativeClassName.parent()
|
||||
if (!parentId.isRoot) {
|
||||
val containingDeclarationId = ClassId(classId.packageFqName, parentId, false)
|
||||
return session.symbolProvider.getClassLikeSymbolByClassId(containingDeclarationId)?.fir
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun isValidTypeParameterFromOuterClass(
|
||||
typeParameterSymbol: FirTypeParameterSymbol,
|
||||
classDeclaration: FirRegularClass?,
|
||||
session: FirSession
|
||||
): Boolean {
|
||||
if (classDeclaration == null) {
|
||||
return true // Extra check is required because of classDeclaration will be resolved later
|
||||
}
|
||||
|
||||
fun containsTypeParameter(currentClassDeclaration: FirRegularClass): Boolean {
|
||||
if (currentClassDeclaration.typeParameters.any { it.symbol == typeParameterSymbol }) {
|
||||
return true
|
||||
}
|
||||
|
||||
for (superTypeRef in currentClassDeclaration.superTypeRefs) {
|
||||
val superClassFir = superTypeRef.firClassLike(session)
|
||||
if (superClassFir == null || superClassFir is FirRegularClass && containsTypeParameter(superClassFir)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return containsTypeParameter(classDeclaration)
|
||||
}
|
||||
|
||||
fun FirTypeRef.firClassLike(session: FirSession): FirClassLikeDeclaration? {
|
||||
val type = coneTypeSafe<ConeClassLikeType>() ?: return null
|
||||
return type.lookupTag.toSymbol(session)?.fir
|
||||
}
|
||||
|
||||
fun List<FirQualifierPart>.toTypeProjections(): Array<ConeTypeProjection> =
|
||||
asReversed().flatMap { it.typeArgumentList.typeArguments.map { typeArgument -> typeArgument.toConeTypeProjection() } }.toTypedArray()
|
||||
|
||||
private object TypeAliasConstructorKey : FirDeclarationDataKey()
|
||||
|
||||
var FirConstructor.originalConstructorIfTypeAlias: FirConstructor? by FirDeclarationDataRegistry.data(TypeAliasConstructorKey)
|
||||
|
||||
val FirConstructorSymbol.isTypeAliasedConstructor: Boolean
|
||||
get() = fir.originalConstructorIfTypeAlias != null
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.resolve
|
||||
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
|
||||
|
||||
abstract class ImplicitReceiverStack : Iterable<ImplicitReceiverValue<*>> {
|
||||
abstract operator fun get(name: String?): ImplicitReceiverValue<*>?
|
||||
|
||||
abstract fun lastDispatchReceiver(): ImplicitDispatchReceiverValue?
|
||||
abstract fun lastDispatchReceiver(lookupCondition: (ImplicitReceiverValue<*>) -> Boolean): ImplicitDispatchReceiverValue?
|
||||
abstract fun receiversAsReversed(): List<ImplicitReceiverValue<*>>
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.resolve
|
||||
|
||||
import kotlinx.collections.immutable.*
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.util.PersistentSetMultimap
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class PersistentImplicitReceiverStack private constructor(
|
||||
private val stack: PersistentList<ImplicitReceiverValue<*>>,
|
||||
// This multi-map holds indexes of the stack ^
|
||||
private val indexesPerLabel: PersistentSetMultimap<Name, Int>,
|
||||
private val indexesPerSymbol: PersistentMap<FirBasedSymbol<*>, Int>,
|
||||
private val originalTypes: PersistentList<ConeKotlinType>,
|
||||
) : ImplicitReceiverStack(), Iterable<ImplicitReceiverValue<*>> {
|
||||
val size: Int get() = stack.size
|
||||
|
||||
constructor() : this(
|
||||
persistentListOf(),
|
||||
PersistentSetMultimap(),
|
||||
persistentMapOf(),
|
||||
persistentListOf(),
|
||||
)
|
||||
|
||||
fun addAll(receivers: List<ImplicitReceiverValue<*>>): PersistentImplicitReceiverStack {
|
||||
return receivers.fold(this) { acc, value -> acc.add(name = null, value) }
|
||||
}
|
||||
|
||||
fun add(name: Name?, value: ImplicitReceiverValue<*>): PersistentImplicitReceiverStack {
|
||||
val stack = stack.add(value)
|
||||
val originalTypes = originalTypes.add(value.originalType)
|
||||
val index = stack.size - 1
|
||||
val indexesPerLabel = name?.let { indexesPerLabel.put(it, index) } ?: indexesPerLabel
|
||||
val indexesPerSymbol = indexesPerSymbol.put(value.boundSymbol, index)
|
||||
|
||||
return PersistentImplicitReceiverStack(
|
||||
stack,
|
||||
indexesPerLabel,
|
||||
indexesPerSymbol,
|
||||
originalTypes
|
||||
)
|
||||
}
|
||||
|
||||
override operator fun get(name: String?): ImplicitReceiverValue<*>? {
|
||||
if (name == null) return stack.lastOrNull()
|
||||
return indexesPerLabel[Name.identifier(name)].lastOrNull()?.let { stack[it] }
|
||||
}
|
||||
|
||||
override fun lastDispatchReceiver(): ImplicitDispatchReceiverValue? {
|
||||
return stack.filterIsInstance<ImplicitDispatchReceiverValue>().lastOrNull()
|
||||
}
|
||||
|
||||
override fun lastDispatchReceiver(lookupCondition: (ImplicitReceiverValue<*>) -> Boolean): ImplicitDispatchReceiverValue? {
|
||||
return stack.filterIsInstance<ImplicitDispatchReceiverValue>().lastOrNull(lookupCondition)
|
||||
}
|
||||
|
||||
override fun receiversAsReversed(): List<ImplicitReceiverValue<*>> = stack.asReversed()
|
||||
|
||||
override operator fun iterator(): Iterator<ImplicitReceiverValue<*>> {
|
||||
return stack.iterator()
|
||||
}
|
||||
|
||||
// These methods are only used at org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer.Companion.createFirDataFlowAnalyzer
|
||||
// No need to be extracted to an interface
|
||||
fun getReceiverIndex(symbol: FirBasedSymbol<*>): Int? = indexesPerSymbol[symbol]
|
||||
|
||||
fun getOriginalType(index: Int): ConeKotlinType {
|
||||
return originalTypes[index]
|
||||
}
|
||||
|
||||
// This method is only used from DFA and it's in some sense breaks persistence contracts of the data structure
|
||||
// But it's ok since DFA handles everything properly yet, but still may be it should be rewritten somehow
|
||||
fun replaceReceiverType(index: Int, type: ConeKotlinType) {
|
||||
assert(index >= 0 && index < stack.size)
|
||||
stack[index].replaceType(type)
|
||||
}
|
||||
|
||||
fun createSnapshot(): PersistentImplicitReceiverStack {
|
||||
return PersistentImplicitReceiverStack(
|
||||
stack.map { it.createSnapshot() }.toPersistentList(),
|
||||
indexesPerLabel,
|
||||
indexesPerSymbol,
|
||||
originalTypes
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.resolve
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
|
||||
interface SessionHolder {
|
||||
val session: FirSession
|
||||
val scopeSession: ScopeSession
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.resolve.calls
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.expressions.FirArgumentList
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
abstract class AbstractCallInfo {
|
||||
abstract val callSite: FirElement
|
||||
abstract val name: Name
|
||||
abstract val containingFile: FirFile
|
||||
abstract val isImplicitInvoke: Boolean
|
||||
abstract val explicitReceiver: FirExpression?
|
||||
abstract val argumentList: FirArgumentList
|
||||
}
|
||||
+22
@@ -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.resolve.calls
|
||||
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||
|
||||
abstract class AbstractCandidate {
|
||||
abstract val symbol: FirBasedSymbol<*>
|
||||
abstract val dispatchReceiverValue: ReceiverValue?
|
||||
abstract val extensionReceiverValue: ReceiverValue?
|
||||
abstract val explicitReceiverKind: ExplicitReceiverKind
|
||||
abstract val callInfo: AbstractCallInfo
|
||||
abstract val diagnostics: List<ResolutionDiagnostic>
|
||||
abstract val system: NewConstraintSystemImpl
|
||||
abstract val applicability: CandidateApplicability
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.resolve.calls
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
|
||||
import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability.*
|
||||
|
||||
abstract class ResolutionDiagnostic(val applicability: CandidateApplicability)
|
||||
|
||||
abstract class InapplicableArgumentDiagnostic : ResolutionDiagnostic(INAPPLICABLE) {
|
||||
abstract val argument: FirExpression
|
||||
}
|
||||
|
||||
class MixingNamedAndPositionArguments(override val argument: FirExpression) : InapplicableArgumentDiagnostic()
|
||||
|
||||
class TooManyArguments(
|
||||
val argument: FirExpression,
|
||||
val function: FirFunction
|
||||
) : ResolutionDiagnostic(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR)
|
||||
|
||||
class NamedArgumentNotAllowed(
|
||||
override val argument: FirExpression,
|
||||
val function: FirFunction,
|
||||
val forbiddenNamedArgumentsTarget: ForbiddenNamedArgumentsTarget
|
||||
) : InapplicableArgumentDiagnostic()
|
||||
|
||||
class ArgumentPassedTwice(
|
||||
override val argument: FirExpression,
|
||||
val valueParameter: FirValueParameter,
|
||||
val firstOccurrence: ResolvedCallArgument
|
||||
) : InapplicableArgumentDiagnostic()
|
||||
|
||||
class VarargArgumentOutsideParentheses(
|
||||
override val argument: FirExpression,
|
||||
val valueParameter: FirValueParameter
|
||||
) : InapplicableArgumentDiagnostic()
|
||||
|
||||
class NonVarargSpread(override val argument: FirExpression) : InapplicableArgumentDiagnostic()
|
||||
|
||||
class NoValueForParameter(
|
||||
val valueParameter: FirValueParameter,
|
||||
val function: FirFunction
|
||||
) : ResolutionDiagnostic(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR)
|
||||
|
||||
class NameNotFound(
|
||||
val argument: FirNamedArgumentExpression,
|
||||
val function: FirFunction
|
||||
) : ResolutionDiagnostic(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR)
|
||||
|
||||
object InapplicableCandidate : ResolutionDiagnostic(INAPPLICABLE)
|
||||
|
||||
object HiddenCandidate : ResolutionDiagnostic(HIDDEN)
|
||||
|
||||
object VisibilityError : ResolutionDiagnostic(VISIBILITY_ERROR)
|
||||
|
||||
object ResolvedWithLowPriority : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
|
||||
|
||||
class InapplicableWrongReceiver(
|
||||
val expectedType: ConeKotlinType? = null,
|
||||
val actualType: ConeKotlinType? = null,
|
||||
) : ResolutionDiagnostic(INAPPLICABLE_WRONG_RECEIVER)
|
||||
|
||||
object NoCompanionObject : ResolutionDiagnostic(NO_COMPANION_OBJECT)
|
||||
|
||||
class UnsafeCall(val actualType: ConeKotlinType) : ResolutionDiagnostic(UNSAFE_CALL)
|
||||
|
||||
object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(RESOLVED_NEED_PRESERVE_COMPATIBILITY)
|
||||
|
||||
object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(RESOLVED)
|
||||
|
||||
class UnstableSmartCast(val argument: FirExpressionWithSmartcast, val targetType: ConeKotlinType, val isCastToNotNull: Boolean) : ResolutionDiagnostic(UNSTABLE_SMARTCAST)
|
||||
|
||||
class ArgumentTypeMismatch(
|
||||
val expectedType: ConeKotlinType,
|
||||
val actualType: ConeKotlinType,
|
||||
val argument: FirExpression,
|
||||
val isMismatchDueToNullability: Boolean,
|
||||
) : ResolutionDiagnostic(INAPPLICABLE)
|
||||
|
||||
class NullForNotNullType(
|
||||
val argument: FirExpression
|
||||
) : ResolutionDiagnostic(INAPPLICABLE)
|
||||
|
||||
class ManyLambdaExpressionArguments(
|
||||
val argument: FirExpression
|
||||
) : ResolutionDiagnostic(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR)
|
||||
|
||||
class InfixCallOfNonInfixFunction(val function: FirNamedFunctionSymbol) : ResolutionDiagnostic(INAPPLICABLE_MODIFIER)
|
||||
class OperatorCallOfNonOperatorFunction(val function: FirNamedFunctionSymbol) : ResolutionDiagnostic(INAPPLICABLE_MODIFIER)
|
||||
|
||||
class Unsupported(val message: String, val source: FirSourceElement? = null) : ResolutionDiagnostic(UNSUPPORTED)
|
||||
|
||||
object PropertyAsOperator : ResolutionDiagnostic(PROPERTY_AS_OPERATOR)
|
||||
|
||||
class DslScopeViolation(val calleeSymbol: FirBasedSymbol<*>) : ResolutionDiagnostic(DSL_SCOPE_VIOLATION)
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
@RequiresOptIn
|
||||
annotation class DfaInternals
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.types.SmartcastStability
|
||||
|
||||
data class Identifier(
|
||||
val symbol: FirBasedSymbol<*>,
|
||||
val dispatchReceiver: DataFlowVariable?,
|
||||
val extensionReceiver: DataFlowVariable?
|
||||
) {
|
||||
override fun toString(): String {
|
||||
val callableId = (symbol as? FirCallableSymbol<*>)?.callableId
|
||||
return "[$callableId, dispatchReceiver = $dispatchReceiver, extensionReceiver = $extensionReceiver]"
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DataFlowVariable(private val variableIndexForDebug: Int) {
|
||||
final override fun toString(): String {
|
||||
return "d$variableIndexForDebug"
|
||||
}
|
||||
}
|
||||
|
||||
enum class PropertyStability(val impliedSmartcastStability: SmartcastStability?) {
|
||||
// Immutable and no custom getter or local.
|
||||
// Smartcast is definitely safe regardless of usage.
|
||||
STABLE_VALUE(SmartcastStability.STABLE_VALUE),
|
||||
|
||||
// Open or custom getter.
|
||||
// Smartcast is always unsafe regardless of usage.
|
||||
PROPERTY_WITH_GETTER(SmartcastStability.PROPERTY_WITH_GETTER),
|
||||
|
||||
// Protected / public member value from another module.
|
||||
// Smartcast is always unsafe regardless of usage.
|
||||
ALIEN_PUBLIC_PROPERTY(SmartcastStability.ALIEN_PUBLIC_PROPERTY),
|
||||
|
||||
// Smartcast may or may not be safe, depending on whether there are concurrent writes to this local variable.
|
||||
LOCAL_VAR(null),
|
||||
|
||||
// Mutable member property of a class or object.
|
||||
// Smartcast is always unsafe regardless of usage.
|
||||
MUTABLE_PROPERTY(SmartcastStability.MUTABLE_PROPERTY),
|
||||
|
||||
// Delegated property of a class or object.
|
||||
// Smartcast is always unsafe regardless of usage.
|
||||
DELEGATED_PROPERTY(SmartcastStability.DELEGATED_PROPERTY),
|
||||
}
|
||||
|
||||
class RealVariable(
|
||||
val identifier: Identifier,
|
||||
val isThisReference: Boolean,
|
||||
val explicitReceiverVariable: DataFlowVariable?,
|
||||
variableIndexForDebug: Int,
|
||||
val stability: PropertyStability,
|
||||
) : DataFlowVariable(variableIndexForDebug) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return this === other
|
||||
}
|
||||
|
||||
private val _hashCode by lazy {
|
||||
31 * identifier.hashCode() + (explicitReceiverVariable?.hashCode() ?: 0)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return _hashCode
|
||||
}
|
||||
}
|
||||
|
||||
class RealVariableAndType(val variable: RealVariable, val originalType: ConeKotlinType?) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as RealVariableAndType
|
||||
|
||||
if (variable != other.variable) return false
|
||||
if (originalType != other.originalType) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = variable.hashCode()
|
||||
result = 31 * result + originalType.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class SyntheticVariable(val fir: FirElement, variableIndexForDebug: Int) : DataFlowVariable(variableIndexForDebug) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as SyntheticVariable
|
||||
|
||||
return fir isEqualsTo other.fir
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
// hack for enums
|
||||
return if (fir is FirResolvedQualifier) {
|
||||
31 * fir.packageFqName.hashCode() + fir.classId.hashCode()
|
||||
} else {
|
||||
fir.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private infix fun FirElement.isEqualsTo(other: FirElement): Boolean {
|
||||
if (this !is FirResolvedQualifier || other !is FirResolvedQualifier) return this == other
|
||||
if (packageFqName != other.packageFqName) return false
|
||||
if (classId != other.classId) return false
|
||||
return true
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
|
||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitor
|
||||
|
||||
class FirControlFlowGraphReferenceImpl(
|
||||
val controlFlowGraph: ControlFlowGraph,
|
||||
val dataFlowInfo: DataFlowInfo? = null
|
||||
) : FirControlFlowGraphReference() {
|
||||
override val source: FirSourceElement? get() = null
|
||||
|
||||
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {}
|
||||
|
||||
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirControlFlowGraphReference {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
class DataFlowInfo(val variableStorage: VariableStorage, val flowOnNodes: Map<CFGNode<*>, Flow>)
|
||||
|
||||
val FirControlFlowGraphReference.controlFlowGraph: ControlFlowGraph?
|
||||
get() = (this as? FirControlFlowGraphReferenceImpl)?.controlFlowGraph
|
||||
|
||||
val FirControlFlowGraphReference.dataFlowInfo: DataFlowInfo?
|
||||
get() = (this as? FirControlFlowGraphReferenceImpl)?.dataFlowInfo
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
abstract class Flow {
|
||||
abstract fun getTypeStatement(variable: RealVariable): TypeStatement?
|
||||
abstract fun getImplications(variable: DataFlowVariable): Collection<Implication>
|
||||
abstract fun getVariablesInTypeStatements(): Collection<RealVariable>
|
||||
abstract fun removeOperations(variable: DataFlowVariable): Collection<Implication>
|
||||
|
||||
abstract val directAliasMap: Map<RealVariable, RealVariableAndType>
|
||||
abstract val backwardsAliasMap: Map<RealVariable, List<RealVariable>>
|
||||
abstract val assignmentIndex: Map<RealVariable, Int>
|
||||
}
|
||||
|
||||
fun Flow.unwrapVariable(variable: RealVariable): RealVariable {
|
||||
return directAliasMap[variable]?.variable ?: variable
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.types.ConeInferenceContext
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.canBeNull
|
||||
import org.jetbrains.kotlin.fir.types.commonSuperTypeOrNull
|
||||
|
||||
abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceContext) {
|
||||
// ------------------------------- Flow operations -------------------------------
|
||||
|
||||
abstract fun createEmptyFlow(): FLOW
|
||||
abstract fun forkFlow(flow: FLOW): FLOW
|
||||
abstract fun joinFlow(flows: Collection<FLOW>): FLOW
|
||||
abstract fun unionFlow(flows: Collection<FLOW>): FLOW
|
||||
|
||||
abstract fun addTypeStatement(flow: FLOW, statement: TypeStatement)
|
||||
|
||||
abstract fun addImplication(flow: FLOW, implication: Implication)
|
||||
|
||||
abstract fun removeTypeStatementsAboutVariable(flow: FLOW, variable: RealVariable)
|
||||
abstract fun removeLogicStatementsAboutVariable(flow: FLOW, variable: DataFlowVariable)
|
||||
abstract fun removeAliasInformationAboutVariable(flow: FLOW, variable: RealVariable)
|
||||
|
||||
abstract fun translateVariableFromConditionInStatements(
|
||||
flow: FLOW,
|
||||
originalVariable: DataFlowVariable,
|
||||
newVariable: DataFlowVariable,
|
||||
shouldRemoveOriginalStatements: Boolean,
|
||||
filter: (Implication) -> Boolean = { true },
|
||||
transform: (Implication) -> Implication? = { it },
|
||||
)
|
||||
|
||||
abstract fun approveStatementsInsideFlow(
|
||||
flow: FLOW,
|
||||
approvedStatement: OperationStatement,
|
||||
shouldForkFlow: Boolean,
|
||||
shouldRemoveSynthetics: Boolean,
|
||||
): FLOW
|
||||
|
||||
abstract fun addLocalVariableAlias(flow: FLOW, alias: RealVariable, underlyingVariable: RealVariableAndType)
|
||||
abstract fun removeLocalVariableAlias(flow: FLOW, alias: RealVariable)
|
||||
|
||||
abstract fun recordNewAssignment(flow: FLOW, variable: RealVariable, index: Int)
|
||||
|
||||
protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication>
|
||||
|
||||
protected abstract fun ConeKotlinType.isAcceptableForSmartcast(): Boolean
|
||||
|
||||
// ------------------------------- Callbacks for updating implicit receiver stack -------------------------------
|
||||
|
||||
abstract fun processUpdatedReceiverVariable(flow: FLOW, variable: RealVariable)
|
||||
abstract fun updateAllReceivers(flow: FLOW)
|
||||
|
||||
// ------------------------------- Public TypeStatement util functions -------------------------------
|
||||
|
||||
data class InfoForBooleanOperator(
|
||||
val conditionalFromLeft: Collection<Implication>,
|
||||
val conditionalFromRight: Collection<Implication>,
|
||||
val knownFromRight: TypeStatements,
|
||||
)
|
||||
|
||||
abstract fun collectInfoForBooleanOperator(
|
||||
leftFlow: FLOW,
|
||||
leftVariable: DataFlowVariable,
|
||||
rightFlow: FLOW,
|
||||
rightVariable: DataFlowVariable,
|
||||
): InfoForBooleanOperator
|
||||
|
||||
abstract fun approveStatementsTo(
|
||||
destination: MutableTypeStatements,
|
||||
flow: FLOW,
|
||||
approvedStatement: OperationStatement,
|
||||
statements: Collection<Implication>,
|
||||
)
|
||||
|
||||
/**
|
||||
* Recursively collects all TypeStatements approved by [approvedStatement] and all predicates
|
||||
* that has been implied by it
|
||||
* TODO: or not recursively?
|
||||
*/
|
||||
fun approveOperationStatement(flow: FLOW, approvedStatement: OperationStatement): Collection<TypeStatement> {
|
||||
val statements = getImplicationsWithVariable(flow, approvedStatement.variable)
|
||||
return approveOperationStatement(flow, approvedStatement, statements).values
|
||||
}
|
||||
|
||||
fun orForTypeStatements(
|
||||
left: TypeStatements,
|
||||
right: TypeStatements,
|
||||
): MutableTypeStatements {
|
||||
if (left.isNullOrEmpty() || right.isNullOrEmpty()) return mutableMapOf()
|
||||
val map = mutableMapOf<RealVariable, MutableTypeStatement>()
|
||||
for (variable in left.keys.intersect(right.keys)) {
|
||||
val leftStatement = left.getValue(variable)
|
||||
val rightStatement = right.getValue(variable)
|
||||
map[variable] = or(listOf(leftStatement, rightStatement))
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
// ------------------------------- Util functions -------------------------------
|
||||
|
||||
// TODO
|
||||
protected fun <E> Collection<Collection<E>>.intersectSets(): Set<E> {
|
||||
if (isEmpty()) return emptySet()
|
||||
val iterator = iterator()
|
||||
val result = LinkedHashSet<E>(iterator.next())
|
||||
while (iterator.hasNext()) {
|
||||
result.retainAll(iterator.next())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun manipulateTypeStatements(
|
||||
statements: Collection<TypeStatement>,
|
||||
op: (Collection<Set<ConeKotlinType>>) -> MutableSet<ConeKotlinType>
|
||||
): MutableTypeStatement {
|
||||
require(statements.isNotEmpty())
|
||||
statements.singleOrNull()?.let { return it as MutableTypeStatement }
|
||||
val variable = statements.first().variable
|
||||
assert(statements.all { it.variable == variable })
|
||||
val exactType = op.invoke(statements.map { it.exactType })
|
||||
val exactNotType = op.invoke(statements.map { it.exactNotType })
|
||||
return MutableTypeStatement(variable, exactType, exactNotType)
|
||||
}
|
||||
|
||||
protected fun or(statements: Collection<TypeStatement>): MutableTypeStatement =
|
||||
manipulateTypeStatements(statements, ::orForTypes)
|
||||
|
||||
private fun orForTypes(types: Collection<Set<ConeKotlinType>>): MutableSet<ConeKotlinType> {
|
||||
if (types.any { it.isEmpty() }) return mutableSetOf()
|
||||
val intersectedTypes = types.map {
|
||||
if (it.size > 1) {
|
||||
context.intersectTypes(it.toList())
|
||||
} else {
|
||||
assert(it.size == 1) { "We've already checked each set of types is not empty." }
|
||||
it.single()
|
||||
}
|
||||
}
|
||||
val result = mutableSetOf<ConeKotlinType>()
|
||||
context.commonSuperTypeOrNull(intersectedTypes)?.let {
|
||||
if (it.isAcceptableForSmartcast()) {
|
||||
result.add(it)
|
||||
} else if (!it.canBeNull) {
|
||||
result.add(context.anyType())
|
||||
}
|
||||
Unit
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
protected fun and(statements: Collection<TypeStatement>): MutableTypeStatement =
|
||||
manipulateTypeStatements(statements, ::andForTypes)
|
||||
|
||||
private fun andForTypes(types: Collection<Set<ConeKotlinType>>): MutableSet<ConeKotlinType> {
|
||||
return types.flatMapTo(mutableSetOf()) { it }
|
||||
}
|
||||
}
|
||||
|
||||
fun <FLOW : Flow> LogicSystem<FLOW>.approveOperationStatement(
|
||||
flow: FLOW,
|
||||
approvedStatement: OperationStatement,
|
||||
statements: Collection<Implication>,
|
||||
): MutableTypeStatements {
|
||||
return mutableMapOf<RealVariable, MutableTypeStatement>().apply {
|
||||
approveStatementsTo(this, flow, approvedStatement, statements)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* used for:
|
||||
* 1. val b = x is String
|
||||
* 2. b = x is String
|
||||
* 3. !b | b.not() for Booleans
|
||||
*/
|
||||
fun <F : Flow> LogicSystem<F>.replaceVariableFromConditionInStatements(
|
||||
flow: F,
|
||||
originalVariable: DataFlowVariable,
|
||||
newVariable: DataFlowVariable,
|
||||
filter: (Implication) -> Boolean = { true },
|
||||
transform: (Implication) -> Implication = { it },
|
||||
) {
|
||||
translateVariableFromConditionInStatements(
|
||||
flow,
|
||||
originalVariable,
|
||||
newVariable,
|
||||
shouldRemoveOriginalStatements = true,
|
||||
filter,
|
||||
transform,
|
||||
)
|
||||
}
|
||||
+536
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap
|
||||
import kotlinx.collections.immutable.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeInferenceContext
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.util.*
|
||||
|
||||
data class PersistentTypeStatement(
|
||||
override val variable: RealVariable,
|
||||
override val exactType: PersistentSet<ConeKotlinType>,
|
||||
override val exactNotType: PersistentSet<ConeKotlinType>
|
||||
) : TypeStatement() {
|
||||
override operator fun plus(other: TypeStatement): PersistentTypeStatement {
|
||||
return PersistentTypeStatement(
|
||||
variable,
|
||||
exactType + other.exactType,
|
||||
exactNotType + other.exactNotType
|
||||
)
|
||||
}
|
||||
|
||||
override val isEmpty: Boolean
|
||||
get() = exactType.isEmpty() && exactNotType.isEmpty()
|
||||
|
||||
override fun invert(): PersistentTypeStatement {
|
||||
return PersistentTypeStatement(variable, exactNotType, exactType)
|
||||
}
|
||||
}
|
||||
|
||||
typealias PersistentApprovedTypeStatements = PersistentMap<RealVariable, PersistentTypeStatement>
|
||||
typealias PersistentImplications = PersistentMap<DataFlowVariable, PersistentList<Implication>>
|
||||
|
||||
class PersistentFlow : Flow {
|
||||
val previousFlow: PersistentFlow?
|
||||
var approvedTypeStatements: PersistentApprovedTypeStatements
|
||||
var logicStatements: PersistentImplications
|
||||
val level: Int
|
||||
var approvedTypeStatementsDiff: PersistentApprovedTypeStatements = persistentHashMapOf()
|
||||
|
||||
/*
|
||||
* val x = a
|
||||
* val y = a
|
||||
*
|
||||
* directAliasMap: { x -> a, y -> a}
|
||||
* backwardsAliasMap: { a -> [x, y] }
|
||||
*/
|
||||
override var directAliasMap: PersistentMap<RealVariable, RealVariableAndType>
|
||||
override var backwardsAliasMap: PersistentMap<RealVariable, PersistentList<RealVariable>>
|
||||
|
||||
override var assignmentIndex: PersistentMap<RealVariable, Int>
|
||||
|
||||
constructor(previousFlow: PersistentFlow) {
|
||||
this.previousFlow = previousFlow
|
||||
approvedTypeStatements = previousFlow.approvedTypeStatements
|
||||
logicStatements = previousFlow.logicStatements
|
||||
level = previousFlow.level + 1
|
||||
|
||||
directAliasMap = previousFlow.directAliasMap
|
||||
backwardsAliasMap = previousFlow.backwardsAliasMap
|
||||
assignmentIndex = previousFlow.assignmentIndex
|
||||
}
|
||||
|
||||
constructor() {
|
||||
previousFlow = null
|
||||
approvedTypeStatements = persistentHashMapOf()
|
||||
logicStatements = persistentHashMapOf()
|
||||
level = 1
|
||||
|
||||
directAliasMap = persistentMapOf()
|
||||
backwardsAliasMap = persistentMapOf()
|
||||
assignmentIndex = persistentMapOf()
|
||||
}
|
||||
|
||||
override fun getTypeStatement(variable: RealVariable): TypeStatement? {
|
||||
return approvedTypeStatements[variable]
|
||||
}
|
||||
|
||||
override fun getImplications(variable: DataFlowVariable): Collection<Implication> {
|
||||
return logicStatements[variable] ?: emptyList()
|
||||
}
|
||||
|
||||
override fun getVariablesInTypeStatements(): Collection<RealVariable> {
|
||||
return approvedTypeStatements.keys
|
||||
}
|
||||
|
||||
override fun removeOperations(variable: DataFlowVariable): Collection<Implication> {
|
||||
return getImplications(variable).also {
|
||||
if (it.isNotEmpty()) {
|
||||
logicStatements -= variable
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSystem<PersistentFlow>(context) {
|
||||
override fun createEmptyFlow(): PersistentFlow {
|
||||
return PersistentFlow()
|
||||
}
|
||||
|
||||
override fun forkFlow(flow: PersistentFlow): PersistentFlow {
|
||||
return PersistentFlow(flow)
|
||||
}
|
||||
|
||||
override fun joinFlow(flows: Collection<PersistentFlow>): PersistentFlow {
|
||||
return foldFlow(
|
||||
flows,
|
||||
mergeOperation = { statements -> this.or(statements).takeIf { it.isNotEmpty } },
|
||||
)
|
||||
}
|
||||
|
||||
override fun unionFlow(flows: Collection<PersistentFlow>): PersistentFlow {
|
||||
return foldFlow(
|
||||
flows,
|
||||
this::and,
|
||||
)
|
||||
}
|
||||
|
||||
private inline fun foldFlow(
|
||||
flows: Collection<PersistentFlow>,
|
||||
mergeOperation: (Collection<TypeStatement>) -> MutableTypeStatement?,
|
||||
): PersistentFlow {
|
||||
if (flows.isEmpty()) return createEmptyFlow()
|
||||
flows.singleOrNull()?.let { return it }
|
||||
|
||||
val aliasedVariablesThatDontChangeAlias = computeAliasesThatDontChange(flows)
|
||||
|
||||
val commonFlow = flows.reduce(::lowestCommonFlow)
|
||||
|
||||
val variables = flows.flatMap { it.approvedTypeStatements.keys }.toSet()
|
||||
for (variable in variables) {
|
||||
val info = mergeOperation(flows.map { it.getApprovedTypeStatements(variable) }) ?: continue
|
||||
removeTypeStatementsAboutVariable(commonFlow, variable)
|
||||
val thereWereReassignments = variable.hasDifferentReassignments(flows)
|
||||
if (thereWereReassignments) {
|
||||
removeLogicStatementsAboutVariable(commonFlow, variable)
|
||||
removeAliasInformationAboutVariable(commonFlow, variable)
|
||||
}
|
||||
commonFlow.addApprovedStatements(info)
|
||||
}
|
||||
|
||||
commonFlow.addVariableAliases(aliasedVariablesThatDontChangeAlias)
|
||||
return commonFlow
|
||||
}
|
||||
|
||||
private fun RealVariable.hasDifferentReassignments(flows: Collection<PersistentFlow>): Boolean {
|
||||
val firstIndex = flows.first().assignmentIndex[this] ?: -1
|
||||
for (flow in flows) {
|
||||
val index = flow.assignmentIndex[this] ?: -1
|
||||
if (index != firstIndex) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun computeAliasesThatDontChange(
|
||||
flows: Collection<PersistentFlow>
|
||||
): MutableMap<RealVariable, RealVariableAndType> {
|
||||
val flowsSize = flows.size
|
||||
val aliasedVariablesThatDontChangeAlias = mutableMapOf<RealVariable, RealVariableAndType>()
|
||||
|
||||
flows.flatMapTo(mutableSetOf()) { it.directAliasMap.keys }.forEach { aliasedVariable ->
|
||||
val originals = flows.map { it.directAliasMap[aliasedVariable] ?: return@forEach }
|
||||
if (originals.size != flowsSize) return@forEach
|
||||
val firstOriginal = originals.first()
|
||||
if (originals.all { it == firstOriginal }) {
|
||||
aliasedVariablesThatDontChangeAlias[aliasedVariable] = firstOriginal
|
||||
}
|
||||
}
|
||||
|
||||
return aliasedVariablesThatDontChangeAlias
|
||||
}
|
||||
|
||||
private fun PersistentFlow.addVariableAliases(
|
||||
aliasedVariablesThatDontChangeAlias: MutableMap<RealVariable, RealVariableAndType>
|
||||
) {
|
||||
for ((alias, underlyingVariable) in aliasedVariablesThatDontChangeAlias) {
|
||||
addLocalVariableAlias(this, alias, underlyingVariable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun PersistentFlow.addApprovedStatements(
|
||||
info: MutableTypeStatement
|
||||
) {
|
||||
approvedTypeStatements = approvedTypeStatements.addTypeStatement(info)
|
||||
if (previousFlow != null) {
|
||||
approvedTypeStatementsDiff = approvedTypeStatementsDiff.addTypeStatement(info)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addLocalVariableAlias(flow: PersistentFlow, alias: RealVariable, underlyingVariable: RealVariableAndType) {
|
||||
removeLocalVariableAlias(flow, alias)
|
||||
flow.directAliasMap = flow.directAliasMap.put(alias, underlyingVariable)
|
||||
flow.backwardsAliasMap = flow.backwardsAliasMap.put(
|
||||
underlyingVariable.variable,
|
||||
{ persistentListOf(alias) },
|
||||
{ variables -> variables + alias }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* For example, consider `var b = a`. In this case, `b` is an alias of `a`. In other words, `a` is the original variable of `b`.
|
||||
*
|
||||
* For back alias, consider the following.
|
||||
* ```
|
||||
* var b = a
|
||||
* var c = b
|
||||
* ```
|
||||
* Here, if the current `alias` references `b`, `c` is a back alias of `b`. So if one calls this method with `b`, we must also
|
||||
* remove aliasing between `b` and `c`. But before removing aliasing, we need to copy any statements that apply to `b` to `c`.
|
||||
*/
|
||||
override fun removeLocalVariableAlias(flow: PersistentFlow, alias: RealVariable) {
|
||||
val backAliases = flow.backwardsAliasMap[alias] ?: emptyList()
|
||||
for (backAlias in backAliases) {
|
||||
flow.logicStatements[alias]?.let { it ->
|
||||
val newStatements = it.map { it.replaceVariable(alias, backAlias) }
|
||||
val replacedStatements =
|
||||
flow.logicStatements[backAlias]?.let { existing -> existing + newStatements } ?: newStatements.toPersistentList()
|
||||
flow.logicStatements = flow.logicStatements.put(backAlias, replacedStatements)
|
||||
}
|
||||
flow.approvedTypeStatements[alias]?.let { it ->
|
||||
val newStatements = it.replaceVariable(alias, backAlias)
|
||||
val replacedStatements =
|
||||
flow.approvedTypeStatements[backAlias]?.let { existing -> existing + newStatements } ?: newStatements
|
||||
flow.approvedTypeStatements = flow.approvedTypeStatements.put(backAlias, replacedStatements.toPersistent())
|
||||
}
|
||||
flow.approvedTypeStatementsDiff[alias]?.let { it ->
|
||||
val newStatements = it.replaceVariable(alias, backAlias)
|
||||
val replacedStatements =
|
||||
flow.approvedTypeStatementsDiff[backAlias]?.let { existing -> existing + newStatements } ?: newStatements
|
||||
flow.approvedTypeStatementsDiff = flow.approvedTypeStatementsDiff.put(backAlias, replacedStatements.toPersistent())
|
||||
}
|
||||
}
|
||||
|
||||
val original = flow.directAliasMap[alias]?.variable
|
||||
if (original != null) {
|
||||
flow.directAliasMap = flow.directAliasMap.remove(alias)
|
||||
val variables = flow.backwardsAliasMap.getValue(original)
|
||||
flow.backwardsAliasMap = flow.backwardsAliasMap.put(original, variables - alias)
|
||||
}
|
||||
flow.backwardsAliasMap = flow.backwardsAliasMap.remove(alias)
|
||||
for (backAlias in backAliases) {
|
||||
flow.directAliasMap = flow.directAliasMap.remove(backAlias)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Implication.replaceVariable(from: RealVariable, to: RealVariable): Implication {
|
||||
return Implication(condition.replaceVariable(from, to), effect.replaceVariable(from, to))
|
||||
}
|
||||
|
||||
private fun <T : Statement<T>> Statement<T>.replaceVariable(from: RealVariable, to: RealVariable): T {
|
||||
val statement = when (this) {
|
||||
is OperationStatement -> if (variable == from) copy(variable = to) else this
|
||||
is PersistentTypeStatement -> if (variable == from) copy(variable = to) else this
|
||||
is MutableTypeStatement -> if (variable == from) MutableTypeStatement(to, exactType, exactNotType) else this
|
||||
else -> throw IllegalArgumentException("unknown type of statement $this")
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return statement as T
|
||||
}
|
||||
|
||||
|
||||
@OptIn(DfaInternals::class)
|
||||
private fun PersistentFlow.getApprovedTypeStatements(variable: RealVariable): MutableTypeStatement {
|
||||
var flow = this
|
||||
val result = MutableTypeStatement(variable)
|
||||
val variableUnderAlias = directAliasMap[variable]
|
||||
if (variableUnderAlias == null) {
|
||||
flow.approvedTypeStatements[variable]?.let {
|
||||
result += it
|
||||
}
|
||||
} else {
|
||||
result.exactType.addIfNotNull(variableUnderAlias.originalType)
|
||||
flow.approvedTypeStatements[variableUnderAlias.variable]?.let { result += it }
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun addTypeStatement(flow: PersistentFlow, statement: TypeStatement) {
|
||||
if (statement.isEmpty) return
|
||||
with(flow) {
|
||||
approvedTypeStatements = approvedTypeStatements.addTypeStatement(statement)
|
||||
if (previousFlow != null) {
|
||||
approvedTypeStatementsDiff = approvedTypeStatementsDiff.addTypeStatement(statement)
|
||||
}
|
||||
if (statement.variable.isThisReference) {
|
||||
processUpdatedReceiverVariable(flow, statement.variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun addImplication(flow: PersistentFlow, implication: Implication) {
|
||||
if ((implication.effect as? TypeStatement)?.isEmpty == true) return
|
||||
if (implication.condition == implication.effect) return
|
||||
with(flow) {
|
||||
val variable = implication.condition.variable
|
||||
val existingImplications = logicStatements[variable]
|
||||
logicStatements = if (existingImplications == null) {
|
||||
logicStatements.put(variable, persistentListOf(implication))
|
||||
} else {
|
||||
logicStatements.put(variable, existingImplications + implication)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeTypeStatementsAboutVariable(flow: PersistentFlow, variable: RealVariable) {
|
||||
flow.approvedTypeStatements -= variable
|
||||
flow.approvedTypeStatementsDiff -= variable
|
||||
}
|
||||
|
||||
override fun removeLogicStatementsAboutVariable(flow: PersistentFlow, variable: DataFlowVariable) {
|
||||
flow.logicStatements -= variable
|
||||
var newLogicStatements = flow.logicStatements
|
||||
for ((key, implications) in flow.logicStatements) {
|
||||
val implicationsToDelete = mutableListOf<Implication>()
|
||||
implications.forEach { implication ->
|
||||
if (implication.effect.variable == variable) {
|
||||
implicationsToDelete += implication
|
||||
}
|
||||
}
|
||||
if (implicationsToDelete.isEmpty()) continue
|
||||
val newImplications = implications.removeAll(implicationsToDelete)
|
||||
newLogicStatements = if (newImplications.isNotEmpty()) {
|
||||
newLogicStatements.put(key, newImplications)
|
||||
} else {
|
||||
newLogicStatements.remove(key)
|
||||
}
|
||||
}
|
||||
flow.logicStatements = newLogicStatements
|
||||
}
|
||||
|
||||
override fun removeAliasInformationAboutVariable(flow: PersistentFlow, variable: RealVariable) {
|
||||
val existedAlias = flow.directAliasMap[variable]?.variable
|
||||
if (existedAlias != null) {
|
||||
flow.directAliasMap = flow.directAliasMap.remove(variable)
|
||||
val updatedBackwardsAliasList = flow.backwardsAliasMap.getValue(existedAlias).remove(variable)
|
||||
flow.backwardsAliasMap = if (updatedBackwardsAliasList.isEmpty()) {
|
||||
flow.backwardsAliasMap.remove(existedAlias)
|
||||
} else {
|
||||
flow.backwardsAliasMap.put(existedAlias, updatedBackwardsAliasList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun translateVariableFromConditionInStatements(
|
||||
flow: PersistentFlow,
|
||||
originalVariable: DataFlowVariable,
|
||||
newVariable: DataFlowVariable,
|
||||
shouldRemoveOriginalStatements: Boolean,
|
||||
filter: (Implication) -> Boolean,
|
||||
transform: (Implication) -> Implication?
|
||||
) {
|
||||
with(flow) {
|
||||
val statements = logicStatements[originalVariable]?.takeIf { it.isNotEmpty() } ?: return
|
||||
val newStatements = statements.filter(filter).mapNotNull {
|
||||
val newStatement = OperationStatement(newVariable, it.condition.operation) implies it.effect
|
||||
transform(newStatement)
|
||||
}.toPersistentList()
|
||||
if (shouldRemoveOriginalStatements) {
|
||||
logicStatements -= originalVariable
|
||||
}
|
||||
logicStatements = logicStatements.put(newVariable, logicStatements[newVariable]?.let { it + newStatements } ?: newStatements)
|
||||
}
|
||||
}
|
||||
|
||||
override fun approveStatementsInsideFlow(
|
||||
flow: PersistentFlow,
|
||||
approvedStatement: OperationStatement,
|
||||
shouldForkFlow: Boolean,
|
||||
shouldRemoveSynthetics: Boolean
|
||||
): PersistentFlow {
|
||||
val approvedFacts = approveOperationStatementsInternal(
|
||||
flow,
|
||||
approvedStatement,
|
||||
initialStatements = null,
|
||||
shouldRemoveSynthetics
|
||||
)
|
||||
|
||||
val resultFlow = if (shouldForkFlow) forkFlow(flow) else flow
|
||||
if (approvedFacts.isEmpty) return resultFlow
|
||||
|
||||
val updatedReceivers = mutableSetOf<RealVariable>()
|
||||
approvedFacts.asMap().forEach { (variable, infos) ->
|
||||
var resultInfo = PersistentTypeStatement(variable, persistentSetOf(), persistentSetOf())
|
||||
for (info in infos) {
|
||||
resultInfo += info
|
||||
}
|
||||
if (variable.isThisReference) {
|
||||
updatedReceivers += variable
|
||||
}
|
||||
addTypeStatement(resultFlow, resultInfo)
|
||||
}
|
||||
|
||||
updatedReceivers.forEach {
|
||||
processUpdatedReceiverVariable(resultFlow, it)
|
||||
}
|
||||
|
||||
return resultFlow
|
||||
}
|
||||
|
||||
private fun approveOperationStatementsInternal(
|
||||
flow: PersistentFlow,
|
||||
approvedStatement: OperationStatement,
|
||||
initialStatements: Collection<Implication>?,
|
||||
shouldRemoveSynthetics: Boolean
|
||||
): ArrayListMultimap<RealVariable, TypeStatement> {
|
||||
val approvedFacts: ArrayListMultimap<RealVariable, TypeStatement> = ArrayListMultimap.create()
|
||||
val approvedStatements = LinkedList<OperationStatement>().apply { this += approvedStatement }
|
||||
approveOperationStatementsInternal(flow, approvedStatements, initialStatements, shouldRemoveSynthetics, approvedFacts)
|
||||
return approvedFacts
|
||||
}
|
||||
|
||||
private fun approveOperationStatementsInternal(
|
||||
flow: PersistentFlow,
|
||||
approvedStatements: LinkedList<OperationStatement>,
|
||||
initialStatements: Collection<Implication>?,
|
||||
shouldRemoveSynthetics: Boolean,
|
||||
approvedTypeStatements: ArrayListMultimap<RealVariable, TypeStatement>
|
||||
) {
|
||||
if (approvedStatements.isEmpty()) return
|
||||
val approvedOperationStatements = mutableSetOf<OperationStatement>()
|
||||
var firstIteration = true
|
||||
while (approvedStatements.isNotEmpty()) {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val approvedStatement: OperationStatement = approvedStatements.removeFirst()
|
||||
// Defense from cycles in facts
|
||||
if (!approvedOperationStatements.add(approvedStatement)) {
|
||||
continue
|
||||
}
|
||||
val statements = initialStatements?.takeIf { firstIteration }
|
||||
?: flow.logicStatements[approvedStatement.variable]?.takeIf { it.isNotEmpty() }
|
||||
?: continue
|
||||
if (shouldRemoveSynthetics && approvedStatement.variable.isSynthetic()) {
|
||||
flow.logicStatements -= approvedStatement.variable
|
||||
}
|
||||
for (statement in statements) {
|
||||
if (statement.condition == approvedStatement) {
|
||||
when (val effect = statement.effect) {
|
||||
is OperationStatement -> approvedStatements += effect
|
||||
is TypeStatement -> approvedTypeStatements.put(effect.variable, effect)
|
||||
}
|
||||
}
|
||||
}
|
||||
firstIteration = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun approveStatementsTo(
|
||||
destination: MutableTypeStatements,
|
||||
flow: PersistentFlow,
|
||||
approvedStatement: OperationStatement,
|
||||
statements: Collection<Implication>
|
||||
) {
|
||||
val approveOperationStatements =
|
||||
approveOperationStatementsInternal(flow, approvedStatement, statements, shouldRemoveSynthetics = false)
|
||||
approveOperationStatements.asMap().forEach { (variable, infos) ->
|
||||
for (info in infos) {
|
||||
val mutableInfo = info.asMutableStatement()
|
||||
destination.put(variable, mutableInfo) {
|
||||
it += mutableInfo
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun collectInfoForBooleanOperator(
|
||||
leftFlow: PersistentFlow,
|
||||
leftVariable: DataFlowVariable,
|
||||
rightFlow: PersistentFlow,
|
||||
rightVariable: DataFlowVariable
|
||||
): InfoForBooleanOperator {
|
||||
return InfoForBooleanOperator(
|
||||
leftFlow.logicStatements[leftVariable] ?: emptyList(),
|
||||
rightFlow.logicStatements[rightVariable] ?: emptyList(),
|
||||
rightFlow.approvedTypeStatementsDiff
|
||||
)
|
||||
}
|
||||
|
||||
override fun getImplicationsWithVariable(flow: PersistentFlow, variable: DataFlowVariable): Collection<Implication> {
|
||||
return flow.logicStatements[variable] ?: emptyList()
|
||||
}
|
||||
|
||||
override fun recordNewAssignment(flow: PersistentFlow, variable: RealVariable, index: Int) {
|
||||
flow.assignmentIndex = flow.assignmentIndex.put(variable, index)
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------\
|
||||
}
|
||||
|
||||
private fun lowestCommonFlow(left: PersistentFlow, right: PersistentFlow): PersistentFlow {
|
||||
val level = minOf(left.level, right.level)
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
var left = left
|
||||
while (left.level > level) {
|
||||
left = left.previousFlow!!
|
||||
}
|
||||
@Suppress("NAME_SHADOWING")
|
||||
var right = right
|
||||
while (right.level > level) {
|
||||
right = right.previousFlow!!
|
||||
}
|
||||
while (left != right) {
|
||||
left = left.previousFlow!!
|
||||
right = right.previousFlow!!
|
||||
}
|
||||
return left
|
||||
}
|
||||
|
||||
private fun PersistentApprovedTypeStatements.addTypeStatement(info: TypeStatement): PersistentApprovedTypeStatements {
|
||||
val variable = info.variable
|
||||
val existingInfo = this[variable]
|
||||
return if (existingInfo == null) {
|
||||
val persistentInfo = if (info is PersistentTypeStatement) info else info.toPersistent()
|
||||
put(variable, persistentInfo)
|
||||
} else {
|
||||
put(variable, existingInfo + info)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TypeStatement.toPersistent(): PersistentTypeStatement = PersistentTypeStatement(
|
||||
variable,
|
||||
exactType.toPersistentSet(),
|
||||
exactNotType.toPersistentSet()
|
||||
)
|
||||
|
||||
fun TypeStatement.asMutableStatement(): MutableTypeStatement = when (this) {
|
||||
is MutableTypeStatement -> this
|
||||
is PersistentTypeStatement -> MutableTypeStatement(variable, exactType.toMutableSet(), exactNotType.toMutableSet())
|
||||
else -> throw IllegalArgumentException("Unknown TypeStatement type: ${this::class}")
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.util.asReversedFrozen
|
||||
|
||||
abstract class Stack<T> {
|
||||
abstract val size: Int
|
||||
abstract fun top(): T
|
||||
abstract fun pop(): T
|
||||
abstract fun push(value: T)
|
||||
abstract fun reset()
|
||||
|
||||
/**
|
||||
* returns all elements of the stack in order of retrieval
|
||||
*/
|
||||
abstract fun all(): List<T>
|
||||
}
|
||||
|
||||
fun <T> stackOf(vararg values: T): Stack<T> = StackImpl(*values)
|
||||
val Stack<*>.isEmpty: Boolean get() = size == 0
|
||||
val Stack<*>.isNotEmpty: Boolean get() = size != 0
|
||||
fun <T> Stack<T>.topOrNull(): T? = if (size == 0) null else top()
|
||||
fun <T> Stack<T>.popOrNull(): T? = if (size == 0) null else pop()
|
||||
|
||||
private class StackImpl<T>(vararg values: T) : Stack<T>() {
|
||||
private val stack = mutableListOf(*values)
|
||||
|
||||
override fun top(): T = stack[stack.size - 1]
|
||||
override fun pop(): T = stack.removeAt(stack.size - 1)
|
||||
|
||||
override fun push(value: T) {
|
||||
stack.add(value)
|
||||
}
|
||||
|
||||
override val size: Int get() = stack.size
|
||||
override fun reset() {
|
||||
stack.clear()
|
||||
}
|
||||
|
||||
override fun all(): List<T> = stack.asReversedFrozen()
|
||||
}
|
||||
|
||||
class NodeStorage<T : FirElement, N : CFGNode<T>> : Stack<N>(){
|
||||
private val stack: Stack<N> = StackImpl()
|
||||
private val map: MutableMap<T, N> = mutableMapOf()
|
||||
|
||||
override val size: Int get() = stack.size
|
||||
|
||||
override fun top(): N = stack.top()
|
||||
|
||||
override fun pop(): N = stack.pop().also {
|
||||
map.remove(it.fir)
|
||||
}
|
||||
|
||||
override fun push(value: N) {
|
||||
stack.push(value)
|
||||
map[value.fir] = value
|
||||
}
|
||||
|
||||
operator fun get(key: T): N? {
|
||||
return map[key]
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
stack.reset()
|
||||
map.clear()
|
||||
}
|
||||
|
||||
override fun all(): List<N> = stack.all()
|
||||
}
|
||||
|
||||
class SymbolBasedNodeStorage<T, N : CFGNode<T>> : Stack<N>() where T : FirElement {
|
||||
private val stack: Stack<N> = StackImpl()
|
||||
private val map: MutableMap<FirBasedSymbol<*>, N> = mutableMapOf()
|
||||
|
||||
override val size: Int get() = stack.size
|
||||
|
||||
override fun top(): N = stack.top()
|
||||
|
||||
override fun pop(): N = stack.pop().also {
|
||||
map.remove((it.fir as FirDeclaration).symbol)
|
||||
}
|
||||
|
||||
override fun push(value: N) {
|
||||
stack.push(value)
|
||||
map[(value.fir as FirDeclaration).symbol] = value
|
||||
}
|
||||
|
||||
operator fun get(key: FirBasedSymbol<*>): N? {
|
||||
return map[key]
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
stack.reset()
|
||||
map.clear()
|
||||
}
|
||||
|
||||
override fun all(): List<N> = stack.all()
|
||||
}
|
||||
@@ -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.fir.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
|
||||
abstract class VariableStorage {
|
||||
abstract fun getRealVariableWithoutUnwrappingAlias(symbol: FirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable?
|
||||
abstract fun getRealVariable(symbol: FirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable?
|
||||
abstract fun getSyntheticVariable(fir: FirElement): SyntheticVariable?
|
||||
abstract fun getVariable(fir: FirElement, flow: Flow): DataFlowVariable?
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.modality
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
|
||||
import org.jetbrains.kotlin.fir.originalOrSelf
|
||||
import org.jetbrains.kotlin.fir.references.FirThisReference
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@OptIn(DfaInternals::class)
|
||||
class VariableStorageImpl(private val session: FirSession) : VariableStorage() {
|
||||
private var counter = 1
|
||||
private val _realVariables: MutableMap<Identifier, RealVariable> = HashMap()
|
||||
val realVariables: Map<Identifier, RealVariable>
|
||||
get() = _realVariables
|
||||
|
||||
private val syntheticVariables: MutableMap<FirElement, SyntheticVariable> = HashMap()
|
||||
|
||||
fun clear(): VariableStorageImpl = VariableStorageImpl(session)
|
||||
|
||||
fun getOrCreateRealVariableWithoutUnwrappingAlias(
|
||||
flow: Flow,
|
||||
symbol: FirBasedSymbol<*>,
|
||||
fir: FirElement,
|
||||
stability: PropertyStability
|
||||
): RealVariable {
|
||||
val realFir = fir.unwrapElement()
|
||||
val identifier = getIdentifierBySymbol(flow, symbol, realFir)
|
||||
return _realVariables.getOrPut(identifier) { createRealVariableInternal(flow, identifier, realFir, stability) }
|
||||
}
|
||||
|
||||
private fun getOrCreateRealVariable(
|
||||
flow: Flow,
|
||||
symbol: FirBasedSymbol<*>,
|
||||
fir: FirElement,
|
||||
stability: PropertyStability
|
||||
): RealVariable {
|
||||
val variable = getOrCreateRealVariableWithoutUnwrappingAlias(flow, symbol, fir, stability)
|
||||
return flow.directAliasMap[variable]?.variable ?: variable
|
||||
}
|
||||
|
||||
private fun FirElement.unwrapElement(): FirElement = when (this) {
|
||||
is FirWhenSubjectExpression -> whenRef.value.let { it.subjectVariable ?: it.subject }?.unwrapElement() ?: this
|
||||
is FirExpressionWithSmartcast -> originalExpression.unwrapElement()
|
||||
is FirSafeCallExpression -> regularQualifiedAccess.unwrapElement()
|
||||
is FirCheckedSafeCallSubject -> originalReceiverRef.value.unwrapElement()
|
||||
else -> this
|
||||
}
|
||||
|
||||
private fun getIdentifierBySymbol(
|
||||
flow: Flow,
|
||||
symbol: FirBasedSymbol<*>,
|
||||
fir: FirElement,
|
||||
): Identifier {
|
||||
val expression = fir as? FirQualifiedAccess
|
||||
return Identifier(
|
||||
symbol,
|
||||
expression?.dispatchReceiver?.takeIf { it != FirNoReceiverExpression }?.let { getOrCreateVariable(flow, it) },
|
||||
expression?.extensionReceiver?.takeIf { it != FirNoReceiverExpression }?.let { getOrCreateVariable(flow, it) }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [originalFir] used for extracting expression under <when_subject> and extracting receiver
|
||||
*/
|
||||
private fun createRealVariableInternal(
|
||||
flow: Flow,
|
||||
identifier: Identifier,
|
||||
originalFir: FirElement,
|
||||
stability: PropertyStability
|
||||
): RealVariable {
|
||||
val receiver: FirExpression?
|
||||
val isThisReference: Boolean
|
||||
val expression: FirQualifiedAccess? = when (originalFir) {
|
||||
is FirQualifiedAccessExpression -> originalFir
|
||||
is FirWhenSubjectExpression -> originalFir.whenRef.value.subject as? FirQualifiedAccessExpression
|
||||
is FirVariableAssignment -> originalFir
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (expression != null) {
|
||||
receiver = expression.explicitReceiver
|
||||
isThisReference = expression.calleeReference is FirThisReference
|
||||
} else {
|
||||
receiver = null
|
||||
isThisReference = false
|
||||
}
|
||||
|
||||
val receiverVariable = receiver?.let { getOrCreateVariable(flow, it) }
|
||||
return RealVariable(identifier, isThisReference, receiverVariable, counter++, stability)
|
||||
}
|
||||
|
||||
@JvmName("getOrCreateRealVariableOrNull")
|
||||
fun getOrCreateRealVariable(flow: Flow, symbol: FirBasedSymbol<*>?, fir: FirElement): RealVariable? =
|
||||
symbol.getStability(fir)?.let { getOrCreateRealVariable(flow, symbol!!, fir, it) }
|
||||
|
||||
fun createSyntheticVariable(fir: FirElement): SyntheticVariable =
|
||||
SyntheticVariable(fir, counter++).also { syntheticVariables[fir] = it }
|
||||
|
||||
fun getOrCreateVariable(flow: Flow, fir: FirElement): DataFlowVariable {
|
||||
val realFir = fir.unwrapElement()
|
||||
val symbol = realFir.symbol
|
||||
val stability = symbol.getStability(realFir)
|
||||
return if (stability != null) {
|
||||
getOrCreateRealVariable(flow, symbol!!, realFir, stability)
|
||||
} else {
|
||||
syntheticVariables[realFir] ?: createSyntheticVariable(realFir)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRealVariableWithoutUnwrappingAlias(symbol: FirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
|
||||
val realFir = fir.unwrapElement()
|
||||
return symbol.takeIf { it.getStability(realFir) != null }?.let {
|
||||
_realVariables[getIdentifierBySymbol(flow, it, realFir.unwrapElement())]
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRealVariable(symbol: FirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
|
||||
return getRealVariableWithoutUnwrappingAlias(symbol, fir, flow)?.let { flow.unwrapVariable(it) }
|
||||
}
|
||||
|
||||
override fun getSyntheticVariable(fir: FirElement): SyntheticVariable? {
|
||||
return syntheticVariables[fir.unwrapElement()]
|
||||
}
|
||||
|
||||
override fun getVariable(fir: FirElement, flow: Flow): DataFlowVariable? {
|
||||
val realFir = fir.unwrapElement()
|
||||
val symbol = realFir.symbol
|
||||
val stability = symbol.getStability(fir)
|
||||
return if (stability != null) {
|
||||
getRealVariable(symbol, realFir, flow)
|
||||
} else {
|
||||
getSyntheticVariable(fir)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeRealVariable(symbol: FirBasedSymbol<*>) {
|
||||
_realVariables.remove(Identifier(symbol, null, null))
|
||||
}
|
||||
|
||||
fun removeSyntheticVariable(variable: DataFlowVariable) {
|
||||
if (!variable.isSynthetic()) return
|
||||
syntheticVariables.remove(variable.fir)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun FirBasedSymbol<*>?.getStability(originalFir: FirElement): PropertyStability? {
|
||||
contract {
|
||||
returnsNotNull() implies (this@getStability != null)
|
||||
}
|
||||
when (this) {
|
||||
is FirAnonymousObjectSymbol -> return null
|
||||
is FirFunctionSymbol<*>,
|
||||
is FirClassSymbol<*>,
|
||||
is FirBackingFieldSymbol -> return PropertyStability.STABLE_VALUE
|
||||
null -> return null
|
||||
}
|
||||
if (originalFir is FirThisReceiverExpression) return PropertyStability.STABLE_VALUE
|
||||
if (this !is FirVariableSymbol<*>) return null
|
||||
|
||||
val property = this.fir as? FirProperty ?: return PropertyStability.STABLE_VALUE
|
||||
|
||||
return when {
|
||||
property.delegate != null -> PropertyStability.DELEGATED_PROPERTY
|
||||
property.isLocal -> if (property.isVal) PropertyStability.STABLE_VALUE else PropertyStability.LOCAL_VAR
|
||||
property.isVar -> PropertyStability.MUTABLE_PROPERTY
|
||||
property.receiverTypeRef != null -> PropertyStability.PROPERTY_WITH_GETTER
|
||||
property.getter.let { it != null && it !is FirDefaultPropertyAccessor } -> PropertyStability.PROPERTY_WITH_GETTER
|
||||
property.originalOrSelf().moduleData.session != session -> PropertyStability.ALIEN_PUBLIC_PROPERTY
|
||||
property.modality != Modality.FINAL -> {
|
||||
val dispatchReceiver = (originalFir.unwrapElement() as? FirQualifiedAccess)?.dispatchReceiver ?: return null
|
||||
val receiverType = dispatchReceiver.typeRef.coneTypeSafe<ConeClassLikeType>()?.fullyExpandedType(session) ?: return null
|
||||
val receiverSymbol = receiverType.lookupTag.toSymbol(session) ?: return null
|
||||
when (val receiverFir = receiverSymbol.fir) {
|
||||
is FirAnonymousObject -> PropertyStability.STABLE_VALUE
|
||||
is FirRegularClass -> if (receiverFir.modality == Modality.FINAL) PropertyStability.STABLE_VALUE else PropertyStability.PROPERTY_WITH_GETTER
|
||||
else -> throw IllegalStateException("Should not be here: $receiverFir")
|
||||
}
|
||||
}
|
||||
else -> PropertyStability.STABLE_VALUE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,833 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("Reformat")
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
|
||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitor
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
|
||||
@RequiresOptIn
|
||||
annotation class CfgInternals
|
||||
|
||||
sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level: Int, private val id: Int) {
|
||||
companion object {
|
||||
@CfgInternals
|
||||
fun addEdge(
|
||||
from: CFGNode<*>,
|
||||
to: CFGNode<*>,
|
||||
kind: EdgeKind,
|
||||
propagateDeadness: Boolean,
|
||||
label: EdgeLabel = NormalPath
|
||||
) {
|
||||
from._followingNodes += to
|
||||
to._previousNodes += from
|
||||
addJustKindEdge(from, to, kind, propagateDeadness, edgeExists = false, label = label)
|
||||
}
|
||||
|
||||
@CfgInternals
|
||||
fun addJustKindEdge(
|
||||
from: CFGNode<*>,
|
||||
to: CFGNode<*>,
|
||||
kind: EdgeKind,
|
||||
propagateDeadness: Boolean,
|
||||
label: EdgeLabel = NormalPath
|
||||
) {
|
||||
addJustKindEdge(from, to, kind, propagateDeadness, edgeExists = true, label = label)
|
||||
}
|
||||
|
||||
private fun addJustKindEdge(
|
||||
from: CFGNode<*>,
|
||||
to: CFGNode<*>,
|
||||
kind: EdgeKind,
|
||||
propagateDeadness: Boolean,
|
||||
edgeExists: Boolean,
|
||||
label: EdgeLabel = NormalPath
|
||||
) {
|
||||
// It's hard to define label merging, hence overwritten with the latest one.
|
||||
// One day, if we allow multiple edges between nodes with different labels, we won't even need kind merging.
|
||||
if (kind != EdgeKind.Forward || label != NormalPath) {
|
||||
val fromToKind = from._outgoingEdges[to]?.kind ?: runIf(edgeExists) { EdgeKind.Forward }
|
||||
merge(kind, fromToKind)?.let {
|
||||
from._outgoingEdges[to] = Edge.create(label, it)
|
||||
} ?: from._outgoingEdges.remove(to)
|
||||
val toFromKind = to._incomingEdges[from]?.kind ?: runIf(edgeExists) { EdgeKind.Forward }
|
||||
merge(kind, toFromKind)?.let {
|
||||
to._incomingEdges[from] = Edge.create(label, it)
|
||||
} ?: to._incomingEdges.remove(from)
|
||||
}
|
||||
if (propagateDeadness && kind == EdgeKind.DeadForward) {
|
||||
to.isDead = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun merge(first: EdgeKind, second: EdgeKind?): EdgeKind? {
|
||||
return when {
|
||||
second == null -> first
|
||||
first == second -> first
|
||||
first == EdgeKind.DeadForward || second == EdgeKind.DeadForward -> EdgeKind.DeadForward
|
||||
first == EdgeKind.DeadBackward || second == EdgeKind.DeadBackward -> EdgeKind.DeadBackward
|
||||
first == EdgeKind.Forward || second == EdgeKind.Forward -> null
|
||||
first.usedInDfa xor second.usedInDfa -> null
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
@CfgInternals
|
||||
fun removeAllIncomingEdges(to: CFGNode<*>) {
|
||||
for (from in to._previousNodes) {
|
||||
from._followingNodes.remove(to)
|
||||
from._outgoingEdges.remove(to)
|
||||
to._incomingEdges.remove(from)
|
||||
}
|
||||
to._previousNodes.clear()
|
||||
}
|
||||
|
||||
@CfgInternals
|
||||
fun removeAllOutgoingEdges(from: CFGNode<*>) {
|
||||
for (to in from._followingNodes) {
|
||||
to._previousNodes.remove(from)
|
||||
from._outgoingEdges.remove(to)
|
||||
to._incomingEdges.remove(from)
|
||||
}
|
||||
from._followingNodes.clear()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
@Suppress("LeakingThis")
|
||||
owner.addNode(this)
|
||||
}
|
||||
|
||||
private val _previousNodes: MutableList<CFGNode<*>> = mutableListOf()
|
||||
private val _followingNodes: MutableList<CFGNode<*>> = mutableListOf()
|
||||
|
||||
val previousNodes: List<CFGNode<*>> get() = _previousNodes
|
||||
val followingNodes: List<CFGNode<*>> get() = _followingNodes
|
||||
|
||||
private val _incomingEdges = mutableMapOf<CFGNode<*>, Edge>().withDefault { Edge.Normal_Forward }
|
||||
private val _outgoingEdges = mutableMapOf<CFGNode<*>, Edge>().withDefault { Edge.Normal_Forward }
|
||||
|
||||
val incomingEdges: Map<CFGNode<*>, Edge> get() = _incomingEdges
|
||||
val outgoingEdges: Map<CFGNode<*>, Edge> get() = _outgoingEdges
|
||||
|
||||
abstract val fir: E
|
||||
var isDead: Boolean = false
|
||||
protected set
|
||||
|
||||
@CfgInternals
|
||||
fun updateDeadStatus() {
|
||||
isDead = incomingEdges.size == previousNodes.size && incomingEdges.values.all {
|
||||
it.kind == EdgeKind.DeadForward || !it.kind.usedInCfa
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R
|
||||
|
||||
fun accept(visitor: ControlFlowGraphVisitorVoid) {
|
||||
accept(visitor, null)
|
||||
}
|
||||
|
||||
final override fun equals(other: Any?): Boolean {
|
||||
if (other !is CFGNode<*>) return false
|
||||
return this === other
|
||||
}
|
||||
|
||||
final override fun hashCode(): Int {
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
val CFGNode<*>.firstPreviousNode: CFGNode<*> get() = previousNodes[0]
|
||||
val CFGNode<*>.lastPreviousNode: CFGNode<*> get() = previousNodes.last()
|
||||
|
||||
interface EnterNodeMarker
|
||||
interface ExitNodeMarker
|
||||
|
||||
// ----------------------------------- EnterNode for declaration with CFG -----------------------------------
|
||||
|
||||
sealed class CFGNodeWithCfgOwner<out E : FirControlFlowGraphOwner>(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<E>(owner, level, id) {
|
||||
private val _subGraphs = mutableListOf<ControlFlowGraph>()
|
||||
|
||||
fun addSubGraph(graph: ControlFlowGraph){
|
||||
_subGraphs += graph
|
||||
}
|
||||
|
||||
val subGraphs: List<ControlFlowGraph> by lazy {
|
||||
_subGraphs.also { it.addIfNotNull(fir.controlFlowGraphReference?.controlFlowGraph) }
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Named function -----------------------------------
|
||||
|
||||
class FunctionEnterNode(owner: ControlFlowGraph, override val fir: FirFunction, level: Int, id: Int) : CFGNode<FirFunction>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFunctionEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class FunctionExitNode(owner: ControlFlowGraph, override val fir: FirFunction, level: Int, id: Int) : CFGNode<FirFunction>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFunctionExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class LocalFunctionDeclarationNode(owner: ControlFlowGraph, override val fir: FirFunction, level: Int, id: Int) : CFGNodeWithCfgOwner<FirFunction>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitLocalFunctionDeclarationNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------- Default arguments -----------------------------------
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class EnterDefaultArgumentsNode(owner: ControlFlowGraph, override val fir: FirValueParameter, level: Int, id: Int) : CFGNodeWithCfgOwner<FirValueParameter>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitEnterDefaultArgumentsNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class ExitDefaultArgumentsNode(owner: ControlFlowGraph, override val fir: FirValueParameter, level: Int, id: Int) : CFGNode<FirValueParameter>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitExitDefaultArgumentsNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Anonymous function -----------------------------------
|
||||
|
||||
class PostponedLambdaEnterNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunction, level: Int, id: Int) : CFGNodeWithCfgOwner<FirAnonymousFunction>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitPostponedLambdaEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class PostponedLambdaExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunctionExpression, level: Int, id: Int) : CFGNode<FirAnonymousFunctionExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitPostponedLambdaExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class UnionFunctionCallArgumentsNode(owner: ControlFlowGraph, override val fir: FirElement, level: Int, id: Int) : CFGNode<FirElement>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitUnionFunctionCallArgumentsNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class AnonymousFunctionExpressionExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunctionExpression, level: Int, id: Int) : CFGNode<FirAnonymousFunctionExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitAnonymousFunctionExpressionExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Classes -----------------------------------
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class ClassEnterNode(owner: ControlFlowGraph, override val fir: FirClass, level: Int, id: Int) : CFGNode<FirClass>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitClassEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class ClassExitNode(owner: ControlFlowGraph, override val fir: FirClass, level: Int, id: Int) : CFGNode<FirClass>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitClassExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class LocalClassExitNode(owner: ControlFlowGraph, override val fir: FirRegularClass, level: Int, id: Int) : CFGNodeWithCfgOwner<FirRegularClass>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitLocalClassExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class AnonymousObjectExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousObject, level: Int, id: Int) : CFGNodeWithCfgOwner<FirAnonymousObject>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitAnonymousObjectExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class AnonymousObjectExpressionExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousObjectExpression, level: Int, id: Int) : CFGNode<FirAnonymousObjectExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitAnonymousObjectExpressionExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Initialization -----------------------------------
|
||||
|
||||
class PartOfClassInitializationNode(owner: ControlFlowGraph, override val fir: FirControlFlowGraphOwner, level: Int, id: Int) : CFGNodeWithCfgOwner<FirControlFlowGraphOwner>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitPartOfClassInitializationNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Property -----------------------------------
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class PropertyInitializerEnterNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode<FirProperty>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitPropertyInitializerEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class PropertyInitializerExitNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode<FirProperty>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitPropertyInitializerExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Field -----------------------------------
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class FieldInitializerEnterNode(owner: ControlFlowGraph, override val fir: FirField, level: Int, id: Int) : CFGNode<FirField>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFieldInitializerEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class FieldInitializerExitNode(owner: ControlFlowGraph, override val fir: FirField, level: Int, id: Int) : CFGNode<FirField>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFieldInitializerExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Init -----------------------------------
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class InitBlockEnterNode(owner: ControlFlowGraph, override val fir: FirAnonymousInitializer, level: Int, id: Int) : CFGNode<FirAnonymousInitializer>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitInitBlockEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class InitBlockExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousInitializer, level: Int, id: Int) : CFGNode<FirAnonymousInitializer>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitInitBlockExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Block -----------------------------------
|
||||
|
||||
class BlockEnterNode(owner: ControlFlowGraph, override val fir: FirBlock, level: Int, id: Int) : CFGNode<FirBlock>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitBlockEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class BlockExitNode(owner: ControlFlowGraph, override val fir: FirBlock, level: Int, id: Int) : CFGNode<FirBlock>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitBlockExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- When -----------------------------------
|
||||
|
||||
class WhenEnterNode(owner: ControlFlowGraph, override val fir: FirWhenExpression, level: Int, id: Int) : CFGNode<FirWhenExpression>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitWhenEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class WhenExitNode(owner: ControlFlowGraph, override val fir: FirWhenExpression, level: Int, id: Int) : CFGNode<FirWhenExpression>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitWhenExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class WhenBranchConditionEnterNode(owner: ControlFlowGraph, override val fir: FirWhenBranch, level: Int, id: Int) : CFGNode<FirWhenBranch>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitWhenBranchConditionEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class WhenBranchConditionExitNode(owner: ControlFlowGraph, override val fir: FirWhenBranch, level: Int, id: Int) : CFGNode<FirWhenBranch>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitWhenBranchConditionExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class WhenBranchResultEnterNode(owner: ControlFlowGraph, override val fir: FirWhenBranch, level: Int, id: Int) : CFGNode<FirWhenBranch>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitWhenBranchResultEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class WhenBranchResultExitNode(owner: ControlFlowGraph, override val fir: FirWhenBranch, level: Int, id: Int) : CFGNode<FirWhenBranch>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitWhenBranchResultExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class WhenSyntheticElseBranchNode(owner: ControlFlowGraph, override val fir: FirWhenExpression, level: Int, id: Int) : CFGNode<FirWhenExpression>(owner, level, id) {
|
||||
init {
|
||||
assert(!fir.isProperlyExhaustive)
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitWhenSyntheticElseBranchNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Loop -----------------------------------
|
||||
|
||||
class LoopEnterNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int, id: Int) : CFGNode<FirLoop>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitLoopEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class LoopBlockEnterNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int, id: Int) : CFGNode<FirLoop>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitLoopBlockEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class LoopBlockExitNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int, id: Int) : CFGNode<FirLoop>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitLoopBlockExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class LoopConditionEnterNode(owner: ControlFlowGraph, override val fir: FirExpression, val loop: FirLoop, level: Int, id: Int) : CFGNode<FirExpression>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitLoopConditionEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class LoopConditionExitNode(owner: ControlFlowGraph, override val fir: FirExpression, level: Int, id: Int) : CFGNode<FirExpression>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitLoopConditionExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class LoopExitNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int, id: Int) : CFGNode<FirLoop>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitLoopExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Try-catch-finally -----------------------------------
|
||||
|
||||
class TryExpressionEnterNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int, id: Int) : CFGNode<FirTryExpression>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitTryExpressionEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class TryMainBlockEnterNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int, id: Int) : CFGNode<FirTryExpression>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitTryMainBlockEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class TryMainBlockExitNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int, id: Int) : CFGNode<FirTryExpression>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitTryMainBlockExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class CatchClauseEnterNode(owner: ControlFlowGraph, override val fir: FirCatch, level: Int, id: Int) : CFGNode<FirCatch>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitCatchClauseEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class CatchClauseExitNode(owner: ControlFlowGraph, override val fir: FirCatch, level: Int, id: Int) : CFGNode<FirCatch>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitCatchClauseExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class FinallyBlockEnterNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int, id: Int) : CFGNode<FirTryExpression>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFinallyBlockEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class FinallyBlockExitNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int, id: Int) : CFGNode<FirTryExpression>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFinallyBlockExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class FinallyProxyEnterNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int, id: Int) : CFGNode<FirTryExpression>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFinallyProxyEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class FinallyProxyExitNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int, id: Int) : CFGNode<FirTryExpression>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFinallyProxyExitNode(this, data)
|
||||
}
|
||||
}
|
||||
class TryExpressionExitNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int, id: Int) : CFGNode<FirTryExpression>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitTryExpressionExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Boolean operators -----------------------------------
|
||||
|
||||
abstract class AbstractBinaryExitNode<T : FirElement>(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<T>(owner, level, id) {
|
||||
val leftOperandNode: CFGNode<*> get() = previousNodes[0]
|
||||
val rightOperandNode: CFGNode<*> get() = previousNodes[1]
|
||||
}
|
||||
|
||||
class BinaryAndEnterNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int, id: Int) : CFGNode<FirBinaryLogicExpression>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitBinaryAndEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class BinaryAndExitLeftOperandNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int, id: Int) : CFGNode<FirBinaryLogicExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitBinaryAndExitLeftOperandNode(this, data)
|
||||
}
|
||||
}
|
||||
class BinaryAndEnterRightOperandNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int, id: Int) : CFGNode<FirBinaryLogicExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitBinaryAndEnterRightOperandNode(this, data)
|
||||
}
|
||||
}
|
||||
class BinaryAndExitNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int, id: Int) : AbstractBinaryExitNode<FirBinaryLogicExpression>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitBinaryAndExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryOrEnterNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int, id: Int) : CFGNode<FirBinaryLogicExpression>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitBinaryOrEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
class BinaryOrExitLeftOperandNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int, id: Int) : CFGNode<FirBinaryLogicExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitBinaryOrExitLeftOperandNode(this, data)
|
||||
}
|
||||
}
|
||||
class BinaryOrEnterRightOperandNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int, id: Int) : CFGNode<FirBinaryLogicExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitBinaryOrEnterRightOperandNode(this, data)
|
||||
}
|
||||
}
|
||||
class BinaryOrExitNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int, id: Int) : AbstractBinaryExitNode<FirBinaryLogicExpression>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitBinaryOrExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Operator call -----------------------------------
|
||||
|
||||
class TypeOperatorCallNode(owner: ControlFlowGraph, override val fir: FirTypeOperatorCall, level: Int, id: Int) : CFGNode<FirTypeOperatorCall>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitTypeOperatorCallNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class ComparisonExpressionNode(owner: ControlFlowGraph, override val fir: FirComparisonExpression, level: Int, id: Int) : CFGNode<FirComparisonExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitComparisonExpressionNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class EqualityOperatorCallNode(owner: ControlFlowGraph, override val fir: FirEqualityOperatorCall, level: Int, id: Int) : AbstractBinaryExitNode<FirEqualityOperatorCall>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitEqualityOperatorCallNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Jump -----------------------------------
|
||||
|
||||
class JumpNode(owner: ControlFlowGraph, override val fir: FirJump<*>, level: Int, id: Int) : CFGNode<FirJump<*>>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitJumpNode(this, data)
|
||||
}
|
||||
}
|
||||
class ConstExpressionNode(owner: ControlFlowGraph, override val fir: FirConstExpression<*>, level: Int, id: Int) : CFGNode<FirConstExpression<*>>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitConstExpressionNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Check not null call -----------------------------------
|
||||
|
||||
class CheckNotNullCallNode(owner: ControlFlowGraph, override val fir: FirCheckNotNullCall, level: Int, id: Int) : CFGNode<FirCheckNotNullCall>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitCheckNotNullCallNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Resolvable call -----------------------------------
|
||||
|
||||
class QualifiedAccessNode(
|
||||
owner: ControlFlowGraph,
|
||||
override val fir: FirQualifiedAccessExpression,
|
||||
level: Int, id: Int
|
||||
) : CFGNode<FirQualifiedAccessExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitQualifiedAccessNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class ResolvedQualifierNode(
|
||||
owner: ControlFlowGraph,
|
||||
override val fir: FirResolvedQualifier,
|
||||
level: Int, id: Int
|
||||
) : CFGNode<FirResolvedQualifier>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitResolvedQualifierNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionCallNode(
|
||||
owner: ControlFlowGraph,
|
||||
override val fir: FirFunctionCall,
|
||||
level: Int, id: Int
|
||||
) : CFGNode<FirFunctionCall>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFunctionCallNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class CallableReferenceNode(
|
||||
owner: ControlFlowGraph,
|
||||
override val fir: FirCallableReferenceAccess,
|
||||
level: Int, id: Int
|
||||
) : CFGNode<FirCallableReferenceAccess>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitCallableReferenceNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class GetClassCallNode(owner: ControlFlowGraph, override val fir: FirGetClassCall, level: Int, id: Int) : CFGNode<FirGetClassCall>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitGetClassCallNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class DelegatedConstructorCallNode(
|
||||
owner: ControlFlowGraph,
|
||||
override val fir: FirDelegatedConstructorCall,
|
||||
level: Int,
|
||||
id: Int
|
||||
) : CFGNode<FirDelegatedConstructorCall>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitDelegatedConstructorCallNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class StringConcatenationCallNode(
|
||||
owner: ControlFlowGraph,
|
||||
override val fir: FirStringConcatenationCall,
|
||||
level: Int,
|
||||
id: Int
|
||||
) : CFGNode<FirStringConcatenationCall>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitStringConcatenationCallNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class ThrowExceptionNode(
|
||||
owner: ControlFlowGraph,
|
||||
override val fir: FirThrowExpression,
|
||||
level: Int, id: Int
|
||||
) : CFGNode<FirThrowExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitThrowExceptionNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class StubNode(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<FirStub>(owner, level, id) {
|
||||
init {
|
||||
isDead = true
|
||||
}
|
||||
|
||||
override val fir: FirStub get() = FirStub
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitStubNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class ContractDescriptionEnterNode(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<FirStub>(owner, level, id) {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
override val fir: FirStub = FirStub
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitContractDescriptionEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class VariableDeclarationNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode<FirProperty>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitVariableDeclarationNode(this, data)
|
||||
}
|
||||
}
|
||||
class VariableAssignmentNode(owner: ControlFlowGraph, override val fir: FirVariableAssignment, level: Int, id: Int) : CFGNode<FirVariableAssignment>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitVariableAssignmentNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class EnterContractNode(owner: ControlFlowGraph, override val fir: FirQualifiedAccess, level: Int, id: Int) : CFGNode<FirQualifiedAccess>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitEnterContractNode(this, data)
|
||||
}
|
||||
}
|
||||
class ExitContractNode(owner: ControlFlowGraph, override val fir: FirQualifiedAccess, level: Int, id: Int) : CFGNode<FirQualifiedAccess>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitExitContractNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class EnterSafeCallNode(owner: ControlFlowGraph, override val fir: FirSafeCallExpression, level: Int, id: Int) : CFGNode<FirSafeCallExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitEnterSafeCallNode(this, data)
|
||||
}
|
||||
}
|
||||
class ExitSafeCallNode(owner: ControlFlowGraph, override val fir: FirSafeCallExpression, level: Int, id: Int) : CFGNode<FirSafeCallExpression>(owner, level, id) {
|
||||
val lastPreviousNode: CFGNode<*> get() = previousNodes.last()
|
||||
val secondPreviousNode: CFGNode<*>? get() = previousNodes.getOrNull(1)
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitExitSafeCallNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Elvis -----------------------------------
|
||||
|
||||
class ElvisLhsExitNode(owner: ControlFlowGraph, override val fir: FirElvisExpression, level: Int, id: Int) : CFGNode<FirElvisExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitElvisLhsExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class ElvisLhsIsNotNullNode(owner: ControlFlowGraph, override val fir: FirElvisExpression, level: Int, id: Int) : CFGNode<FirElvisExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitElvisLhsIsNotNullNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class ElvisRhsEnterNode(owner: ControlFlowGraph, override val fir: FirElvisExpression, level: Int, id: Int) : CFGNode<FirElvisExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitElvisRhsEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class ElvisExitNode(owner: ControlFlowGraph, override val fir: FirElvisExpression, level: Int, id: Int) : AbstractBinaryExitNode<FirElvisExpression>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitElvisExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Other -----------------------------------
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class AnnotationEnterNode(owner: ControlFlowGraph, override val fir: FirAnnotation, level: Int, id: Int) : CFGNode<FirAnnotation>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitAnnotationEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class AnnotationExitNode(owner: ControlFlowGraph, override val fir: FirAnnotation, level: Int, id: Int) : CFGNode<FirAnnotation>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitAnnotationExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Stub -----------------------------------
|
||||
|
||||
object FirStub : FirElement {
|
||||
override val source: FirSourceElement? get() = null
|
||||
|
||||
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {}
|
||||
|
||||
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement {
|
||||
return this
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.resolve.dfa.cfg
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirRenderer
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirDoWhileLoop
|
||||
import org.jetbrains.kotlin.fir.expressions.FirLoop
|
||||
import org.jetbrains.kotlin.fir.expressions.FirWhileLoop
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
|
||||
fun CFGNode<*>.render(): String =
|
||||
buildString {
|
||||
append(
|
||||
when (this@render) {
|
||||
is FunctionEnterNode -> "Enter function \"${fir.name()}\""
|
||||
is FunctionExitNode -> "Exit function \"${fir.name()}\""
|
||||
is LocalFunctionDeclarationNode -> "Local function declaration ${owner.name}"
|
||||
|
||||
is BlockEnterNode -> "Enter block"
|
||||
is BlockExitNode -> "Exit block"
|
||||
|
||||
is WhenEnterNode -> "Enter when"
|
||||
is WhenBranchConditionEnterNode -> "Enter when branch condition ${if (fir.condition is FirElseIfTrueCondition) "\"else\"" else ""}"
|
||||
is WhenBranchConditionExitNode -> "Exit when branch condition"
|
||||
is WhenBranchResultEnterNode -> "Enter when branch result"
|
||||
is WhenBranchResultExitNode -> "Exit when branch result"
|
||||
is WhenSyntheticElseBranchNode -> "Synthetic else branch"
|
||||
is WhenExitNode -> "Exit when"
|
||||
|
||||
is LoopEnterNode -> "Enter ${fir.type()} loop"
|
||||
is LoopBlockEnterNode -> "Enter loop block"
|
||||
is LoopBlockExitNode -> "Exit loop block"
|
||||
is LoopConditionEnterNode -> "Enter loop condition"
|
||||
is LoopConditionExitNode -> "Exit loop condition"
|
||||
is LoopExitNode -> "Exit ${fir.type()}loop"
|
||||
|
||||
is QualifiedAccessNode -> "Access variable ${fir.calleeReference.render(CfgRenderMode)}"
|
||||
is ResolvedQualifierNode -> "Access qualifier ${fir.classId}"
|
||||
is ComparisonExpressionNode -> "Comparison ${fir.operation.operator}"
|
||||
is TypeOperatorCallNode -> "Type operator: \"${fir.render(CfgRenderMode)}\""
|
||||
is EqualityOperatorCallNode -> "Equality operator ${fir.operation.operator}"
|
||||
is JumpNode -> "Jump: ${fir.render()}"
|
||||
is StubNode -> "Stub"
|
||||
is CheckNotNullCallNode -> "Check not null: ${fir.render(CfgRenderMode)}"
|
||||
|
||||
is ConstExpressionNode -> "Const: ${fir.render()}"
|
||||
is VariableDeclarationNode ->
|
||||
"Variable declaration: ${buildString {
|
||||
FirRenderer(
|
||||
this,
|
||||
CfgRenderMode
|
||||
).visitCallableDeclaration(fir)
|
||||
}}"
|
||||
|
||||
is VariableAssignmentNode -> "Assignment: ${fir.lValue.render(CfgRenderMode)}"
|
||||
is FunctionCallNode -> "Function call: ${fir.render(CfgRenderMode)}"
|
||||
is DelegatedConstructorCallNode -> "Delegated constructor call: ${fir.render(CfgRenderMode)}"
|
||||
is StringConcatenationCallNode -> "String concatenation call: ${fir.render(CfgRenderMode)}"
|
||||
is ThrowExceptionNode -> "Throw: ${fir.render(CfgRenderMode)}"
|
||||
|
||||
is TryExpressionEnterNode -> "Try expression enter"
|
||||
is TryMainBlockEnterNode -> "Try main block enter"
|
||||
is TryMainBlockExitNode -> "Try main block exit"
|
||||
is CatchClauseEnterNode -> "Catch enter"
|
||||
is CatchClauseExitNode -> "Catch exit"
|
||||
is FinallyBlockEnterNode -> "Enter finally"
|
||||
is FinallyBlockExitNode -> "Exit finally"
|
||||
is FinallyProxyEnterNode -> TODO()
|
||||
is FinallyProxyExitNode -> TODO()
|
||||
is TryExpressionExitNode -> "Try expression exit"
|
||||
|
||||
is BinaryAndEnterNode -> "Enter &&"
|
||||
is BinaryAndExitLeftOperandNode -> "Exit left part of &&"
|
||||
is BinaryAndEnterRightOperandNode -> "Enter right part of &&"
|
||||
is BinaryAndExitNode -> "Exit &&"
|
||||
is BinaryOrEnterNode -> "Enter ||"
|
||||
is BinaryOrExitLeftOperandNode -> "Exit left part of ||"
|
||||
is BinaryOrEnterRightOperandNode -> "Enter right part of ||"
|
||||
is BinaryOrExitNode -> "Exit ||"
|
||||
|
||||
is PartOfClassInitializationNode -> "Part of class initialization"
|
||||
is PropertyInitializerEnterNode -> "Enter property"
|
||||
is PropertyInitializerExitNode -> "Exit property"
|
||||
is FieldInitializerEnterNode -> "Enter field"
|
||||
is FieldInitializerExitNode -> "Exit field"
|
||||
is InitBlockEnterNode -> "Enter init block"
|
||||
is InitBlockExitNode -> "Exit init block"
|
||||
is AnnotationEnterNode -> "Enter annotation"
|
||||
is AnnotationExitNode -> "Exit annotation"
|
||||
|
||||
is EnterContractNode -> "Enter contract"
|
||||
is ExitContractNode -> "Exit contract"
|
||||
|
||||
is EnterSafeCallNode -> "Enter safe call"
|
||||
is ExitSafeCallNode -> "Exit safe call"
|
||||
|
||||
is PostponedLambdaEnterNode -> "Postponed enter to lambda"
|
||||
is PostponedLambdaExitNode -> "Postponed exit from lambda"
|
||||
|
||||
is AnonymousFunctionExpressionExitNode -> "Exit anonymous function expression"
|
||||
|
||||
is UnionFunctionCallArgumentsNode -> "Call arguments union"
|
||||
|
||||
is ClassEnterNode -> "Enter class ${owner.name}"
|
||||
is ClassExitNode -> "Exit class ${owner.name}"
|
||||
is LocalClassExitNode -> "Exit local class ${owner.name}"
|
||||
is AnonymousObjectExitNode -> "Exit anonymous object"
|
||||
is AnonymousObjectExpressionExitNode -> "Exit anonymous object expression"
|
||||
|
||||
is ContractDescriptionEnterNode -> "Enter contract description"
|
||||
|
||||
is EnterDefaultArgumentsNode -> "Enter default value of ${fir.name}"
|
||||
is ExitDefaultArgumentsNode -> "Exit default value of ${fir.name}"
|
||||
|
||||
is ElvisLhsExitNode -> "Exit lhs of ?:"
|
||||
is ElvisLhsIsNotNullNode -> "Lhs of ?: is not null"
|
||||
is ElvisRhsEnterNode -> "Enter rhs of ?:"
|
||||
is ElvisExitNode -> "Exit ?:"
|
||||
|
||||
is CallableReferenceNode -> "Callable reference: ${fir.render(CfgRenderMode)}"
|
||||
is GetClassCallNode -> "::class call"
|
||||
|
||||
is AbstractBinaryExitNode -> throw IllegalStateException()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private val CfgRenderMode = FirRenderer.RenderMode(
|
||||
renderLambdaBodies = false,
|
||||
renderCallArguments = false,
|
||||
renderCallableFqNames = false,
|
||||
renderDeclarationResolvePhase = false,
|
||||
renderAnnotation = false,
|
||||
)
|
||||
|
||||
private fun FirFunction.name(): String = when (this) {
|
||||
is FirSimpleFunction -> name.asString()
|
||||
is FirAnonymousFunction -> "anonymousFunction"
|
||||
is FirConstructor -> "<init>"
|
||||
is FirPropertyAccessor -> if (isGetter) "getter" else "setter"
|
||||
is FirErrorFunction -> "errorFunction"
|
||||
else -> TODO(toString())
|
||||
}
|
||||
|
||||
private fun FirLoop.type(): String = when (this) {
|
||||
is FirWhileLoop -> "while"
|
||||
is FirDoWhileLoop -> "do-while"
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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.resolve.dfa.cfg
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
|
||||
class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val kind: Kind) {
|
||||
private var _nodes: MutableList<CFGNode<*>> = mutableListOf()
|
||||
|
||||
val nodes: List<CFGNode<*>>
|
||||
get() = _nodes
|
||||
|
||||
internal fun addNode(node: CFGNode<*>) {
|
||||
assertState(State.Building)
|
||||
_nodes.add(node)
|
||||
}
|
||||
|
||||
@set:CfgInternals
|
||||
lateinit var enterNode: CFGNode<*>
|
||||
|
||||
@set:CfgInternals
|
||||
lateinit var exitNode: CFGNode<*>
|
||||
|
||||
var owner: ControlFlowGraph? = null
|
||||
private set
|
||||
|
||||
var state: State = State.Building
|
||||
private set
|
||||
|
||||
private val _subGraphs: MutableList<ControlFlowGraph> = mutableListOf()
|
||||
val subGraphs: List<ControlFlowGraph> get() = _subGraphs
|
||||
|
||||
@CfgInternals
|
||||
fun complete() {
|
||||
assertState(State.Building)
|
||||
state = State.Completed
|
||||
if (kind == Kind.Stub) return
|
||||
val sortedNodes = orderNodes()
|
||||
// TODO Fix this
|
||||
// assert(sortedNodes.size == _nodes.size)
|
||||
// for (node in _nodes) {
|
||||
// assert(node in sortedNodes)
|
||||
// }
|
||||
_nodes.clear()
|
||||
_nodes.addAll(sortedNodes)
|
||||
}
|
||||
|
||||
@CfgInternals
|
||||
fun addSubGraph(graph: ControlFlowGraph) {
|
||||
assert(graph.owner == null) {
|
||||
"SubGraph already has owner"
|
||||
}
|
||||
graph.owner = this
|
||||
_subGraphs += graph
|
||||
}
|
||||
|
||||
@CfgInternals
|
||||
fun removeSubGraph(graph: ControlFlowGraph) {
|
||||
assert(graph.owner == this)
|
||||
_subGraphs.remove(graph)
|
||||
graph.owner = null
|
||||
|
||||
CFGNode.removeAllIncomingEdges(graph.enterNode)
|
||||
CFGNode.removeAllOutgoingEdges(graph.exitNode)
|
||||
}
|
||||
|
||||
private fun assertState(state: State) {
|
||||
assert(this.state == state) {
|
||||
"This action can not be performed at $this state"
|
||||
}
|
||||
}
|
||||
|
||||
enum class State {
|
||||
Building,
|
||||
Completed;
|
||||
}
|
||||
|
||||
enum class Kind(val withBody: Boolean) {
|
||||
Function(withBody = true),
|
||||
AnonymousFunction(withBody = true),
|
||||
ClassInitializer(withBody = true),
|
||||
PropertyInitializer(withBody = true),
|
||||
FieldInitializer(withBody = true),
|
||||
TopLevel(withBody = false),
|
||||
AnnotationCall(withBody = true),
|
||||
DefaultArgument(withBody = false),
|
||||
Stub(withBody = true)
|
||||
}
|
||||
}
|
||||
|
||||
data class Edge(
|
||||
val label: EdgeLabel,
|
||||
val kind: EdgeKind,
|
||||
) {
|
||||
companion object {
|
||||
val Normal_Forward = Edge(NormalPath, EdgeKind.Forward)
|
||||
private val Normal_DeadForward = Edge(NormalPath, EdgeKind.DeadForward)
|
||||
private val Normal_DfgForward = Edge(NormalPath, EdgeKind.DfgForward)
|
||||
private val Normal_CfgForward = Edge(NormalPath, EdgeKind.CfgForward)
|
||||
private val Normal_CfgBackward = Edge(NormalPath, EdgeKind.CfgBackward)
|
||||
private val Normal_DeadBackward = Edge(NormalPath, EdgeKind.DeadBackward)
|
||||
|
||||
fun create(label: EdgeLabel, kind: EdgeKind): Edge =
|
||||
when (label) {
|
||||
NormalPath -> {
|
||||
when (kind) {
|
||||
EdgeKind.Forward -> Normal_Forward
|
||||
EdgeKind.DeadForward -> Normal_DeadForward
|
||||
EdgeKind.DfgForward -> Normal_DfgForward
|
||||
EdgeKind.CfgForward -> Normal_CfgForward
|
||||
EdgeKind.CfgBackward -> Normal_CfgBackward
|
||||
EdgeKind.DeadBackward -> Normal_DeadBackward
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Edge(label, kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class EdgeLabel(val label: String?) {
|
||||
open val isNormal: Boolean
|
||||
get() = false
|
||||
|
||||
override fun toString(): String {
|
||||
return label ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
object NormalPath : EdgeLabel(label = null) {
|
||||
override val isNormal: Boolean
|
||||
get() = true
|
||||
}
|
||||
|
||||
object LoopBackPath : EdgeLabel(label = null) {
|
||||
override val isNormal: Boolean
|
||||
get() = true
|
||||
}
|
||||
|
||||
object UncaughtExceptionPath : EdgeLabel(label = "onUncaughtException")
|
||||
|
||||
// TODO: Label `return`ing edge with this.
|
||||
class ReturnPath(
|
||||
returnTargetSymbol: FirFunctionSymbol<*>
|
||||
) : EdgeLabel(label = "\"return@${returnTargetSymbol.callableId}\"")
|
||||
|
||||
enum class EdgeKind(
|
||||
val usedInDfa: Boolean,
|
||||
val usedInCfa: Boolean,
|
||||
val isBack: Boolean,
|
||||
val isDead: Boolean
|
||||
) {
|
||||
Forward(usedInDfa = true, usedInCfa = true, isBack = false, isDead = false),
|
||||
DeadForward(usedInDfa = false, usedInCfa = true, isBack = false, isDead = true),
|
||||
DfgForward(usedInDfa = true, usedInCfa = false, isBack = false, isDead = false),
|
||||
CfgForward(usedInDfa = false, usedInCfa = true, isBack = false, isDead = false),
|
||||
CfgBackward(usedInDfa = false, usedInCfa = true, isBack = true, isDead = false),
|
||||
DeadBackward(usedInDfa = false, usedInCfa = true, isBack = true, isDead = true)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun ControlFlowGraph.orderNodes(): LinkedHashSet<CFGNode<*>> {
|
||||
val visitedNodes = linkedSetOf<CFGNode<*>>()
|
||||
/*
|
||||
* [delayedNodes] is needed to accomplish next order contract:
|
||||
* for each node all previous node lays before it
|
||||
*/
|
||||
val stack = ArrayDeque<CFGNode<*>>()
|
||||
stack.addFirst(enterNode)
|
||||
while (stack.isNotEmpty()) {
|
||||
val node = stack.removeFirst()
|
||||
val previousNodes = node.previousNodes
|
||||
if (previousNodes.any { it !in visitedNodes && it.owner == this && !node.incomingEdges.getValue(it).kind.isBack }) {
|
||||
stack.addLast(node)
|
||||
continue
|
||||
}
|
||||
if (!visitedNodes.add(node)) continue
|
||||
val followingNodes = node.followingNodes
|
||||
|
||||
for (followingNode in followingNodes) {
|
||||
if (followingNode.owner == this) {
|
||||
if (followingNode !in visitedNodes) {
|
||||
stack.addFirst(followingNode)
|
||||
}
|
||||
} else {
|
||||
walkThrowSubGraphs(followingNode.owner, visitedNodes, stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
return visitedNodes
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun ControlFlowGraph.walkThrowSubGraphs(
|
||||
otherGraph: ControlFlowGraph,
|
||||
visitedNodes: Set<CFGNode<*>>,
|
||||
stack: ArrayDeque<CFGNode<*>>
|
||||
) {
|
||||
if (otherGraph.owner != this) return
|
||||
for (otherNode in otherGraph.exitNode.followingNodes) {
|
||||
if (otherNode.owner == this) {
|
||||
if (otherNode !in visitedNodes) {
|
||||
stack.addFirst(otherNode)
|
||||
}
|
||||
} else if (otherNode.owner != otherGraph) {
|
||||
walkThrowSubGraphs(otherNode.owner, visitedNodes, stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
+377
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* 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.resolve.dfa.cfg
|
||||
|
||||
abstract class ControlFlowGraphVisitor<out R, in D> {
|
||||
abstract fun visitNode(node: CFGNode<*>, data: D): R
|
||||
|
||||
// ----------------------------------- Simple function -----------------------------------
|
||||
|
||||
open fun visitFunctionEnterNode(node: FunctionEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitFunctionExitNode(node: FunctionExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitLocalFunctionDeclarationNode(node: LocalFunctionDeclarationNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Default arguments -----------------------------------
|
||||
|
||||
open fun visitExitDefaultArgumentsNode(node: ExitDefaultArgumentsNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitEnterDefaultArgumentsNode(node: EnterDefaultArgumentsNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Anonymous function -----------------------------------
|
||||
|
||||
open fun visitPostponedLambdaEnterNode(node: PostponedLambdaEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitPostponedLambdaExitNode(node: PostponedLambdaExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitUnionFunctionCallArgumentsNode(node: UnionFunctionCallArgumentsNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitAnonymousFunctionExpressionExitNode(node: AnonymousFunctionExpressionExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Classes -----------------------------------
|
||||
|
||||
open fun visitAnonymousObjectExitNode(node: AnonymousObjectExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitAnonymousObjectExpressionExitNode(node: AnonymousObjectExpressionExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitClassEnterNode(node: ClassEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitClassExitNode(node: ClassExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitLocalClassExitNode(node: LocalClassExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Initialization -----------------------------------
|
||||
|
||||
open fun visitPartOfClassInitializationNode(node: PartOfClassInitializationNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Property -----------------------------------
|
||||
|
||||
open fun visitPropertyInitializerEnterNode(node: PropertyInitializerEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitPropertyInitializerExitNode(node: PropertyInitializerExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Field -----------------------------------
|
||||
|
||||
open fun visitFieldInitializerEnterNode(node: FieldInitializerEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitFieldInitializerExitNode(node: FieldInitializerExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Init -----------------------------------
|
||||
|
||||
open fun visitInitBlockEnterNode(node: InitBlockEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitInitBlockExitNode(node: InitBlockExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Block -----------------------------------
|
||||
|
||||
open fun visitBlockEnterNode(node: BlockEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitBlockExitNode(node: BlockExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- When -----------------------------------
|
||||
|
||||
open fun visitWhenEnterNode(node: WhenEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitWhenExitNode(node: WhenExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitWhenBranchConditionEnterNode(node: WhenBranchConditionEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitWhenBranchConditionExitNode(node: WhenBranchConditionExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitWhenBranchResultEnterNode(node: WhenBranchResultEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitWhenBranchResultExitNode(node: WhenBranchResultExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitWhenSyntheticElseBranchNode(node: WhenSyntheticElseBranchNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------- Loop -----------------------------------
|
||||
|
||||
open fun visitLoopEnterNode(node: LoopEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitLoopBlockEnterNode(node: LoopBlockEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitLoopBlockExitNode(node: LoopBlockExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitLoopConditionEnterNode(node: LoopConditionEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitLoopConditionExitNode(node: LoopConditionExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitLoopExitNode(node: LoopExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Try-catch-finally -----------------------------------
|
||||
|
||||
open fun visitTryExpressionEnterNode(node: TryExpressionEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitTryMainBlockEnterNode(node: TryMainBlockEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitTryMainBlockExitNode(node: TryMainBlockExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitCatchClauseEnterNode(node: CatchClauseEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitCatchClauseExitNode(node: CatchClauseExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitFinallyBlockEnterNode(node: FinallyBlockEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitFinallyBlockExitNode(node: FinallyBlockExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitFinallyProxyEnterNode(node: FinallyProxyEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitFinallyProxyExitNode(node: FinallyProxyExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitTryExpressionExitNode(node: TryExpressionExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Boolean operators -----------------------------------
|
||||
|
||||
open fun visitBinaryAndEnterNode(node: BinaryAndEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitBinaryAndExitLeftOperandNode(node: BinaryAndExitLeftOperandNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitBinaryAndEnterRightOperandNode(node: BinaryAndEnterRightOperandNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitBinaryAndExitNode(node: BinaryAndExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitBinaryOrEnterNode(node: BinaryOrEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitBinaryOrExitLeftOperandNode(node: BinaryOrExitLeftOperandNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitBinaryOrEnterRightOperandNode(node: BinaryOrEnterRightOperandNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitBinaryOrExitNode(node: BinaryOrExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Operator call -----------------------------------
|
||||
|
||||
open fun visitTypeOperatorCallNode(node: TypeOperatorCallNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitComparisonExpressionNode(node: ComparisonExpressionNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitEqualityOperatorCallNode(node: EqualityOperatorCallNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Jump -----------------------------------
|
||||
|
||||
open fun visitJumpNode(node: JumpNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitConstExpressionNode(node: ConstExpressionNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Check not null call -----------------------------------
|
||||
|
||||
open fun visitCheckNotNullCallNode(node: CheckNotNullCallNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Resolvable call -----------------------------------
|
||||
|
||||
open fun visitQualifiedAccessNode(node: QualifiedAccessNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitResolvedQualifierNode(node: ResolvedQualifierNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitFunctionCallNode(node: FunctionCallNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitCallableReferenceNode(node: CallableReferenceNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitGetClassCallNode(node: GetClassCallNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitDelegatedConstructorCallNode(node: DelegatedConstructorCallNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitStringConcatenationCallNode(node: StringConcatenationCallNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitThrowExceptionNode(node: ThrowExceptionNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitStubNode(node: StubNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitContractDescriptionEnterNode(node: ContractDescriptionEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitVariableDeclarationNode(node: VariableDeclarationNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitVariableAssignmentNode(node: VariableAssignmentNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitEnterContractNode(node: EnterContractNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitExitContractNode(node: ExitContractNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitEnterSafeCallNode(node: EnterSafeCallNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitExitSafeCallNode(node: ExitSafeCallNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Elvis -----------------------------------
|
||||
|
||||
open fun visitElvisLhsExitNode(node: ElvisLhsExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitElvisLhsIsNotNullNode(node: ElvisLhsIsNotNullNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitElvisRhsEnterNode(node: ElvisRhsEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitElvisExitNode(node: ElvisExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
// ----------------------------------- Other -----------------------------------
|
||||
|
||||
open fun visitAnnotationEnterNode(node: AnnotationEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitAnnotationExitNode(node: AnnotationExitNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
}
|
||||
+590
@@ -0,0 +1,590 @@
|
||||
/*
|
||||
* 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.resolve.dfa.cfg
|
||||
|
||||
abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothing?>() {
|
||||
abstract fun visitNode(node: CFGNode<*>)
|
||||
|
||||
// ----------------------------------- Simple function -----------------------------------
|
||||
|
||||
open fun visitFunctionEnterNode(node: FunctionEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitFunctionExitNode(node: FunctionExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Anonymous function -----------------------------------
|
||||
|
||||
open fun visitPostponedLambdaEnterNode(node: PostponedLambdaEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitPostponedLambdaExitNode(node: PostponedLambdaExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitUnionFunctionCallArgumentsNode(node: UnionFunctionCallArgumentsNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Anonymous object -----------------------------------
|
||||
|
||||
open fun visitAnonymousObjectExitNode(node: AnonymousObjectExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Property -----------------------------------
|
||||
|
||||
open fun visitPropertyInitializerEnterNode(node: PropertyInitializerEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitPropertyInitializerExitNode(node: PropertyInitializerExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Init -----------------------------------
|
||||
|
||||
open fun visitInitBlockEnterNode(node: InitBlockEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitInitBlockExitNode(node: InitBlockExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Block -----------------------------------
|
||||
|
||||
open fun visitBlockEnterNode(node: BlockEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitBlockExitNode(node: BlockExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- When -----------------------------------
|
||||
|
||||
open fun visitWhenEnterNode(node: WhenEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitWhenExitNode(node: WhenExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitWhenBranchConditionEnterNode(node: WhenBranchConditionEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitWhenBranchConditionExitNode(node: WhenBranchConditionExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitWhenBranchResultEnterNode(node: WhenBranchResultEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitWhenBranchResultExitNode(node: WhenBranchResultExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitWhenSyntheticElseBranchNode(node: WhenSyntheticElseBranchNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------- Loop -----------------------------------
|
||||
|
||||
open fun visitLoopEnterNode(node: LoopEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitLoopBlockEnterNode(node: LoopBlockEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitLoopBlockExitNode(node: LoopBlockExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitLoopConditionEnterNode(node: LoopConditionEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitLoopConditionExitNode(node: LoopConditionExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitLoopExitNode(node: LoopExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Try-catch-finally -----------------------------------
|
||||
|
||||
open fun visitTryExpressionEnterNode(node: TryExpressionEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitTryMainBlockEnterNode(node: TryMainBlockEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitTryMainBlockExitNode(node: TryMainBlockExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitCatchClauseEnterNode(node: CatchClauseEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitCatchClauseExitNode(node: CatchClauseExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitFinallyBlockEnterNode(node: FinallyBlockEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitFinallyBlockExitNode(node: FinallyBlockExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitFinallyProxyEnterNode(node: FinallyProxyEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitFinallyProxyExitNode(node: FinallyProxyExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitTryExpressionExitNode(node: TryExpressionExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Boolean operators -----------------------------------
|
||||
|
||||
open fun visitBinaryAndEnterNode(node: BinaryAndEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitBinaryAndExitLeftOperandNode(node: BinaryAndExitLeftOperandNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitBinaryAndEnterRightOperandNode(node: BinaryAndEnterRightOperandNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitBinaryAndExitNode(node: BinaryAndExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitBinaryOrEnterNode(node: BinaryOrEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitBinaryOrExitLeftOperandNode(node: BinaryOrExitLeftOperandNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitBinaryOrEnterRightOperandNode(node: BinaryOrEnterRightOperandNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitBinaryOrExitNode(node: BinaryOrExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Operator call -----------------------------------
|
||||
|
||||
open fun visitTypeOperatorCallNode(node: TypeOperatorCallNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitComparisonExpressionNode(node: ComparisonExpressionNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitEqualityOperatorCallNode(node: EqualityOperatorCallNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Jump -----------------------------------
|
||||
|
||||
open fun visitJumpNode(node: JumpNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitConstExpressionNode(node: ConstExpressionNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Check not null call -----------------------------------
|
||||
|
||||
open fun visitCheckNotNullCallNode(node: CheckNotNullCallNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Resolvable call -----------------------------------
|
||||
|
||||
open fun visitQualifiedAccessNode(node: QualifiedAccessNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitResolvedQualifierNode(node: ResolvedQualifierNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitFunctionCallNode(node: FunctionCallNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitDelegatedConstructorCallNode(node: DelegatedConstructorCallNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitStringConcatenationCallNode(node: StringConcatenationCallNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitThrowExceptionNode(node: ThrowExceptionNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitStubNode(node: StubNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitVariableDeclarationNode(node: VariableDeclarationNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitEnterContractNode(node: EnterContractNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitExitContractNode(node: ExitContractNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitEnterSafeCallNode(node: EnterSafeCallNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitExitSafeCallNode(node: ExitSafeCallNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Other -----------------------------------
|
||||
|
||||
open fun visitAnnotationEnterNode(node: AnnotationEnterNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
open fun visitAnnotationExitNode(node: AnnotationExitNode) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
final override fun visitNode(node: CFGNode<*>, data: Nothing?) {
|
||||
visitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Simple function -----------------------------------
|
||||
|
||||
final override fun visitFunctionEnterNode(node: FunctionEnterNode, data: Nothing?) {
|
||||
visitFunctionEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitFunctionExitNode(node: FunctionExitNode, data: Nothing?) {
|
||||
visitFunctionExitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Anonymous function -----------------------------------
|
||||
|
||||
final override fun visitPostponedLambdaEnterNode(node: PostponedLambdaEnterNode, data: Nothing?) {
|
||||
visitPostponedLambdaEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitPostponedLambdaExitNode(node: PostponedLambdaExitNode, data: Nothing?) {
|
||||
visitPostponedLambdaExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitUnionFunctionCallArgumentsNode(node: UnionFunctionCallArgumentsNode, data: Nothing?) {
|
||||
visitUnionFunctionCallArgumentsNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Anonymous object -----------------------------------
|
||||
|
||||
final override fun visitAnonymousObjectExitNode(node: AnonymousObjectExitNode, data: Nothing?) {
|
||||
visitAnonymousObjectExitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Property -----------------------------------
|
||||
|
||||
final override fun visitPropertyInitializerEnterNode(node: PropertyInitializerEnterNode, data: Nothing?) {
|
||||
visitPropertyInitializerEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitPropertyInitializerExitNode(node: PropertyInitializerExitNode, data: Nothing?) {
|
||||
visitPropertyInitializerExitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Init -----------------------------------
|
||||
|
||||
final override fun visitInitBlockEnterNode(node: InitBlockEnterNode, data: Nothing?) {
|
||||
visitInitBlockEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitInitBlockExitNode(node: InitBlockExitNode, data: Nothing?) {
|
||||
visitInitBlockExitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Block -----------------------------------
|
||||
|
||||
final override fun visitBlockEnterNode(node: BlockEnterNode, data: Nothing?) {
|
||||
visitBlockEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitBlockExitNode(node: BlockExitNode, data: Nothing?) {
|
||||
visitBlockExitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- When -----------------------------------
|
||||
|
||||
final override fun visitWhenEnterNode(node: WhenEnterNode, data: Nothing?) {
|
||||
visitWhenEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitWhenExitNode(node: WhenExitNode, data: Nothing?) {
|
||||
visitWhenExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitWhenBranchConditionEnterNode(node: WhenBranchConditionEnterNode, data: Nothing?) {
|
||||
visitWhenBranchConditionEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitWhenBranchConditionExitNode(node: WhenBranchConditionExitNode, data: Nothing?) {
|
||||
visitWhenBranchConditionExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitWhenBranchResultEnterNode(node: WhenBranchResultEnterNode, data: Nothing?) {
|
||||
visitWhenBranchResultEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitWhenBranchResultExitNode(node: WhenBranchResultExitNode, data: Nothing?) {
|
||||
visitWhenBranchResultExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitWhenSyntheticElseBranchNode(node: WhenSyntheticElseBranchNode, data: Nothing?) {
|
||||
visitWhenSyntheticElseBranchNode(node)
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------- Loop -----------------------------------
|
||||
|
||||
final override fun visitLoopEnterNode(node: LoopEnterNode, data: Nothing?) {
|
||||
visitLoopEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitLoopBlockEnterNode(node: LoopBlockEnterNode, data: Nothing?) {
|
||||
visitLoopBlockEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitLoopBlockExitNode(node: LoopBlockExitNode, data: Nothing?) {
|
||||
visitLoopBlockExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitLoopConditionEnterNode(node: LoopConditionEnterNode, data: Nothing?) {
|
||||
visitLoopConditionEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitLoopConditionExitNode(node: LoopConditionExitNode, data: Nothing?) {
|
||||
visitLoopConditionExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitLoopExitNode(node: LoopExitNode, data: Nothing?) {
|
||||
visitLoopExitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Try-catch-finally -----------------------------------
|
||||
|
||||
final override fun visitTryExpressionEnterNode(node: TryExpressionEnterNode, data: Nothing?) {
|
||||
visitTryExpressionEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitTryMainBlockEnterNode(node: TryMainBlockEnterNode, data: Nothing?) {
|
||||
visitTryMainBlockEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitTryMainBlockExitNode(node: TryMainBlockExitNode, data: Nothing?) {
|
||||
visitTryMainBlockExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitCatchClauseEnterNode(node: CatchClauseEnterNode, data: Nothing?) {
|
||||
visitCatchClauseEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitCatchClauseExitNode(node: CatchClauseExitNode, data: Nothing?) {
|
||||
visitCatchClauseExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitFinallyBlockEnterNode(node: FinallyBlockEnterNode, data: Nothing?) {
|
||||
visitFinallyBlockEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitFinallyBlockExitNode(node: FinallyBlockExitNode, data: Nothing?) {
|
||||
visitFinallyBlockExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitFinallyProxyEnterNode(node: FinallyProxyEnterNode, data: Nothing?) {
|
||||
visitFinallyProxyEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitFinallyProxyExitNode(node: FinallyProxyExitNode, data: Nothing?) {
|
||||
visitFinallyProxyExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitTryExpressionExitNode(node: TryExpressionExitNode, data: Nothing?) {
|
||||
visitTryExpressionExitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Boolean operators -----------------------------------
|
||||
|
||||
final override fun visitBinaryAndEnterNode(node: BinaryAndEnterNode, data: Nothing?) {
|
||||
visitBinaryAndEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitBinaryAndExitLeftOperandNode(node: BinaryAndExitLeftOperandNode, data: Nothing?) {
|
||||
visitBinaryAndExitLeftOperandNode(node)
|
||||
}
|
||||
|
||||
final override fun visitBinaryAndEnterRightOperandNode(node: BinaryAndEnterRightOperandNode, data: Nothing?) {
|
||||
visitBinaryAndEnterRightOperandNode(node)
|
||||
}
|
||||
|
||||
final override fun visitBinaryAndExitNode(node: BinaryAndExitNode, data: Nothing?) {
|
||||
visitBinaryAndExitNode(node)
|
||||
}
|
||||
|
||||
final override fun visitBinaryOrEnterNode(node: BinaryOrEnterNode, data: Nothing?) {
|
||||
visitBinaryOrEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitBinaryOrExitLeftOperandNode(node: BinaryOrExitLeftOperandNode, data: Nothing?) {
|
||||
visitBinaryOrExitLeftOperandNode(node)
|
||||
}
|
||||
|
||||
final override fun visitBinaryOrEnterRightOperandNode(node: BinaryOrEnterRightOperandNode, data: Nothing?) {
|
||||
visitBinaryOrEnterRightOperandNode(node)
|
||||
}
|
||||
|
||||
final override fun visitBinaryOrExitNode(node: BinaryOrExitNode, data: Nothing?) {
|
||||
visitBinaryOrExitNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Operator call -----------------------------------
|
||||
|
||||
final override fun visitTypeOperatorCallNode(node: TypeOperatorCallNode, data: Nothing?) {
|
||||
visitTypeOperatorCallNode(node)
|
||||
}
|
||||
|
||||
final override fun visitEqualityOperatorCallNode(node: EqualityOperatorCallNode, data: Nothing?) {
|
||||
visitEqualityOperatorCallNode(node)
|
||||
}
|
||||
|
||||
final override fun visitComparisonExpressionNode(node: ComparisonExpressionNode, data: Nothing?) {
|
||||
visitComparisonExpressionNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Jump -----------------------------------
|
||||
|
||||
final override fun visitJumpNode(node: JumpNode, data: Nothing?) {
|
||||
visitJumpNode(node)
|
||||
}
|
||||
|
||||
final override fun visitConstExpressionNode(node: ConstExpressionNode, data: Nothing?) {
|
||||
visitConstExpressionNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Check not null call -----------------------------------
|
||||
|
||||
final override fun visitCheckNotNullCallNode(node: CheckNotNullCallNode, data: Nothing?) {
|
||||
visitCheckNotNullCallNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Resolvable call -----------------------------------
|
||||
|
||||
final override fun visitQualifiedAccessNode(node: QualifiedAccessNode, data: Nothing?) {
|
||||
visitQualifiedAccessNode(node)
|
||||
}
|
||||
|
||||
final override fun visitResolvedQualifierNode(node: ResolvedQualifierNode, data: Nothing?) {
|
||||
visitResolvedQualifierNode(node)
|
||||
}
|
||||
|
||||
final override fun visitFunctionCallNode(node: FunctionCallNode, data: Nothing?) {
|
||||
visitFunctionCallNode(node)
|
||||
}
|
||||
|
||||
final override fun visitDelegatedConstructorCallNode(node: DelegatedConstructorCallNode, data: Nothing?) {
|
||||
visitDelegatedConstructorCallNode(node)
|
||||
}
|
||||
|
||||
final override fun visitStringConcatenationCallNode(node: StringConcatenationCallNode, data: Nothing?) {
|
||||
visitStringConcatenationCallNode(node)
|
||||
}
|
||||
|
||||
final override fun visitThrowExceptionNode(node: ThrowExceptionNode, data: Nothing?) {
|
||||
visitThrowExceptionNode(node)
|
||||
}
|
||||
|
||||
final override fun visitStubNode(node: StubNode, data: Nothing?) {
|
||||
visitStubNode(node)
|
||||
}
|
||||
|
||||
final override fun visitVariableDeclarationNode(node: VariableDeclarationNode, data: Nothing?) {
|
||||
visitVariableDeclarationNode(node)
|
||||
}
|
||||
|
||||
final override fun visitVariableAssignmentNode(node: VariableAssignmentNode, data: Nothing?) {
|
||||
visitVariableAssignmentNode(node)
|
||||
}
|
||||
|
||||
final override fun visitEnterContractNode(node: EnterContractNode, data: Nothing?) {
|
||||
visitEnterContractNode(node)
|
||||
}
|
||||
|
||||
final override fun visitExitContractNode(node: ExitContractNode, data: Nothing?) {
|
||||
visitExitContractNode(node)
|
||||
}
|
||||
|
||||
final override fun visitEnterSafeCallNode(node: EnterSafeCallNode, data: Nothing?) {
|
||||
visitEnterSafeCallNode(node)
|
||||
}
|
||||
|
||||
final override fun visitExitSafeCallNode(node: ExitSafeCallNode, data: Nothing?) {
|
||||
visitExitSafeCallNode(node)
|
||||
}
|
||||
|
||||
// ----------------------------------- Other -----------------------------------
|
||||
|
||||
final override fun visitAnnotationEnterNode(node: AnnotationEnterNode, data: Nothing?) {
|
||||
visitAnnotationEnterNode(node)
|
||||
}
|
||||
|
||||
final override fun visitAnnotationExitNode(node: AnnotationExitNode, data: Nothing?) {
|
||||
visitAnnotationExitNode(node)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassErrorType
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
// --------------------------------------- Facts ---------------------------------------
|
||||
|
||||
operator fun TypeStatement.plus(other: TypeStatement?): TypeStatement = other?.let { this + other } ?: this
|
||||
|
||||
class MutableTypeStatement(
|
||||
override val variable: RealVariable,
|
||||
override val exactType: MutableSet<ConeKotlinType> = linkedSetOf(),
|
||||
override val exactNotType: MutableSet<ConeKotlinType> = linkedSetOf()
|
||||
) : TypeStatement() {
|
||||
override fun plus(other: TypeStatement): MutableTypeStatement = MutableTypeStatement(
|
||||
variable,
|
||||
LinkedHashSet(exactType).apply { addAll(other.exactType) },
|
||||
LinkedHashSet(exactNotType).apply { addAll(other.exactNotType) }
|
||||
)
|
||||
|
||||
override val isEmpty: Boolean
|
||||
get() = exactType.isEmpty() && exactType.isEmpty()
|
||||
|
||||
override fun invert(): MutableTypeStatement {
|
||||
return MutableTypeStatement(
|
||||
variable,
|
||||
LinkedHashSet(exactNotType),
|
||||
LinkedHashSet(exactType)
|
||||
)
|
||||
}
|
||||
|
||||
operator fun plusAssign(info: TypeStatement) {
|
||||
exactType += info.exactType
|
||||
exactNotType += info.exactNotType
|
||||
}
|
||||
|
||||
fun copy(): MutableTypeStatement = MutableTypeStatement(variable, LinkedHashSet(exactType), LinkedHashSet(exactNotType))
|
||||
}
|
||||
|
||||
fun Implication.invertCondition(): Implication = Implication(condition.invert(), effect)
|
||||
|
||||
// --------------------------------------- Aliases ---------------------------------------
|
||||
|
||||
typealias TypeStatements = Map<RealVariable, TypeStatement>
|
||||
typealias MutableTypeStatements = MutableMap<RealVariable, MutableTypeStatement>
|
||||
|
||||
typealias MutableOperationStatements = MutableMap<RealVariable, MutableTypeStatement>
|
||||
|
||||
fun MutableTypeStatements.addStatement(variable: RealVariable, statement: TypeStatement) {
|
||||
put(variable, statement.asMutableStatement()) { it.apply { this += statement } }
|
||||
}
|
||||
|
||||
fun MutableTypeStatements.mergeTypeStatements(other: TypeStatements) {
|
||||
other.forEach { (variable, info) ->
|
||||
addStatement(variable, info)
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------- DSL ---------------------------------------
|
||||
|
||||
infix fun DataFlowVariable.eq(constant: Boolean?): OperationStatement {
|
||||
val condition = when (constant) {
|
||||
true -> Operation.EqTrue
|
||||
false -> Operation.EqFalse
|
||||
null -> Operation.EqNull
|
||||
}
|
||||
return OperationStatement(this, condition)
|
||||
}
|
||||
|
||||
infix fun DataFlowVariable.notEq(constant: Boolean?): OperationStatement {
|
||||
val condition = when (constant) {
|
||||
true -> Operation.EqFalse
|
||||
false -> Operation.EqTrue
|
||||
null -> Operation.NotEqNull
|
||||
}
|
||||
return OperationStatement(this, condition)
|
||||
}
|
||||
|
||||
infix fun OperationStatement.implies(effect: Statement<*>): Implication = Implication(this, effect)
|
||||
|
||||
infix fun RealVariable.typeEq(type: ConeKotlinType): TypeStatement =
|
||||
if (type !is ConeClassErrorType) {
|
||||
MutableTypeStatement(this, linkedSetOf<ConeKotlinType>().apply { this += type }, HashSet())
|
||||
} else {
|
||||
MutableTypeStatement(this)
|
||||
}
|
||||
|
||||
infix fun RealVariable.typeNotEq(type: ConeKotlinType): TypeStatement =
|
||||
if (type !is ConeClassErrorType) {
|
||||
MutableTypeStatement(this, linkedSetOf(), LinkedHashSet<ConeKotlinType>().apply { this += type })
|
||||
} else {
|
||||
MutableTypeStatement(this)
|
||||
}
|
||||
|
||||
// --------------------------------------- Utils ---------------------------------------
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun DataFlowVariable.isSynthetic(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isSynthetic is SyntheticVariable)
|
||||
}
|
||||
return this is SyntheticVariable
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun DataFlowVariable.isReal(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isReal is RealVariable)
|
||||
}
|
||||
return this is RealVariable
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
|
||||
sealed class Statement<T : Statement<T>> {
|
||||
abstract fun invert(): T
|
||||
abstract val variable: DataFlowVariable
|
||||
}
|
||||
|
||||
/*
|
||||
* Examples:
|
||||
* d == Null
|
||||
* d != Null
|
||||
* d == True
|
||||
* d == False
|
||||
*/
|
||||
data class OperationStatement(override val variable: DataFlowVariable, val operation: Operation) : Statement<OperationStatement>() {
|
||||
override fun invert(): OperationStatement {
|
||||
return OperationStatement(variable, operation.invert())
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "$variable $operation"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TypeStatement : Statement<TypeStatement>() {
|
||||
abstract override val variable: RealVariable
|
||||
abstract val exactType: Set<ConeKotlinType>
|
||||
abstract val exactNotType: Set<ConeKotlinType>
|
||||
|
||||
abstract operator fun plus(other: TypeStatement): TypeStatement
|
||||
abstract val isEmpty: Boolean
|
||||
val isNotEmpty: Boolean get() = !isEmpty
|
||||
|
||||
override fun toString(): String {
|
||||
return "$variable: $exactType, $exactNotType"
|
||||
}
|
||||
}
|
||||
|
||||
class Implication(
|
||||
val condition: OperationStatement,
|
||||
val effect: Statement<*>
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "$condition -> $effect"
|
||||
}
|
||||
}
|
||||
|
||||
enum class Operation {
|
||||
EqTrue, EqFalse, EqNull, NotEqNull;
|
||||
|
||||
fun invert(): Operation = when (this) {
|
||||
EqTrue -> EqFalse
|
||||
EqFalse -> EqTrue
|
||||
EqNull -> NotEqNull
|
||||
NotEqNull -> EqNull
|
||||
}
|
||||
|
||||
override fun toString(): String = when (this) {
|
||||
EqTrue -> "== True"
|
||||
EqFalse -> "== False"
|
||||
EqNull -> "== Null"
|
||||
NotEqNull -> "!= Null"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.resolve.dfa
|
||||
|
||||
import kotlinx.collections.immutable.PersistentMap
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirThisReference
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirAccessorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
internal inline fun <K, V> MutableMap<K, V>.put(key: K, value: V, remappingFunction: (existing: V) -> V) {
|
||||
contract {
|
||||
callsInPlace(remappingFunction, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
val existing = this[key]
|
||||
if (existing == null) {
|
||||
put(key, value)
|
||||
} else {
|
||||
put(key, remappingFunction(existing))
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
internal inline fun <K, V> PersistentMap<K, V>.put(
|
||||
key: K,
|
||||
valueProducer: () -> V,
|
||||
remappingFunction: (existing: V) -> V
|
||||
): PersistentMap<K, V> {
|
||||
contract {
|
||||
callsInPlace(remappingFunction, InvocationKind.AT_MOST_ONCE)
|
||||
callsInPlace(valueProducer, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
val existing = this[key]
|
||||
return if (existing == null) {
|
||||
put(key, valueProducer())
|
||||
} else {
|
||||
put(key, remappingFunction(existing))
|
||||
}
|
||||
}
|
||||
|
||||
@DfaInternals
|
||||
fun FirOperation.invert(): FirOperation = when (this) {
|
||||
FirOperation.EQ -> FirOperation.NOT_EQ
|
||||
FirOperation.NOT_EQ -> FirOperation.EQ
|
||||
FirOperation.IDENTITY -> FirOperation.NOT_IDENTITY
|
||||
FirOperation.NOT_IDENTITY -> FirOperation.IDENTITY
|
||||
else -> throw IllegalArgumentException("$this can not be inverted")
|
||||
}
|
||||
|
||||
@DfaInternals
|
||||
fun FirOperation.isEq(): Boolean {
|
||||
return when (this) {
|
||||
FirOperation.EQ, FirOperation.IDENTITY -> true
|
||||
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> false
|
||||
else -> throw IllegalArgumentException("$this should not be there")
|
||||
}
|
||||
}
|
||||
|
||||
fun FirFunctionCall.isBooleanNot(): Boolean {
|
||||
val symbol = calleeReference.safeAs<FirResolvedNamedReference>()?.resolvedSymbol as? FirNamedFunctionSymbol ?: return false
|
||||
return symbol.callableId == StandardClassIds.Callables.not
|
||||
}
|
||||
|
||||
fun ConeConstantReference.toOperation(): Operation = when (this) {
|
||||
ConeConstantReference.NULL -> Operation.EqNull
|
||||
ConeConstantReference.NOT_NULL -> Operation.NotEqNull
|
||||
ConeBooleanConstantReference.TRUE -> Operation.EqTrue
|
||||
ConeBooleanConstantReference.FALSE -> Operation.EqFalse
|
||||
else -> throw IllegalArgumentException("$this can not be transformed to Operation")
|
||||
}
|
||||
|
||||
@DfaInternals
|
||||
val FirExpression.coneType: ConeKotlinType
|
||||
get() = typeRef.coneType
|
||||
|
||||
@DfaInternals
|
||||
val FirElement.symbol: FirBasedSymbol<*>?
|
||||
get() = when (this) {
|
||||
is FirResolvable -> symbol
|
||||
is FirDeclaration -> symbol
|
||||
is FirWhenSubjectExpression -> whenRef.value.subject?.symbol
|
||||
is FirSafeCallExpression -> regularQualifiedAccess.symbol
|
||||
else -> null
|
||||
}?.takeIf {
|
||||
(this as? FirExpression)?.unwrapSmartcastExpression() is FirThisReceiverExpression ||
|
||||
(it !is FirFunctionSymbol<*> && it !is FirAccessorSymbol)
|
||||
}
|
||||
|
||||
@DfaInternals
|
||||
internal val FirResolvable.symbol: FirBasedSymbol<*>?
|
||||
get() = when (val reference = calleeReference) {
|
||||
is FirThisReference -> reference.boundSymbol
|
||||
is FirResolvedNamedReference -> reference.resolvedSymbol
|
||||
is FirSimpleNamedReference -> reference.candidateSymbol
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun FirExpression.unwrapSmartcastExpression(): FirExpression =
|
||||
when (this) {
|
||||
is FirExpressionWithSmartcast -> originalExpression
|
||||
else -> this
|
||||
}
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.resolve.diagnostics
|
||||
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirVariable
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnosticWithSource
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate
|
||||
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.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||
|
||||
sealed interface ConeUnresolvedError : ConeDiagnostic {
|
||||
val qualifier: String?
|
||||
}
|
||||
|
||||
interface ConeDiagnosticWithCandidates : ConeDiagnostic {
|
||||
val candidateSymbols: Collection<FirBasedSymbol<*>>
|
||||
}
|
||||
|
||||
interface ConeDiagnosticWithSingleCandidate : ConeDiagnosticWithCandidates {
|
||||
override val candidateSymbols: Collection<FirBasedSymbol<*>> get() = listOf(candidateSymbol)
|
||||
val candidateSymbol: FirBasedSymbol<*>
|
||||
}
|
||||
|
||||
class ConeUnresolvedReferenceError(val name: Name? = null) : ConeUnresolvedError {
|
||||
override val qualifier: String? get() = name?.asString()
|
||||
override val reason: String get() = "Unresolved reference" + if (name != null) ": ${name.asString()}" else ""
|
||||
}
|
||||
|
||||
class ConeUnresolvedSymbolError(val classId: ClassId) : ConeUnresolvedError {
|
||||
override val qualifier: String get() = classId.asSingleFqName().asString()
|
||||
override val reason: String get() = "Symbol not found for $classId"
|
||||
}
|
||||
|
||||
class ConeUnresolvedQualifierError(override val qualifier: String) : ConeUnresolvedError {
|
||||
override val reason: String get() = "Symbol not found for $qualifier"
|
||||
}
|
||||
|
||||
class ConeUnresolvedNameError(val name: Name) : ConeUnresolvedError {
|
||||
override val qualifier: String get() = name.asString()
|
||||
override val reason: String get() = "Unresolved name: $name"
|
||||
}
|
||||
|
||||
class ConeFunctionCallExpectedError(
|
||||
val name: Name,
|
||||
val hasValueParameters: Boolean,
|
||||
override val candidateSymbols: Collection<FirBasedSymbol<*>>
|
||||
) : ConeDiagnosticWithCandidates {
|
||||
override val reason: String get() = "Function call expected: $name(${if (hasValueParameters) "..." else ""})"
|
||||
}
|
||||
|
||||
class ConeFunctionExpectedError(val expression: String, val type: ConeKotlinType) : ConeDiagnostic {
|
||||
override val reason: String get() = "Expression '$expression' of type '$type' cannot be invoked as a function"
|
||||
}
|
||||
|
||||
class ConeResolutionToClassifierError(override val candidateSymbol: FirRegularClassSymbol) : ConeDiagnosticWithSingleCandidate {
|
||||
override val reason: String get() = "Resolution to classifier"
|
||||
}
|
||||
|
||||
class ConeHiddenCandidateError(
|
||||
override val candidateSymbol: FirBasedSymbol<*>
|
||||
) : ConeDiagnosticWithSingleCandidate {
|
||||
override val reason: String get() = "HIDDEN: ${describeSymbol(candidateSymbol)} is deprecated with DeprecationLevel.HIDDEN"
|
||||
}
|
||||
|
||||
class ConeVisibilityError(
|
||||
override val candidateSymbol: FirBasedSymbol<*>
|
||||
) : ConeDiagnosticWithSingleCandidate {
|
||||
override val reason: String get() = "HIDDEN: ${describeSymbol(candidateSymbol)} is invisible"
|
||||
}
|
||||
|
||||
class ConeInapplicableWrongReceiver(override val candidateSymbols: Collection<FirBasedSymbol<*>>) : ConeDiagnosticWithCandidates {
|
||||
override val reason: String
|
||||
get() = "None of the following candidates is applicable because of receiver type mismatch: ${
|
||||
candidateSymbols.map { describeSymbol(it) }
|
||||
}"
|
||||
}
|
||||
|
||||
class ConeInapplicableCandidateError(
|
||||
val applicability: CandidateApplicability,
|
||||
val candidate: AbstractCandidate,
|
||||
) : ConeDiagnosticWithSingleCandidate {
|
||||
override val reason: String get() = "Inapplicable($applicability): ${describeSymbol(candidateSymbol)}"
|
||||
override val candidateSymbol: FirBasedSymbol<*> get() = candidate.symbol
|
||||
}
|
||||
|
||||
class ConeNoCompanionObject(
|
||||
override val candidateSymbol: FirRegularClassSymbol
|
||||
) : ConeDiagnosticWithSingleCandidate {
|
||||
override val reason: String
|
||||
get() = "Classifier ''$candidateSymbol'' does not have a companion object, and thus must be initialized here"
|
||||
}
|
||||
|
||||
class ConeConstraintSystemHasContradiction(
|
||||
val candidate: AbstractCandidate,
|
||||
) : ConeDiagnosticWithSingleCandidate {
|
||||
override val reason: String get() = "CS errors: ${describeSymbol(candidateSymbol)}"
|
||||
override val candidateSymbol: FirBasedSymbol<*> get() = candidate.symbol
|
||||
}
|
||||
|
||||
class ConeArgumentTypeMismatchCandidateError(
|
||||
val expectedType: ConeKotlinType, val actualType: ConeKotlinType
|
||||
) : ConeDiagnostic {
|
||||
override val reason: String
|
||||
get() = "Type mismatch. Expected: $expectedType, Actual: $actualType"
|
||||
}
|
||||
|
||||
class ConeAmbiguityError(
|
||||
val name: Name,
|
||||
val applicability: CandidateApplicability,
|
||||
val candidates: Collection<AbstractCandidate>
|
||||
) : ConeDiagnosticWithCandidates {
|
||||
override val reason: String get() = "Ambiguity: $name, ${candidateSymbols.map { describeSymbol(it) }}"
|
||||
override val candidateSymbols: Collection<FirBasedSymbol<*>> get() = candidates.map { it.symbol }
|
||||
}
|
||||
|
||||
class ConeOperatorAmbiguityError(override val candidateSymbols: Collection<FirBasedSymbol<*>>) : ConeDiagnosticWithCandidates {
|
||||
override val reason: String get() = "Operator overload ambiguity. Compatible candidates: ${candidateSymbols.map { describeSymbol(it) }}"
|
||||
}
|
||||
|
||||
object ConeVariableExpectedError : ConeDiagnostic {
|
||||
override val reason: String get() = "Variable expected"
|
||||
}
|
||||
|
||||
class ConeValReassignmentError(val variable: FirVariableSymbol<*>) : ConeDiagnostic {
|
||||
override val reason: String get() = "Re-assigning a val variable"
|
||||
}
|
||||
|
||||
class ConeContractDescriptionError(override val reason: String) : ConeDiagnostic
|
||||
|
||||
class ConeIllegalAnnotationError(val name: Name) : ConeDiagnostic {
|
||||
override val reason: String get() = "Not a legal annotation: $name"
|
||||
}
|
||||
|
||||
interface ConeUnmatchedTypeArgumentsError : ConeDiagnosticWithSingleCandidate {
|
||||
val desiredCount: Int
|
||||
override val candidateSymbol: FirClassLikeSymbol<*>
|
||||
}
|
||||
|
||||
class ConeWrongNumberOfTypeArgumentsError(
|
||||
override val desiredCount: Int,
|
||||
override val candidateSymbol: FirRegularClassSymbol,
|
||||
source: FirSourceElement
|
||||
) : ConeDiagnosticWithSource(source), ConeUnmatchedTypeArgumentsError {
|
||||
override val reason: String get() = "Wrong number of type arguments"
|
||||
}
|
||||
|
||||
class ConeNoTypeArgumentsOnRhsError(
|
||||
override val desiredCount: Int,
|
||||
override val candidateSymbol: FirClassLikeSymbol<*>
|
||||
) : ConeUnmatchedTypeArgumentsError {
|
||||
override val reason: String get() = "No type arguments on RHS"
|
||||
}
|
||||
|
||||
class ConeOuterClassArgumentsRequired(
|
||||
val symbol: FirRegularClassSymbol,
|
||||
) : ConeDiagnostic {
|
||||
override val reason: String = "Type arguments should be specified for an outer class"
|
||||
}
|
||||
|
||||
class ConeInstanceAccessBeforeSuperCall(val target: String) : ConeDiagnostic {
|
||||
override val reason: String get() = "Cannot access ''${target}'' before superclass constructor has been called"
|
||||
}
|
||||
|
||||
class ConeUnsupportedCallableReferenceTarget(val fir: FirCallableDeclaration) : ConeDiagnosticWithSingleCandidate {
|
||||
override val reason: String get() = "Unsupported declaration for callable reference: ${fir.render()}"
|
||||
override val candidateSymbol: FirBasedSymbol<*> get() = fir.symbol
|
||||
}
|
||||
|
||||
class ConeTypeParameterSupertype(val symbol: FirTypeParameterSymbol) : ConeDiagnostic {
|
||||
override val reason: String get() = "Type parameter ${symbol.fir.name} cannot be a supertype"
|
||||
}
|
||||
|
||||
class ConeTypeParameterInQualifiedAccess(val symbol: FirTypeParameterSymbol) : ConeDiagnostic {
|
||||
override val reason: String get() = "Type parameter ${symbol.fir.name} in qualified access"
|
||||
}
|
||||
|
||||
class ConeCyclicTypeBound(val symbol: FirTypeParameterSymbol, val bounds: ImmutableList<FirTypeRef>) : ConeDiagnostic {
|
||||
override val reason: String get() = "Type parameter ${symbol.fir.name} has cyclic bounds"
|
||||
}
|
||||
|
||||
class ConeImportFromSingleton(val name: Name) : ConeDiagnostic {
|
||||
override val reason: String get() = "Import from singleton $name is not allowed"
|
||||
}
|
||||
|
||||
open class ConeUnsupported(override val reason: String, val source: FirSourceElement? = null) : ConeDiagnostic
|
||||
|
||||
class ConeUnsupportedDynamicType : ConeUnsupported("Dynamic types are not supported in this context")
|
||||
|
||||
class ConeDeprecated(
|
||||
val source: FirSourceElement?,
|
||||
override val candidateSymbol: FirBasedSymbol<*>,
|
||||
val deprecationInfo: DeprecationInfo
|
||||
) : ConeDiagnosticWithSingleCandidate {
|
||||
override val reason: String get() = "Deprecated: ${deprecationInfo.message}"
|
||||
}
|
||||
|
||||
class ConeLocalVariableNoTypeOrInitializer(val variable: FirVariable) : ConeDiagnostic {
|
||||
override val reason: String get() = "Cannot infer variable type without initializer / getter / delegate"
|
||||
}
|
||||
|
||||
class ConePropertyAsOperator(val symbol: FirPropertySymbol) : ConeDiagnostic {
|
||||
override val reason: String get() = "Cannot use a property as an operator"
|
||||
}
|
||||
|
||||
class ConeUnknownLambdaParameterTypeDiagnostic : ConeDiagnostic {
|
||||
override val reason: String get() = "Unknown return lambda parameter type"
|
||||
}
|
||||
|
||||
private fun describeSymbol(symbol: FirBasedSymbol<*>): String {
|
||||
return when (symbol) {
|
||||
is FirClassLikeSymbol<*> -> symbol.classId.asString()
|
||||
is FirCallableSymbol<*> -> symbol.callableId.toString()
|
||||
else -> "$symbol"
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.resolve.inference
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
|
||||
|
||||
class ConeTypeVariableForPostponedAtom(name: String) : ConeTypeVariable(name)
|
||||
class ConeTypeVariableForLambdaParameterType(name: String, val index: Int) : ConeTypeVariable(name)
|
||||
class ConeTypeVariableForLambdaReturnType(val argument: FirAnonymousFunction, name: String) : ConeTypeVariable(name)
|
||||
|
||||
class ConeTypeParameterBasedTypeVariable(
|
||||
val typeParameterSymbol: FirTypeParameterSymbol
|
||||
) : ConeTypeVariable(typeParameterSymbol.name.identifier, typeParameterSymbol.toLookupTag())
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.resolve.inference.model
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeProjection
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.*
|
||||
import org.jetbrains.kotlin.types.model.TypeVariableMarker
|
||||
|
||||
class ConeDeclaredUpperBoundConstraintPosition : DeclaredUpperBoundConstraintPosition<Nothing?>(null)
|
||||
|
||||
class ConeFixVariableConstraintPosition(variable: TypeVariableMarker) : FixVariableConstraintPosition<Nothing?>(variable, null)
|
||||
|
||||
class ConeArgumentConstraintPosition(argument: FirElement) : ArgumentConstraintPosition<FirElement>(argument)
|
||||
|
||||
class ConeExpectedTypeConstraintPosition(
|
||||
val expectedTypeMismatchIsReportedInChecker: Boolean
|
||||
) : ExpectedTypeConstraintPosition<Nothing?>(null)
|
||||
|
||||
class ConeExplicitTypeParameterConstraintPosition(
|
||||
typeArgument: FirTypeProjection,
|
||||
) : ExplicitTypeParameterConstraintPosition<FirTypeProjection>(typeArgument)
|
||||
|
||||
class ConeLambdaArgumentConstraintPosition(
|
||||
anonymousFunction: FirAnonymousFunction
|
||||
) : LambdaArgumentConstraintPosition<FirAnonymousFunction>(anonymousFunction)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.resolve.transformers
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypedDeclaration
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
|
||||
interface ReturnTypeCalculator {
|
||||
fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef
|
||||
|
||||
fun tryCalculateReturnType(symbol: FirCallableSymbol<*>): FirResolvedTypeRef {
|
||||
return tryCalculateReturnType(symbol.fir)
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.resolve.transformers
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypedDeclaration
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
||||
|
||||
class ReturnTypeCalculatorForFullBodyResolve : ReturnTypeCalculator {
|
||||
override fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef {
|
||||
val returnTypeRef = declaration.returnTypeRef
|
||||
if (returnTypeRef is FirResolvedTypeRef) return returnTypeRef
|
||||
if (declaration.origin.fromSupertypes) {
|
||||
return FakeOverrideTypeCalculator.Forced.computeReturnType(declaration)
|
||||
}
|
||||
|
||||
return buildErrorTypeRef {
|
||||
diagnostic = ConeSimpleDiagnostic(
|
||||
"Cannot calculate return type during full-body resolution (local class/object?): ${declaration.render()}",
|
||||
DiagnosticKind.InferenceError
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.resolve.transformers
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataKey
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataRegistry
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
|
||||
private object PublishedApiEffectiveVisibilityKey : FirDeclarationDataKey()
|
||||
var FirDeclaration.publishedApiEffectiveVisibility: EffectiveVisibility? by FirDeclarationDataRegistry.data(PublishedApiEffectiveVisibilityKey)
|
||||
|
||||
inline val FirCallableSymbol<*>.publishedApiEffectiveVisibility: EffectiveVisibility?
|
||||
get() {
|
||||
ensureResolved(FirResolvePhase.STATUS)
|
||||
return fir.publishedApiEffectiveVisibility
|
||||
}
|
||||
Reference in New Issue
Block a user