[FIR] Add contract description blocks support to "lightTree2FIR" conversion

This commit is contained in:
Arsen Nagdalian
2020-07-23 02:34:04 +03:00
parent 9fd104eec8
commit 66f7250380
2 changed files with 63 additions and 7 deletions
@@ -113,6 +113,16 @@ open class BaseConverter(
return kidsRef.get()
}
fun LighterASTNode?.getFirstChild(): LighterASTNode? {
val firstChild: LighterASTNode?
try {
firstChild = getChildrenAsArray()[0]
} catch (e: ArrayIndexOutOfBoundsException) {
return null
}
return firstChild
}
@OptIn(ExperimentalContracts::class)
protected inline fun LighterASTNode.forEachChildren(vararg skipTokens: KtToken, f: (LighterASTNode) -> Unit) {
val kidsArray = this.getChildrenAsArray()
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.builder.Context
import org.jetbrains.kotlin.fir.builder.extractContractDescriptionIfPossible
import org.jetbrains.kotlin.fir.builder.generateAccessorsByDelegate
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.builder.buildRawContractDescription
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
@@ -1083,6 +1084,7 @@ class DeclarationsConverter(
}
var block: LighterASTNode? = null
var expression: LighterASTNode? = null
var outerContractDescription: FirContractDescription? = null
getterOrSetter.forEachChildren {
if (it.asText == "set") isGetter = false
when (it.tokenType) {
@@ -1090,6 +1092,7 @@ class DeclarationsConverter(
MODIFIER_LIST -> modifiers = convertModifierList(it)
TYPE_REFERENCE -> returnType = convertType(it)
VALUE_PARAMETER_LIST -> firValueParameters = convertSetterParameter(it, propertyTypeRef)
CONTRACT_EFFECT_LIST -> outerContractDescription = obtainContractDescription(it)
BLOCK -> block = it
else -> if (it.isExpression()) expression = it
}
@@ -1137,10 +1140,12 @@ class DeclarationsConverter(
valueParameters += firValueParameters
}
val (body, contractDescription) = convertFunctionBody(block, expression)
this.body = body
val hasContractEffectList = outerContractDescription != null
val bodyWithContractDescription = convertFunctionBody(block, expression, hasContractEffectList)
this.body = bodyWithContractDescription.first
val contractDescription = outerContractDescription ?: bodyWithContractDescription.second
contractDescription?.let {
this.contractDescription = contractDescription
this.contractDescription = it
}
context.firFunctionTargets.removeLast()
}.also {
@@ -1148,6 +1153,31 @@ class DeclarationsConverter(
}
}
private fun obtainContractDescription(rawContractDescription: LighterASTNode): FirContractDescription? =
buildRawContractDescription {
source = rawContractDescription.toFirSourceElement()
extractRawEffects(rawContractDescription, rawEffects)
}
private fun extractRawEffects(rawContractDescription: LighterASTNode, destination: MutableList<FirExpression>) {
rawContractDescription.forEachChildren {
val errorReason = "The contract effect is not an expression"
when (it.tokenType) {
CONTRACT_EFFECT -> {
val effect = it.getFirstChild()
if (effect == null) {
val errorExpression = buildErrorExpression(null, ConeSimpleDiagnostic(errorReason, DiagnosticKind.ExpressionRequired))
destination.add(errorExpression)
} else {
val expression = expressionConverter.convertExpression(effect, errorReason)
destination.add(expression as FirExpression)
}
}
else -> Unit
}
}
}
/**
* this is just a VALUE_PARAMETER_LIST
*
@@ -1195,6 +1225,7 @@ class DeclarationsConverter(
var expression: LighterASTNode? = null
var hasEqToken = false
var typeParameterList: LighterASTNode? = null
var outerContractDescription: FirContractDescription? = null
functionDeclaration.forEachChildren {
when (it.tokenType) {
MODIFIER_LIST -> modifiers = convertModifierList(it)
@@ -1204,6 +1235,7 @@ class DeclarationsConverter(
COLON -> isReturnType = true
TYPE_REFERENCE -> if (isReturnType) returnType = convertType(it) else receiverType = convertType(it)
TYPE_CONSTRAINT_LIST -> typeConstraints += convertTypeConstraints(it)
CONTRACT_EFFECT_LIST -> outerContractDescription = obtainContractDescription(it)
BLOCK -> block = it
EQ -> hasEqToken = true
else -> if (it.isExpression()) expression = it
@@ -1269,8 +1301,11 @@ class DeclarationsConverter(
addCapturedTypeParameters(typeParameters)
}
valueParametersList?.let { list -> valueParameters += convertValueParameters(list).map { it.firValueParameter } }
val (body, contractDescription) = convertFunctionBody(block, expression)
this.body = body
val hasContractEffectList = outerContractDescription != null
val bodyWithContractDescription = convertFunctionBody(block, expression, hasContractEffectList)
this.body = bodyWithContractDescription.first
val contractDescription = outerContractDescription ?: bodyWithContractDescription.second
contractDescription?.let {
// TODO: add error reporting for contracts on lambdas
if (this is FirSimpleFunctionBuilder) {
@@ -1288,9 +1323,20 @@ class DeclarationsConverter(
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parseFunctionBody
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.buildFirBody
*/
private fun convertFunctionBody(blockNode: LighterASTNode?, expression: LighterASTNode?): Pair<FirBlock?, FirContractDescription?> {
private fun convertFunctionBody(
blockNode: LighterASTNode?,
expression: LighterASTNode?,
hasContractEffectList: Boolean = false
): Pair<FirBlock?, FirContractDescription?> {
return when {
blockNode != null -> convertBlock(blockNode).extractContractDescriptionIfPossible()
blockNode != null -> {
val block = convertBlock(blockNode)
if (hasContractEffectList) {
block to null
} else {
block.extractContractDescriptionIfPossible()
}
}
expression != null -> FirSingleExpressionBlock(
expressionConverter.getAsFirExpression<FirExpression>(expression, "Function has no body (but should)").toReturn()
) to null