[FIR] Add contract renderer

This commit is contained in:
Dmitriy Novozhilov
2020-04-08 15:02:32 +03:00
parent a89e0123c7
commit 50627f2c83
3 changed files with 85 additions and 5 deletions
@@ -125,16 +125,20 @@ class FirContractDeserializer(private val c: FirDeserializationContext) {
val valueParameterIndex = proto.valueParameterReference - 1
val name: String
val typeRef = if (valueParameterIndex < 0) {
name = "this"
ownerFunction.receiverTypeRef
} else {
ownerFunction.valueParameters.getOrNull(valueParameterIndex)?.returnTypeRef
val parameter = ownerFunction.valueParameters.getOrNull(valueParameterIndex) ?: return null
name = parameter.name.asString()
parameter.returnTypeRef
} ?: return null
return if (!typeRef.isBoolean)
ConeValueParameterReference(valueParameterIndex)
ConeValueParameterReference(valueParameterIndex, name)
else
ConeBooleanValueParameterReference(valueParameterIndex)
ConeBooleanValueParameterReference(valueParameterIndex, name)
}
private fun ProtoBuf.Effect.InvocationKind.toDescriptorInvocationKind(): InvocationKind? = when (this) {
@@ -0,0 +1,76 @@
/*
* 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.contracts.description
import org.jetbrains.kotlin.contracts.description.expressions.*
class ConeContractRenderer(private val builder: StringBuilder) : ConeContractDescriptionVisitor<Unit, Nothing?>() {
override fun visitConditionalEffectDeclaration(conditionalEffect: ConeConditionalEffectDeclaration, data: Nothing?) {
conditionalEffect.effect.accept(this, data)
builder.append(" -> ")
conditionalEffect.condition.accept(this, data)
}
override fun visitReturnsEffectDeclaration(returnsEffect: ConeReturnsEffectDeclaration, data: Nothing?) {
builder.append("Returns(")
returnsEffect.value.accept(this, data)
builder.append(")")
}
override fun visitCallsEffectDeclaration(callsEffect: ConeCallsEffectDeclaration, data: Nothing?) {
builder.append("CallsInPlace(")
callsEffect.valueParameterReference.accept(this, data)
builder.append(", ${callsEffect.kind})")
}
override fun visitLogicalBinaryOperationContractExpression(binaryLogicExpression: ConeBinaryLogicExpression, data: Nothing?) {
inBracketsIfNecessary(binaryLogicExpression, binaryLogicExpression.left) { binaryLogicExpression.left.accept(this, data) }
builder.append(" ${binaryLogicExpression.kind.token} ")
inBracketsIfNecessary(binaryLogicExpression, binaryLogicExpression.right) { binaryLogicExpression.right.accept(this, data) }
}
override fun visitLogicalNot(logicalNot: ConeLogicalNot, data: Nothing?) {
inBracketsIfNecessary(logicalNot, logicalNot.arg) { builder.append("!") }
logicalNot.arg.accept(this, data)
}
override fun visitIsInstancePredicate(isInstancePredicate: ConeIsInstancePredicate, data: Nothing?) {
isInstancePredicate.arg.accept(this, data)
builder.append(" ${if (isInstancePredicate.isNegated) "!" else ""}is ${isInstancePredicate.type}")
}
override fun visitIsNullPredicate(isNullPredicate: ConeIsNullPredicate, data: Nothing?) {
isNullPredicate.arg.accept(this, data)
builder.append(" ${if (isNullPredicate.isNegated) "!=" else "=="} null")
}
override fun visitConstantDescriptor(constantReference: ConeConstantReference, data: Nothing?) {
builder.append(constantReference.name)
}
override fun visitValueParameterReference(valueParameterReference: ConeValueParameterReference, data: Nothing?) {
builder.append(valueParameterReference.name)
}
private fun inBracketsIfNecessary(parent: ConeContractDescriptionElement, child: ConeContractDescriptionElement, block: () -> Unit) {
if (needsBrackets(parent, child)) {
builder.append("(")
block()
builder.append(")")
} else {
block()
}
}
private fun ConeContractDescriptionElement.isAtom(): Boolean =
this is VariableReference || this is ConstantReference || this is ConeIsNullPredicate || this is ConeIsInstancePredicate
private fun needsBrackets(parent: ConeContractDescriptionElement, child: ConeContractDescriptionElement): Boolean {
if (child.isAtom()) return false
if (parent is ConeLogicalNot) return true
return parent::class != child::class
}
}
@@ -35,7 +35,7 @@ class ConeBooleanConstantReference private constructor(name: String) : ConeConst
* Index of value parameter of function
* -1 means that it is reference to extension receiver
*/
open class ConeValueParameterReference(val parameterIndex: Int) : ConeContractDescriptionValue {
open class ConeValueParameterReference(val parameterIndex: Int, val name: String) : ConeContractDescriptionValue {
init {
assert(parameterIndex >= -1)
}
@@ -44,7 +44,7 @@ open class ConeValueParameterReference(val parameterIndex: Int) : ConeContractDe
contractDescriptionVisitor.visitValueParameterReference(this, data)
}
class ConeBooleanValueParameterReference(parameterIndex: Int) : ConeValueParameterReference(parameterIndex), ConeBooleanExpression {
class ConeBooleanValueParameterReference(parameterIndex: Int, name: String) : ConeValueParameterReference(parameterIndex, name), ConeBooleanExpression {
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitBooleanValueParameterReference(this, data)
}