FIR renderer: extract separate ConeTypeRenderer
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.renderer
|
||||
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
open class ConeTypeRenderer(protected val builder: StringBuilder) {
|
||||
|
||||
open fun renderAsPossibleFunctionType(
|
||||
type: ConeKotlinType, renderType: ConeTypeProjection.() -> Unit = { render() }
|
||||
) {
|
||||
val kind = type.functionTypeKind
|
||||
if (!kind.withPrettyRender()) {
|
||||
type.renderType()
|
||||
return
|
||||
}
|
||||
|
||||
if (type.isMarkedNullable) {
|
||||
builder.append("(")
|
||||
}
|
||||
if (kind == FunctionClassKind.SuspendFunction) {
|
||||
builder.append("suspend ")
|
||||
}
|
||||
val typeArguments = type.typeArguments
|
||||
val isExtension = type.isExtensionFunctionType
|
||||
val (receiver, otherTypeArguments) = if (isExtension && typeArguments.first() != ConeStarProjection) {
|
||||
typeArguments.first() to typeArguments.drop(1)
|
||||
} else {
|
||||
null to typeArguments.toList()
|
||||
}
|
||||
val arguments = otherTypeArguments.subList(0, otherTypeArguments.size - 1)
|
||||
val returnType = otherTypeArguments.last()
|
||||
if (receiver != null) {
|
||||
receiver.render()
|
||||
builder.append(".")
|
||||
}
|
||||
builder.append("(")
|
||||
for ((index, argument) in arguments.withIndex()) {
|
||||
if (index != 0) {
|
||||
builder.append(", ")
|
||||
}
|
||||
argument.render()
|
||||
}
|
||||
builder.append(") -> ")
|
||||
returnType.render()
|
||||
if (type.isMarkedNullable) {
|
||||
builder.append(")?")
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
private fun FunctionClassKind?.withPrettyRender(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@withPrettyRender != null)
|
||||
}
|
||||
return this != null && this != FunctionClassKind.KSuspendFunction && this != FunctionClassKind.KFunction
|
||||
}
|
||||
|
||||
fun render(type: ConeKotlinType) {
|
||||
if (type !is ConeFlexibleType && type !is ConeDefinitelyNotNullType) {
|
||||
// We don't render attributes for flexible/definitely not null types here,
|
||||
// because bounds duplicate these attributes often
|
||||
type.renderAttributes()
|
||||
}
|
||||
when (type) {
|
||||
is ConeTypeVariableType -> {
|
||||
builder.append("TypeVariable(")
|
||||
builder.append(type.lookupTag.name)
|
||||
builder.append(")")
|
||||
}
|
||||
is ConeDefinitelyNotNullType -> {
|
||||
render(type.original)
|
||||
builder.append(" & Any")
|
||||
}
|
||||
is ConeErrorType -> {
|
||||
builder.append("ERROR CLASS: ${type.diagnostic.reason}")
|
||||
}
|
||||
is ConeCapturedType -> {
|
||||
builder.append("CapturedType(")
|
||||
type.constructor.projection.render()
|
||||
builder.append(")")
|
||||
}
|
||||
is ConeClassLikeType -> {
|
||||
type.render()
|
||||
}
|
||||
is ConeLookupTagBasedType -> {
|
||||
builder.append(type.lookupTag.name.asString())
|
||||
}
|
||||
is ConeDynamicType -> {
|
||||
builder.append("dynamic")
|
||||
}
|
||||
is ConeFlexibleType -> {
|
||||
type.render()
|
||||
}
|
||||
is ConeIntersectionType -> {
|
||||
builder.append("it(")
|
||||
for ((index, intersected) in type.intersectedTypes.withIndex()) {
|
||||
if (index > 0) {
|
||||
builder.append(" & ")
|
||||
}
|
||||
render(intersected)
|
||||
}
|
||||
builder.append(")")
|
||||
}
|
||||
is ConeStubTypeForSyntheticFixation -> {
|
||||
builder.append("Stub (fixation): ${type.constructor.variable}")
|
||||
}
|
||||
is ConeStubTypeForChainInference -> {
|
||||
builder.append("Stub (chain inference): ${type.constructor.variable}")
|
||||
}
|
||||
is ConeStubType -> {
|
||||
builder.append("Stub (subtyping): ${type.constructor.variable}")
|
||||
}
|
||||
is ConeIntegerLiteralConstantType -> {
|
||||
builder.append("ILT: ${type.value}")
|
||||
}
|
||||
is ConeIntegerConstantOperatorType -> {
|
||||
builder.append("IOT")
|
||||
}
|
||||
}
|
||||
if (type !is ConeFlexibleType && type !is ConeErrorType) {
|
||||
builder.append(type.nullability.suffix)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConeClassLikeType.render() {
|
||||
lookupTag.classId.render()
|
||||
if (typeArguments.isEmpty()) return
|
||||
builder.append("<")
|
||||
for ((index, typeArgument) in typeArguments.withIndex()) {
|
||||
if (index > 0) {
|
||||
builder.append(", ")
|
||||
}
|
||||
typeArgument.render()
|
||||
}
|
||||
builder.append(">")
|
||||
}
|
||||
|
||||
protected open fun ClassId.render() {
|
||||
builder.append(relativeClassName.asString())
|
||||
}
|
||||
|
||||
private fun ConeFlexibleType.render() {
|
||||
val lowerBound = lowerBound
|
||||
val upperBound = upperBound
|
||||
if (lowerBound is ConeLookupTagBasedType && upperBound is ConeLookupTagBasedType &&
|
||||
lowerBound.lookupTag == upperBound.lookupTag &&
|
||||
lowerBound.nullability == ConeNullability.NOT_NULL && upperBound.nullability == ConeNullability.NULLABLE
|
||||
) {
|
||||
if (lowerBound !is ConeClassLikeType || lowerBound.typeArguments.isEmpty()) {
|
||||
if (upperBound !is ConeClassLikeType || upperBound.typeArguments.isEmpty()) {
|
||||
render(lowerBound)
|
||||
builder.append("!")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.append("ft<")
|
||||
render(lowerBound)
|
||||
builder.append(", ")
|
||||
render(upperBound)
|
||||
builder.append(">")
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.renderAttributes() {
|
||||
if (!attributes.any()) return
|
||||
builder.append(attributes.joinToString(" ", postfix = " ") { it.toString() })
|
||||
}
|
||||
|
||||
private fun ConeTypeProjection.render() {
|
||||
when (this) {
|
||||
ConeStarProjection -> {
|
||||
builder.append("*")
|
||||
}
|
||||
is ConeKotlinTypeConflictingProjection -> {
|
||||
builder.append("CONFLICTING-PROJECTION ")
|
||||
render(type)
|
||||
}
|
||||
is ConeKotlinTypeProjectionIn -> {
|
||||
builder.append("in ")
|
||||
render(type)
|
||||
}
|
||||
is ConeKotlinTypeProjectionOut -> {
|
||||
builder.append("out ")
|
||||
render(type)
|
||||
}
|
||||
is ConeKotlinType -> {
|
||||
render(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.renderer
|
||||
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.ConeTypeProjection
|
||||
|
||||
class ConeTypeRendererForDebugging(builder: StringBuilder) : ConeTypeRendererWithFqNames(builder) {
|
||||
override fun renderAsPossibleFunctionType(type: ConeKotlinType, renderType: ConeTypeProjection.() -> Unit) {
|
||||
builder.append("R|")
|
||||
super.renderAsPossibleFunctionType(type, renderType)
|
||||
builder.append("|")
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.renderer
|
||||
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
open class ConeTypeRendererWithFqNames(builder: StringBuilder) : ConeTypeRenderer(builder) {
|
||||
override fun ClassId.render() {
|
||||
builder.append(asString())
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.types
|
||||
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
|
||||
import org.jetbrains.kotlin.fir.renderer.ConeTypeRenderer
|
||||
import org.jetbrains.kotlin.fir.renderer.ConeTypeRendererForDebugging
|
||||
import org.jetbrains.kotlin.fir.renderer.ConeTypeRendererWithFqNames
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
@@ -107,3 +111,30 @@ fun ConeClassLikeType.replaceArgumentsWithStarProjections(): ConeClassLikeType {
|
||||
val newArguments = Array(typeArguments.size) { ConeStarProjection }
|
||||
return withArguments(newArguments)
|
||||
}
|
||||
|
||||
val ConeKotlinType?.functionTypeKind: FunctionClassKind?
|
||||
get() {
|
||||
val classId = (this as? ConeClassLikeType)?.lookupTag?.classId ?: return null
|
||||
return FunctionClassKind.getFunctionalClassKind(
|
||||
classId.shortClassName.asString(), classId.packageFqName
|
||||
)
|
||||
}
|
||||
|
||||
fun ConeKotlinType.renderForDebugging(): String {
|
||||
val builder = StringBuilder()
|
||||
ConeTypeRendererForDebugging(builder).render(this)
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
fun ConeKotlinType.renderReadable(): String {
|
||||
val builder = StringBuilder()
|
||||
ConeTypeRenderer(builder).render(this)
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
fun ConeKotlinType.renderReadableWithFqNames(): String {
|
||||
val builder = StringBuilder()
|
||||
ConeTypeRendererWithFqNames(builder).render(this)
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ sealed class ConeKotlinType : ConeKotlinTypeProjection(), KotlinTypeMarker, Type
|
||||
abstract val attributes: ConeAttributes
|
||||
|
||||
final override fun toString(): String {
|
||||
return render()
|
||||
return renderForDebugging()
|
||||
}
|
||||
|
||||
abstract override fun equals(other: Any?): Boolean
|
||||
@@ -160,7 +160,7 @@ data class ConeCapturedType(
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = 0
|
||||
var result = 7
|
||||
result = 31 * result + (lowerType?.hashCode() ?: 0)
|
||||
result = 31 * result + constructor.projection.hashCode()
|
||||
result = 31 * result + constructor.typeParameterMarker.hashCode()
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* 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.types
|
||||
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
fun ConeKotlinType.render(renderFqNames: Boolean = true): String {
|
||||
val nullabilitySuffix = if (this !is ConeFlexibleType && this !is ConeErrorType) nullability.suffix else ""
|
||||
return when (this) {
|
||||
is ConeTypeVariableType -> "${renderAttributes()}TypeVariable(${this.lookupTag.name})"
|
||||
is ConeDefinitelyNotNullType -> "${original.render(renderFqNames)} & Any"
|
||||
is ConeErrorType -> "${renderAttributes()}ERROR CLASS: ${diagnostic.reason}"
|
||||
is ConeCapturedType -> "${renderAttributes()}CapturedType(${constructor.projection.render(renderFqNames)})"
|
||||
is ConeClassLikeType -> {
|
||||
buildString {
|
||||
append(renderAttributes())
|
||||
if (renderFqNames) {
|
||||
append(lookupTag.classId.asString())
|
||||
} else {
|
||||
append(lookupTag.classId.relativeClassName.asString())
|
||||
}
|
||||
if (typeArguments.isNotEmpty()) {
|
||||
append(typeArguments.joinToString(prefix = "<", postfix = ">") {
|
||||
it.render(renderFqNames)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
is ConeLookupTagBasedType -> {
|
||||
"${renderAttributes()}${lookupTag.name.asString()}"
|
||||
}
|
||||
is ConeDynamicType -> "dynamic"
|
||||
is ConeFlexibleType -> this.render(renderFqNames)
|
||||
is ConeIntersectionType -> {
|
||||
intersectedTypes.joinToString(
|
||||
separator = " & ",
|
||||
prefix = "${renderAttributes()}it(",
|
||||
postfix = ")"
|
||||
) {
|
||||
it.render(renderFqNames)
|
||||
}
|
||||
}
|
||||
is ConeStubTypeForSyntheticFixation -> "${renderAttributes()}Stub (fixation): ${constructor.variable}"
|
||||
is ConeStubTypeForChainInference -> "${renderAttributes()}Stub (chain inference): ${constructor.variable}"
|
||||
is ConeStubType -> "${renderAttributes()}Stub (subtyping): ${constructor.variable}"
|
||||
is ConeIntegerLiteralConstantType -> "${renderAttributes()}ILT: $value"
|
||||
is ConeIntegerConstantOperatorType -> "${renderAttributes()}IOT"
|
||||
} + nullabilitySuffix
|
||||
}
|
||||
|
||||
private fun ConeFlexibleType.render(renderFqNames: Boolean): String {
|
||||
if (lowerBound is ConeLookupTagBasedType && upperBound is ConeLookupTagBasedType &&
|
||||
lowerBound.lookupTag == upperBound.lookupTag &&
|
||||
lowerBound.nullability == ConeNullability.NOT_NULL && upperBound.nullability == ConeNullability.NULLABLE
|
||||
) {
|
||||
if (lowerBound !is ConeClassLikeType || lowerBound.typeArguments.isEmpty()) {
|
||||
if (upperBound !is ConeClassLikeType || upperBound.typeArguments.isEmpty()) {
|
||||
return buildString {
|
||||
append(lowerBound.render(renderFqNames))
|
||||
append("!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return buildString {
|
||||
append("ft<")
|
||||
append(lowerBound.render(renderFqNames))
|
||||
append(", ")
|
||||
append(upperBound.render(renderFqNames))
|
||||
append(">")
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.renderAttributes(): String {
|
||||
if (!attributes.any()) return ""
|
||||
return attributes.joinToString(" ", postfix = " ") { it.toString() }
|
||||
}
|
||||
|
||||
fun ConeTypeProjection.render(renderFqNames: Boolean): String {
|
||||
return when (this) {
|
||||
ConeStarProjection -> "*"
|
||||
is ConeKotlinTypeConflictingProjection -> "CONFLICTING-PROJECTION ${type.render(renderFqNames)}"
|
||||
is ConeKotlinTypeProjectionIn -> "in ${type.render(renderFqNames)}"
|
||||
is ConeKotlinTypeProjectionOut -> "out ${type.render(renderFqNames)}"
|
||||
is ConeKotlinType -> render(renderFqNames)
|
||||
}
|
||||
}
|
||||
|
||||
fun ConeKotlinType.renderFunctionType(
|
||||
kind: FunctionClassKind?, renderFqNames: Boolean, renderType: ConeTypeProjection.() -> String = { render(renderFqNames) }
|
||||
): String {
|
||||
if (!kind.withPrettyRender()) return renderType()
|
||||
|
||||
val isExtension = isExtensionFunctionType
|
||||
|
||||
val renderedType = buildString {
|
||||
if (kind == FunctionClassKind.SuspendFunction) {
|
||||
append("suspend ")
|
||||
}
|
||||
val (receiver, otherTypeArguments) = if (isExtension && typeArguments.first() != ConeStarProjection) {
|
||||
typeArguments.first() to typeArguments.drop(1)
|
||||
} else {
|
||||
null to typeArguments.toList()
|
||||
}
|
||||
val arguments = otherTypeArguments.subList(0, otherTypeArguments.size - 1)
|
||||
val returnType = otherTypeArguments.last()
|
||||
if (receiver != null) {
|
||||
append(receiver.renderType())
|
||||
append(".")
|
||||
}
|
||||
append(arguments.joinToString(", ", "(", ")") { it.renderType() })
|
||||
append(" -> ")
|
||||
append(returnType.renderType())
|
||||
}
|
||||
return if (isMarkedNullable) "($renderedType)?" else renderedType
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun FunctionClassKind?.withPrettyRender(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@withPrettyRender != null)
|
||||
}
|
||||
return this != null && this != FunctionClassKind.KSuspendFunction && this != FunctionClassKind.KFunction
|
||||
}
|
||||
Reference in New Issue
Block a user