[FIR] Add abstract data flow analyzer with callbacks for body resolve transformer

This commit is contained in:
Dmitriy Novozhilov
2019-08-22 14:50:50 +03:00
parent dcfc75a58f
commit 56ac1201b6
10 changed files with 397 additions and 55 deletions
@@ -0,0 +1,112 @@
/*
* 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.dfa
import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
class DummyFirDataFlowAnalyzer : FirDataFlowAnalyzer() {
companion object {
private const val DUMMY = "<DUMMY>"
}
override fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): ConeKotlinType? {
return qualifiedAccessExpression.typeRef.coneTypeUnsafe()
}
override fun enterFunction(function: FirFunction<*>) {}
override fun exitFunction(function: FirFunction<*>): ControlFlowGraph = ControlFlowGraph(DUMMY)
override fun enterBlock(block: FirBlock) {}
override fun exitBlock(block: FirBlock) {}
override fun enterProperty(property: FirProperty) {}
override fun exitProperty(property: FirProperty) = ControlFlowGraph(DUMMY)
override fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {}
override fun exitOperatorCall(operatorCall: FirOperatorCall) {}
override fun exitJump(jump: FirJump<*>) {}
override fun enterWhenExpression(whenExpression: FirWhenExpression) {}
override fun enterWhenBranchCondition(whenBranch: FirWhenBranch) {}
override fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {}
override fun exitWhenBranchResult(whenBranch: FirWhenBranch) {}
override fun exitWhenExpression(whenExpression: FirWhenExpression) {}
override fun enterWhileLoop(loop: FirLoop) {}
override fun exitWhileLoopCondition(loop: FirLoop) {}
override fun exitWhileLoop(loop: FirLoop) {}
override fun enterDoWhileLoop(loop: FirLoop) {}
override fun enterDoWhileLoopCondition(loop: FirLoop) {}
override fun exitDoWhileLoop(loop: FirLoop) {}
override fun enterTryExpression(tryExpression: FirTryExpression) {}
override fun exitTryMainBlock(tryExpression: FirTryExpression) {}
override fun enterCatchClause(catch: FirCatch) {}
override fun exitCatchClause(catch: FirCatch) {}
override fun enterFinallyBlock(tryExpression: FirTryExpression) {}
override fun exitFinallyBlock(tryExpression: FirTryExpression) {}
override fun exitTryExpression(tryExpression: FirTryExpression) {}
override fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) {}
override fun enterFunctionCall(functionCall: FirFunctionCall) {}
override fun exitFunctionCall(functionCall: FirFunctionCall) {}
override fun exitConstExpresion(constExpression: FirConstExpression<*>) {}
override fun exitVariableDeclaration(variable: FirVariable<*>) {}
override fun exitVariableAssignment(assignment: FirVariableAssignment) {}
override fun exitThrowExceptionNode(throwExpression: FirThrowExpression) {}
override fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun enterAnnotationCall(annotationCall: FirAnnotationCall) {}
override fun exitAnnotationCall(annotationCall: FirAnnotationCall) {}
override fun enterInitBlock(initBlock: FirAnonymousInitializer) {}
override fun exitInitBlock(initBlock: FirAnonymousInitializer) {}
}
@@ -0,0 +1,101 @@
/*
* 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.dfa
import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
import org.jetbrains.kotlin.fir.types.ConeKotlinType
abstract class FirDataFlowAnalyzer {
abstract fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): ConeKotlinType?
// ----------------------------------- Named function -----------------------------------
abstract fun enterFunction(function: FirFunction<*>)
abstract fun exitFunction(function: FirFunction<*>): ControlFlowGraph
// ----------------------------------- Property -----------------------------------
abstract fun enterProperty(property: FirProperty)
abstract fun exitProperty(property: FirProperty): ControlFlowGraph
// ----------------------------------- Block -----------------------------------
abstract fun enterBlock(block: FirBlock)
abstract fun exitBlock(block: FirBlock)
// ----------------------------------- Operator call -----------------------------------
abstract fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall)
abstract fun exitOperatorCall(operatorCall: FirOperatorCall)
// ----------------------------------- Jump -----------------------------------
abstract fun exitJump(jump: FirJump<*>)
// ----------------------------------- When -----------------------------------
abstract fun enterWhenExpression(whenExpression: FirWhenExpression)
abstract fun enterWhenBranchCondition(whenBranch: FirWhenBranch)
abstract fun exitWhenBranchCondition(whenBranch: FirWhenBranch)
abstract fun exitWhenBranchResult(whenBranch: FirWhenBranch)
abstract fun exitWhenExpression(whenExpression: FirWhenExpression)
// ----------------------------------- While Loop -----------------------------------
abstract fun enterWhileLoop(loop: FirLoop)
abstract fun exitWhileLoopCondition(loop: FirLoop)
abstract fun exitWhileLoop(loop: FirLoop)
// ----------------------------------- Do while Loop -----------------------------------
abstract fun enterDoWhileLoop(loop: FirLoop)
abstract fun enterDoWhileLoopCondition(loop: FirLoop)
abstract fun exitDoWhileLoop(loop: FirLoop)
// ----------------------------------- Try-catch-finally -----------------------------------
abstract fun enterTryExpression(tryExpression: FirTryExpression)
abstract fun exitTryMainBlock(tryExpression: FirTryExpression)
abstract fun enterCatchClause(catch: FirCatch)
abstract fun exitCatchClause(catch: FirCatch)
abstract fun enterFinallyBlock(tryExpression: FirTryExpression)
abstract fun exitFinallyBlock(tryExpression: FirTryExpression)
abstract fun exitTryExpression(tryExpression: FirTryExpression)
// ----------------------------------- Resolvable call -----------------------------------
abstract fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression)
abstract fun enterFunctionCall(functionCall: FirFunctionCall)
abstract fun exitFunctionCall(functionCall: FirFunctionCall)
abstract fun exitConstExpresion(constExpression: FirConstExpression<*>)
abstract fun exitVariableDeclaration(variable: FirVariable<*>)
abstract fun exitVariableAssignment(assignment: FirVariableAssignment)
abstract fun exitThrowExceptionNode(throwExpression: FirThrowExpression)
// ----------------------------------- Boolean operators -----------------------------------
abstract fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression)
abstract fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression)
abstract fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression)
abstract fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression)
abstract fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression)
abstract fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression)
// ----------------------------------- Annotations -----------------------------------
abstract fun enterAnnotationCall(annotationCall: FirAnnotationCall)
abstract fun exitAnnotationCall(annotationCall: FirAnnotationCall)
// ----------------------------------- Init block -----------------------------------
abstract fun enterInitBlock(initBlock: FirAnonymousInitializer)
abstract fun exitInitBlock(initBlock: FirAnonymousInitializer)
}
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.fir.references.FirExplicitThisReference
import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.dfa.DummyFirDataFlowAnalyzer
import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter
import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.addImportingScopes import org.jetbrains.kotlin.fir.scopes.addImportingScopes
@@ -30,7 +32,6 @@ import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.SimpleTypeMarker import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeConstructorMarker
@@ -82,6 +83,7 @@ open class FirBodyResolveTransformer(
) )
private val syntheticCallGenerator: FirSyntheticCallGenerator = FirSyntheticCallGenerator(this) private val syntheticCallGenerator: FirSyntheticCallGenerator = FirSyntheticCallGenerator(this)
private val dataFlowAnalyzer: FirDataFlowAnalyzer = DummyFirDataFlowAnalyzer()
override val <D> AbstractFirBasedSymbol<D>.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner<D> override val <D> AbstractFirBasedSymbol<D>.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner<D>
get() { get() {
@@ -115,8 +117,11 @@ open class FirBodyResolveTransformer(
): CompositeTransformResult<FirDeclaration> { ): CompositeTransformResult<FirDeclaration> {
if (implicitTypeOnly) return anonymousInitializer.compose() if (implicitTypeOnly) return anonymousInitializer.compose()
return withScopeCleanup(localScopes) { return withScopeCleanup(localScopes) {
dataFlowAnalyzer.enterInitBlock(anonymousInitializer)
localScopes.addIfNotNull(primaryConstructorParametersScope) localScopes.addIfNotNull(primaryConstructorParametersScope)
super.transformAnonymousInitializer(anonymousInitializer, data) super.transformAnonymousInitializer(anonymousInitializer, data).also {
dataFlowAnalyzer.exitInitBlock(it.single as FirAnonymousInitializer)
}
} }
} }
@@ -166,7 +171,7 @@ open class FirBodyResolveTransformer(
override fun transformTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Any?): CompositeTransformResult<FirStatement> { override fun transformTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Any?): CompositeTransformResult<FirStatement> {
val symbolProvider = session.service<FirSymbolProvider>() val symbolProvider = session.service<FirSymbolProvider>()
val resolved = super.transformTypeOperatorCall(typeOperatorCall, data).single val resolved = transformExpression(typeOperatorCall, data).single
when ((resolved as FirTypeOperatorCall).operation) { when ((resolved as FirTypeOperatorCall).operation) {
FirOperation.IS, FirOperation.NOT_IS -> { FirOperation.IS, FirOperation.NOT_IS -> {
resolved.resultType = FirResolvedTypeRefImpl( resolved.resultType = FirResolvedTypeRefImpl(
@@ -186,6 +191,7 @@ open class FirBodyResolveTransformer(
} }
else -> error("Unknown type operator") else -> error("Unknown type operator")
} }
dataFlowAnalyzer.exitTypeOperatorCall(typeOperatorCall)
return resolved.compose() return resolved.compose()
} }
@@ -194,7 +200,7 @@ open class FirBodyResolveTransformer(
data: Any? data: Any?
): CompositeTransformResult<FirStatement> { ): CompositeTransformResult<FirStatement> {
when (val callee = qualifiedAccessExpression.calleeReference) { val result = when (val callee = qualifiedAccessExpression.calleeReference) {
is FirExplicitThisReference -> { is FirExplicitThisReference -> {
val labelName = callee.labelName val labelName = callee.labelName
val implicitReceiver = implicitReceiverStack[labelName] val implicitReceiver = implicitReceiverStack[labelName]
@@ -203,7 +209,7 @@ open class FirBodyResolveTransformer(
null, implicitReceiver?.type ?: ConeKotlinErrorType("Unresolved this@$labelName"), null, implicitReceiver?.type ?: ConeKotlinErrorType("Unresolved this@$labelName"),
emptyList() emptyList()
) )
return qualifiedAccessExpression.compose() qualifiedAccessExpression
} }
is FirSuperReference -> { is FirSuperReference -> {
if (callee.superTypeRef is FirResolvedTypeRef) { if (callee.superTypeRef is FirResolvedTypeRef) {
@@ -215,29 +221,40 @@ open class FirBodyResolveTransformer(
qualifiedAccessExpression.resultType = superTypeRef qualifiedAccessExpression.resultType = superTypeRef
callee.replaceSuperTypeRef(superTypeRef) callee.replaceSuperTypeRef(superTypeRef)
} }
return qualifiedAccessExpression.compose() qualifiedAccessExpression
} }
is FirDelegateFieldReference -> { is FirDelegateFieldReference -> {
val delegateFieldSymbol = callee.coneSymbol val delegateFieldSymbol = callee.coneSymbol
qualifiedAccessExpression.resultType = delegateFieldSymbol.delegate.typeRef qualifiedAccessExpression.resultType = delegateFieldSymbol.delegate.typeRef
return qualifiedAccessExpression.compose() qualifiedAccessExpression
} }
is FirResolvedCallableReference -> { is FirResolvedCallableReference -> {
if (qualifiedAccessExpression.typeRef !is FirResolvedTypeRef) { if (qualifiedAccessExpression.typeRef !is FirResolvedTypeRef) {
storeTypeFromCallee(qualifiedAccessExpression) storeTypeFromCallee(qualifiedAccessExpression)
} }
return qualifiedAccessExpression.compose() qualifiedAccessExpression
}
else -> {
val transformedCallee = callResolver.resolveVariableAccessAndSelectCandidate(qualifiedAccessExpression, file)
// NB: here we can get raw expression because of dropped qualifiers (see transform callee),
// so candidate existence must be checked before calling completion
val result = if (transformedCallee is FirQualifiedAccessExpression && transformedCallee.candidate() != null) {
callCompleter.completeCall(transformedCallee, data as? FirTypeRef)
} else {
transformedCallee
}
if (result is FirQualifiedAccessExpression) {
dataFlowAnalyzer.getTypeUsingSmartcastInfo(result)?.let { typeWithSmartCasts ->
result.resultType = FirResolvedTypeRefImpl(result.psi, typeWithSmartCasts, result.resultType.annotations)
}
}
result
} }
} }
if (result is FirQualifiedAccessExpression) {
val transformedCallee = callResolver.resolveVariableAccessAndSelectCandidate(qualifiedAccessExpression, file) dataFlowAnalyzer.exitQualifiedAccessExpression(result)
// NB: here we can get raw expression because of dropped qualifiers (see transform callee),
// so candidate existence must be checked before calling completion
return if (transformedCallee is FirQualifiedAccessExpression && transformedCallee.candidate() != null) {
callCompleter.completeCall(transformedCallee, data as? FirTypeRef).compose()
} else {
transformedCallee.compose()
} }
return result.compose()
} }
override fun transformVariableAssignment( override fun transformVariableAssignment(
@@ -246,14 +263,17 @@ open class FirBodyResolveTransformer(
): CompositeTransformResult<FirStatement> { ): CompositeTransformResult<FirStatement> {
// val resolvedAssignment = transformCallee(variableAssignment) // val resolvedAssignment = transformCallee(variableAssignment)
val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment, file) val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment, file)
return if (resolvedAssignment is FirVariableAssignment) { val result = if (resolvedAssignment is FirVariableAssignment) {
val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType) val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType)
val expectedType = typeFromCallee(completeAssignment) val expectedType = typeFromCallee(completeAssignment)
completeAssignment.transformRValue(this, expectedType).compose() completeAssignment.transformRValue(this, expectedType)
} else { } else {
// This can happen in erroneous code only // This can happen in erroneous code only
resolvedAssignment.compose() resolvedAssignment
} }
// TODO: maybe replace with FirAbstractAssignment for performance?
(result as? FirVariableAssignment)?.let { dataFlowAnalyzer.exitVariableAssignment(it) }
return result.compose()
} }
override fun transformAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: Any?): CompositeTransformResult<FirDeclaration> { override fun transformAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: Any?): CompositeTransformResult<FirDeclaration> {
@@ -379,32 +399,46 @@ open class FirBodyResolveTransformer(
override fun transformCatch(catch: FirCatch, data: Any?): CompositeTransformResult<FirCatch> { override fun transformCatch(catch: FirCatch, data: Any?): CompositeTransformResult<FirCatch> {
dataFlowAnalyzer.enterCatchClause(catch)
return withScopeCleanup(localScopes) { return withScopeCleanup(localScopes) {
localScopes += FirLocalScope() localScopes += FirLocalScope()
catch.transformParameter(this, noExpectedType) catch.transformParameter(this, noExpectedType)
catch.transformBlock(this, null).compose() catch.transformBlock(this, null)
} }.also { dataFlowAnalyzer.exitCatchClause(it) }.compose()
} }
override fun transformTryExpression(tryExpression: FirTryExpression, data: Any?): CompositeTransformResult<FirStatement> { override fun transformTryExpression(tryExpression: FirTryExpression, data: Any?): CompositeTransformResult<FirStatement> {
if (tryExpression.calleeReference is FirResolvedCallableReference && tryExpression.resultType !is FirImplicitTypeRef) { if (tryExpression.calleeReference is FirResolvedCallableReference && tryExpression.resultType !is FirImplicitTypeRef) {
return tryExpression.compose() return tryExpression.compose()
} }
dataFlowAnalyzer.enterTryExpression(tryExpression)
tryExpression.transformTryBlock(this, null) tryExpression.transformTryBlock(this, null)
dataFlowAnalyzer.exitTryMainBlock(tryExpression)
tryExpression.transformCatches(this, null) tryExpression.transformCatches(this, null)
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val tryExpression = syntheticCallGenerator.generateCalleeForTryExpression(tryExpression) ?: run { var result = syntheticCallGenerator.generateCalleeForTryExpression(tryExpression)?.let {
tryExpression.resultType = FirErrorTypeRefImpl(null, "Inapplicable when expression") val expectedTypeRef = data as FirTypeRef?
return tryExpression.compose() callCompleter.completeCall(it, expectedTypeRef)
} ?: run {
tryExpression.resultType = FirErrorTypeRefImpl(null, "")
tryExpression
} }
val expectedTypeRef = data as FirTypeRef?
val result = callCompleter.completeCall(tryExpression, expectedTypeRef)
return result.transformFinallyBlock(this, noExpectedType).compose() result = if (result.finallyBlock != null) {
result.also(dataFlowAnalyzer::enterFinallyBlock)
.transformFinallyBlock(this, noExpectedType)
.also(dataFlowAnalyzer::exitFinallyBlock)
} else {
result
}
dataFlowAnalyzer.exitTryExpression(result)
return result.compose()
} }
override fun transformFunctionCall(functionCall: FirFunctionCall, data: Any?): CompositeTransformResult<FirStatement> { override fun transformFunctionCall(functionCall: FirFunctionCall, data: Any?): CompositeTransformResult<FirStatement> {
dataFlowAnalyzer.enterFunctionCall(functionCall)
if (functionCall.calleeReference is FirResolvedCallableReference && functionCall.resultType is FirImplicitTypeRef) { if (functionCall.calleeReference is FirResolvedCallableReference && functionCall.resultType is FirImplicitTypeRef) {
storeTypeFromCallee(functionCall) storeTypeFromCallee(functionCall)
} }
@@ -424,7 +458,7 @@ open class FirBodyResolveTransformer(
throw RuntimeException("While resolving call ${functionCall.render()}", e) throw RuntimeException("While resolving call ${functionCall.render()}", e)
} }
dataFlowAnalyzer.exitFunctionCall(completeInference)
return completeInference.compose() return completeInference.compose()
} }
@@ -437,6 +471,7 @@ open class FirBodyResolveTransformer(
override fun transformBlock(block: FirBlock, data: Any?): CompositeTransformResult<FirStatement> { override fun transformBlock(block: FirBlock, data: Any?): CompositeTransformResult<FirStatement> {
dataFlowAnalyzer.enterBlock(block)
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val block = block.transformChildren(this, data) as FirBlock val block = block.transformChildren(this, data) as FirBlock
val statement = block.statements.lastOrNull() val statement = block.statements.lastOrNull()
@@ -451,18 +486,20 @@ open class FirBodyResolveTransformer(
} else { } else {
(resultExpression.resultType as? FirResolvedTypeRef) ?: FirErrorTypeRefImpl(null, "No type for block") (resultExpression.resultType as? FirResolvedTypeRef) ?: FirErrorTypeRefImpl(null, "No type for block")
} }
dataFlowAnalyzer.exitBlock(block)
return block.compose() return block.compose()
} }
@Deprecated("should be removed after try/when completion") override fun <E : FirTargetElement> transformJump(jump: FirJump<E>, data: Any?): CompositeTransformResult<FirStatement> {
private fun commonSuperType(types: List<FirTypeRef>): FirTypeRef? { val result = super.transformJump(jump, data)
val commonSuperType = with(NewCommonSuperTypeCalculator) { dataFlowAnalyzer.exitJump(jump)
with(inferenceComponents.ctx) { return result
commonSuperType(types.map { it.coneTypeUnsafe() }) }
}
} as ConeKotlinType override fun transformThrowExpression(throwExpression: FirThrowExpression, data: Any?): CompositeTransformResult<FirStatement> {
return FirResolvedTypeRefImpl(null, commonSuperType, emptyList()) return super.transformThrowExpression(throwExpression, data).also {
dataFlowAnalyzer.exitThrowExceptionNode(it.single as FirThrowExpression)
}
} }
override fun transformWhenExpression(whenExpression: FirWhenExpression, data: Any?): CompositeTransformResult<FirStatement> { override fun transformWhenExpression(whenExpression: FirWhenExpression, data: Any?): CompositeTransformResult<FirStatement> {
@@ -470,6 +507,7 @@ open class FirBodyResolveTransformer(
return whenExpression.compose() return whenExpression.compose()
} }
dataFlowAnalyzer.enterWhenExpression(whenExpression)
return withScopeCleanup(localScopes) with@{ return withScopeCleanup(localScopes) with@{
if (whenExpression.subjectVariable != null) { if (whenExpression.subjectVariable != null) {
localScopes += FirLocalScope() localScopes += FirLocalScope()
@@ -479,16 +517,25 @@ open class FirBodyResolveTransformer(
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val whenExpression = syntheticCallGenerator.generateCalleeForWhenExpression(whenExpression) ?: run { val whenExpression = syntheticCallGenerator.generateCalleeForWhenExpression(whenExpression) ?: run {
whenExpression.resultType = FirErrorTypeRefImpl(null, "Inapplicable when expression") dataFlowAnalyzer.exitWhenExpression(whenExpression)
whenExpression.resultType = FirErrorTypeRefImpl(null, "")
return@with whenExpression.compose() return@with whenExpression.compose()
} }
val expectedTypeRef = data as FirTypeRef? val expectedTypeRef = data as FirTypeRef?
val result = callCompleter.completeCall(whenExpression, expectedTypeRef) val result = callCompleter.completeCall(whenExpression, expectedTypeRef)
dataFlowAnalyzer.exitWhenExpression(result)
result.compose() result.compose()
} }
} }
override fun transformWhenBranch(whenBranch: FirWhenBranch, data: Any?): CompositeTransformResult<FirWhenBranch> {
return whenBranch.also { dataFlowAnalyzer.enterWhenBranchCondition(whenBranch) }
.transformCondition(this, data).also { dataFlowAnalyzer.exitWhenBranchCondition(it) }
.transformResult(this, data).also { dataFlowAnalyzer.exitWhenBranchResult(it) }
.compose()
}
override fun transformWhenSubjectExpression( override fun transformWhenSubjectExpression(
whenSubjectExpression: FirWhenSubjectExpression, whenSubjectExpression: FirWhenSubjectExpression,
data: Any? data: Any?
@@ -529,7 +576,9 @@ open class FirBodyResolveTransformer(
} }
return super.transformConstExpression(constExpression, data) return super.transformConstExpression(constExpression, data).also {
dataFlowAnalyzer.exitConstExpresion(it.single as FirConstExpression<*>)
}
} }
override fun transformDeclaration(declaration: FirDeclaration, data: Any?): CompositeTransformResult<FirDeclaration> { override fun transformDeclaration(declaration: FirDeclaration, data: Any?): CompositeTransformResult<FirDeclaration> {
@@ -539,13 +588,21 @@ open class FirBodyResolveTransformer(
} }
override fun transformAnnotationCall(annotationCall: FirAnnotationCall, data: Any?): CompositeTransformResult<FirStatement> { override fun transformAnnotationCall(annotationCall: FirAnnotationCall, data: Any?): CompositeTransformResult<FirStatement> {
return (annotationCall.transformChildren(this, data) as FirStatement).compose() dataFlowAnalyzer.enterAnnotationCall(annotationCall)
return (annotationCall.transformChildren(this, data) as FirAnnotationCall).also {
dataFlowAnalyzer.exitAnnotationCall(it)
}.compose()
} }
override fun <F : FirFunction<F>> transformFunction(function: FirFunction<F>, data: Any?): CompositeTransformResult<FirDeclaration> { override fun <F : FirFunction<F>> transformFunction(function: FirFunction<F>, data: Any?): CompositeTransformResult<FirDeclaration> {
return withScopeCleanup(localScopes) { return withScopeCleanup(localScopes) {
localScopes += FirLocalScope() localScopes += FirLocalScope()
super.transformFunction(function, data) dataFlowAnalyzer.enterFunction(function)
super.transformFunction(function, data).also {
val result = it.single
// TODO: handle graph
val controlFlowGraph = dataFlowAnalyzer.exitFunction(result as FirFunction<*>)
}
} }
} }
@@ -559,7 +616,7 @@ open class FirBodyResolveTransformer(
} }
val result = transformFunction(function, returnTypeRef).single as FirFunction<*> val result = transformFunction(function, returnTypeRef).single as FirFunction<*>
val body = result.body val body = result.body
return if (result is FirTypedDeclaration && result.returnTypeRef is FirImplicitTypeRef && body != null) { return if (result.returnTypeRef is FirImplicitTypeRef && body != null) {
result.transformReturnTypeRef(this, body.resultType) result.transformReturnTypeRef(this, body.resultType)
result result
} else { } else {
@@ -680,6 +737,7 @@ open class FirBodyResolveTransformer(
localScopes.lastOrNull()?.storeDeclaration(variable) localScopes.lastOrNull()?.storeDeclaration(variable)
} }
variable.resolvePhase = transformerPhase variable.resolvePhase = transformerPhase
dataFlowAnalyzer.exitVariableDeclaration(variable)
return variable.compose() return variable.compose()
} }
@@ -687,6 +745,7 @@ open class FirBodyResolveTransformer(
val returnTypeRef = property.returnTypeRef val returnTypeRef = property.returnTypeRef
if (returnTypeRef !is FirImplicitTypeRef && implicitTypeOnly) return property.compose() if (returnTypeRef !is FirImplicitTypeRef && implicitTypeOnly) return property.compose()
if (property.resolvePhase == transformerPhase) return property.compose() if (property.resolvePhase == transformerPhase) return property.compose()
dataFlowAnalyzer.enterProperty(property)
if (returnTypeRef is FirImplicitTypeRef) { if (returnTypeRef is FirImplicitTypeRef) {
property.transformReturnTypeRef(StoreType, FirComputingImplicitTypeRef) property.transformReturnTypeRef(StoreType, FirComputingImplicitTypeRef)
} }
@@ -706,6 +765,8 @@ open class FirBodyResolveTransformer(
} }
} }
property.resolvePhase = transformerPhase property.resolvePhase = transformerPhase
// TODO: handle graph
val controlFlowGraph = dataFlowAnalyzer.exitProperty(property)
property.compose() property.compose()
} }
} }
@@ -755,18 +816,47 @@ open class FirBodyResolveTransformer(
binaryLogicExpression: FirBinaryLogicExpression, binaryLogicExpression: FirBinaryLogicExpression,
data: Any? data: Any?
): CompositeTransformResult<FirStatement> { ): CompositeTransformResult<FirStatement> {
return super.transformBinaryLogicExpression(binaryLogicExpression, builtinTypes.booleanType).also { val booleanType = builtinTypes.booleanType
(it.single as FirBinaryLogicExpression).resultType = builtinTypes.booleanType return when (binaryLogicExpression.kind) {
} FirBinaryLogicExpression.OperationKind.AND ->
binaryLogicExpression.also(dataFlowAnalyzer::enterBinaryAnd)
.transformLeftOperand(this, booleanType).also(dataFlowAnalyzer::exitLeftBinaryAndArgument)
.transformRightOperand(this, booleanType).also(dataFlowAnalyzer::exitBinaryAnd)
FirBinaryLogicExpression.OperationKind.OR ->
binaryLogicExpression.also(dataFlowAnalyzer::enterBinaryOr)
.transformLeftOperand(this, booleanType).also(dataFlowAnalyzer::exitLeftBinaryOrArgument)
.transformRightOperand(this, booleanType).also(dataFlowAnalyzer::exitBinaryOr)
}.transformRestChildren(this, booleanType).also {
it.resultType = booleanType
}.compose()
} }
override fun transformOperatorCall(operatorCall: FirOperatorCall, data: Any?): CompositeTransformResult<FirStatement> { override fun transformOperatorCall(operatorCall: FirOperatorCall, data: Any?): CompositeTransformResult<FirStatement> {
if (operatorCall.operation in FirOperation.BOOLEANS) { val result = if (operatorCall.operation in FirOperation.BOOLEANS) {
return (operatorCall.transformChildren(this, noExpectedType) as FirOperatorCall).also { (operatorCall.transformChildren(this, noExpectedType) as FirOperatorCall).also {
it.resultType = builtinTypes.booleanType it.resultType = builtinTypes.booleanType
}.compose() }
} } else {
return super.transformOperatorCall(operatorCall, data) super.transformOperatorCall(operatorCall, data).single
} as FirOperatorCall
dataFlowAnalyzer.exitOperatorCall(result)
return result.compose()
}
override fun transformWhileLoop(whileLoop: FirWhileLoop, data: Any?): CompositeTransformResult<FirStatement> {
return whileLoop.also(dataFlowAnalyzer::enterWhileLoop)
.transformCondition(this, data).also(dataFlowAnalyzer::exitWhileLoopCondition)
.transformBlock(this, data).also(dataFlowAnalyzer::exitWhileLoop)
.transformRestChildren(this, data).compose()
}
override fun transformDoWhileLoop(doWhileLoop: FirDoWhileLoop, data: Any?): CompositeTransformResult<FirStatement> {
return doWhileLoop.also(dataFlowAnalyzer::enterDoWhileLoop)
.transformBlock(this, data).also(dataFlowAnalyzer::enterDoWhileLoopCondition)
.transformCondition(this, data).also(dataFlowAnalyzer::exitDoWhileLoop)
.transformRestChildren(this, data).compose()
} }
// ----------------------- Util functions ----------------------- // ----------------------- Util functions -----------------------
@@ -27,7 +27,7 @@ FILE: recursiveCallOnWhenWithSealedClass.kt
} }
public final fun unwrap(): <ERROR TYPE REF: Inapplicable when expression> { public final fun unwrap(): <ERROR TYPE REF: > {
^unwrap when (this@R|/Maybe|) { ^unwrap when (this@R|/Maybe|) {
($subj$ is R|Maybe.Nope|) -> { ($subj$ is R|Maybe.Nope|) -> {
throw R|java/lang/Exception.Exception|(String()) throw R|java/lang/Exception.Exception|(String())
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.FirAbstractElement
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
import org.jetbrains.kotlin.fir.expressions.impl.FirErrorExpressionImpl import org.jetbrains.kotlin.fir.expressions.impl.FirErrorExpressionImpl
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor import org.jetbrains.kotlin.fir.visitors.FirVisitor
class FirErrorLoop( class FirErrorLoop(
@@ -29,4 +30,16 @@ class FirErrorLoop(
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) { override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
super<FirLoop>.acceptChildren(visitor, data) super<FirLoop>.acceptChildren(visitor, data)
} }
override fun <D> transformCondition(transformer: FirTransformer<D>, data: D): FirLoop {
return this
}
override fun <D> transformBlock(transformer: FirTransformer<D>, data: D): FirLoop {
return this
}
override fun <D> transformRestChildren(transformer: FirTransformer<D>, data: D): FirLoop {
return this
}
} }
@@ -27,6 +27,7 @@ abstract class FirBinaryLogicExpression(psi: PsiElement?) : FirUnknownTypeExpres
abstract fun <D> transformLeftOperand(transformer: FirTransformer<D>, data: D): FirBinaryLogicExpression abstract fun <D> transformLeftOperand(transformer: FirTransformer<D>, data: D): FirBinaryLogicExpression
abstract fun <D> transformRightOperand(transformer: FirTransformer<D>, data: D): FirBinaryLogicExpression abstract fun <D> transformRightOperand(transformer: FirTransformer<D>, data: D): FirBinaryLogicExpression
abstract fun <D> transformRestChildren(transformer: FirTransformer<D>, data: D): FirBinaryLogicExpression
enum class OperationKind(val token: String) { enum class OperationKind(val token: String) {
AND("&&"), OR("||") AND("&&"), OR("||")
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.expressions
import org.jetbrains.kotlin.fir.FirLabeledElement import org.jetbrains.kotlin.fir.FirLabeledElement
import org.jetbrains.kotlin.fir.VisitedSupertype import org.jetbrains.kotlin.fir.VisitedSupertype
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor import org.jetbrains.kotlin.fir.visitors.FirVisitor
interface FirLoop : @VisitedSupertype FirStatement, FirLabeledElement, FirAnnotationContainer { interface FirLoop : @VisitedSupertype FirStatement, FirLabeledElement, FirAnnotationContainer {
@@ -22,4 +23,8 @@ interface FirLoop : @VisitedSupertype FirStatement, FirLabeledElement, FirAnnota
block.accept(visitor, data) block.accept(visitor, data)
super<FirLabeledElement>.acceptChildren(visitor, data) super<FirLabeledElement>.acceptChildren(visitor, data)
} }
fun <D> transformCondition(transformer: FirTransformer<D>, data: D): FirLoop
fun <D> transformBlock(transformer: FirTransformer<D>, data: D): FirLoop
fun <D> transformRestChildren(transformer: FirTransformer<D>, data: D): FirLoop
} }
@@ -6,10 +6,12 @@
package org.jetbrains.kotlin.fir.expressions.impl package org.jetbrains.kotlin.fir.expressions.impl
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirLabel
import org.jetbrains.kotlin.fir.expressions.FirBlock import org.jetbrains.kotlin.fir.expressions.FirBlock
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirLoop import org.jetbrains.kotlin.fir.expressions.FirLoop
import org.jetbrains.kotlin.fir.transformSingle
import org.jetbrains.kotlin.fir.visitors.FirTransformer import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor import org.jetbrains.kotlin.fir.visitors.FirVisitor
@@ -27,9 +29,23 @@ abstract class FirAbstractLoop(
} }
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement { override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement {
return transformCondition(transformer, data)
.transformBlock(transformer, data)
.transformRestChildren(transformer, data)
}
override fun <D> transformCondition(transformer: FirTransformer<D>, data: D): FirAbstractLoop {
condition = condition.transformSingle(transformer, data) condition = condition.transformSingle(transformer, data)
return this
}
override fun <D> transformBlock(transformer: FirTransformer<D>, data: D): FirAbstractLoop {
block = block.transformSingle(transformer, data) block = block.transformSingle(transformer, data)
return this
}
override fun <D> transformRestChildren(transformer: FirTransformer<D>, data: D): FirAbstractLoop {
label = label?.transformSingle(transformer, data) label = label?.transformSingle(transformer, data)
return super<FirAnnotatedStatement>.transformChildren(transformer, data) return super<FirAnnotatedStatement>.transformChildren(transformer, data) as FirAbstractLoop
} }
} }
@@ -21,7 +21,7 @@ class FirBinaryLogicExpressionImpl(
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement { override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement {
transformLeftOperand(transformer, data) transformLeftOperand(transformer, data)
transformRightOperand(transformer, data) transformRightOperand(transformer, data)
return super.transformChildren(transformer, data) return transformRestChildren(transformer, data)
} }
override fun <D> transformLeftOperand(transformer: FirTransformer<D>, data: D): FirBinaryLogicExpression { override fun <D> transformLeftOperand(transformer: FirTransformer<D>, data: D): FirBinaryLogicExpression {
@@ -33,4 +33,8 @@ class FirBinaryLogicExpressionImpl(
rightOperand = rightOperand.transformSingle(transformer, data) rightOperand = rightOperand.transformSingle(transformer, data)
return this return this
} }
override fun <D> transformRestChildren(transformer: FirTransformer<D>, data: D): FirBinaryLogicExpression {
return super.transformChildren(transformer, data) as FirBinaryLogicExpression
}
} }