FIR renderer: extract separate ConeTypeRenderer

This commit is contained in:
Mikhail Glukhikh
2022-07-05 11:56:50 +02:00
committed by Space
parent 591143be7c
commit a82baf87cb
46 changed files with 438 additions and 266 deletions
@@ -161,7 +161,7 @@ internal class KtFirTypeProvider(
require(typeParameterSymbols.size == argumentTypes.size) {
"'${symbol.fir.render(FirRenderer.RenderMode.NoBodies)}' expects '${typeParameterSymbols.size}' type arguments " +
"but type '${this.render()}' has ${argumentTypes.size} type arguments."
"but type '${this.renderForDebugging()}' has ${argumentTypes.size} type arguments."
}
val substitutor = substitutorByMap(typeParameterSymbols.zip(argumentTypes).toMap(), session)
@@ -77,7 +77,7 @@ internal class ConeTypeIdeRenderer(
}
is ConeCapturedType -> {
renderAnnotationList(type)
append(type.render())
append(type.renderReadableWithFqNames())
renderNullability(type.type)
}
is ConeDefinitelyNotNullType -> {
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.types.KtCapturedType
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
import org.jetbrains.kotlin.fir.types.ConeCapturedType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
internal class KtFirCapturedType(
override val coneType: ConeCapturedType,
@@ -27,7 +27,7 @@ internal class KtFirCapturedType(
KtFirAnnotationListForType.create(coneType, builder.rootSession, token)
}
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.analysis.api.types.KtClassErrorType
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.types.ConeErrorType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
internal class KtFirClassErrorType(
override val coneType: ConeErrorType,
@@ -37,7 +37,7 @@ internal class KtFirClassErrorType(
symbols.map { builder.classifierBuilder.buildClassLikeSymbol(it) }
}
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
override fun hashCode() = typeHashcode()
}
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.types.KtDefinitelyNotNullType
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.fir.types.ConeDefinitelyNotNullType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
internal class KtFirDefinitelyNotNullType(
override val coneType: ConeDefinitelyNotNullType,
@@ -26,7 +26,7 @@ internal class KtFirDefinitelyNotNullType(
KtFirAnnotationListForType.create(coneType, builder.rootSession, token)
}
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
override fun hashCode() = typeHashcode()
}
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.types.KtDynamicType
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
import org.jetbrains.kotlin.fir.types.ConeDynamicType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
internal class KtFirDynamicType(
override val coneType: ConeDynamicType,
@@ -26,7 +26,7 @@ internal class KtFirDynamicType(
}
override val nullability: KtTypeNullability get() = withValidityAssertion { coneType.nullability.asKtNullability() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
override fun hashCode() = typeHashcode()
}
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.analysis.api.types.KtFlexibleType
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
internal class KtFirFlexibleType(
override val coneType: ConeFlexibleType,
@@ -30,7 +30,7 @@ internal class KtFirFlexibleType(
}
override val nullability: KtTypeNullability get() = withValidityAssertion { coneType.nullability.asKtNullability() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
override fun hashCode() = typeHashcode()
}
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.isExtensionFunctionType
import org.jetbrains.kotlin.fir.types.isSuspendFunctionType
import org.jetbrains.kotlin.fir.types.receiverType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
import org.jetbrains.kotlin.name.ClassId
internal class KtFirFunctionalType(
@@ -73,7 +73,7 @@ internal class KtFirFunctionalType(
override val returnType: KtType
get() = withValidityAssertion { (typeArguments.last() as KtTypeArgumentWithVariance).type }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
override fun hashCode() = typeHashcode()
}
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.analysis.api.types.KtClassType
import org.jetbrains.kotlin.analysis.api.types.KtIntegerLiteralType
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
import org.jetbrains.kotlin.fir.types.ConeIntegerLiteralConstantType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
internal class KtFirIntegerLiteralType(
override val coneType: ConeIntegerLiteralConstantType,
@@ -38,7 +38,7 @@ internal class KtFirIntegerLiteralType(
override val nullability: KtTypeNullability get() = withValidityAssertion { coneType.nullability.asKtNullability() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
override fun hashCode() = typeHashcode()
}
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.analysis.api.types.KtIntersectionType
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
import org.jetbrains.kotlin.fir.types.ConeIntersectionType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
internal class KtFirIntersectionType(
override val coneType: ConeIntersectionType,
@@ -30,7 +30,7 @@ internal class KtFirIntersectionType(
}
override val nullability: KtTypeNullability get() = withValidityAssertion { coneType.nullability.asKtNullability() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
override fun hashCode() = typeHashcode()
}
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtTypeParameterSymbol
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
import org.jetbrains.kotlin.analysis.api.types.KtTypeParameterType
import org.jetbrains.kotlin.fir.types.ConeTypeParameterType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
import org.jetbrains.kotlin.name.Name
internal class KtFirTypeParameterType(
@@ -35,7 +35,7 @@ internal class KtFirTypeParameterType(
override val nullability: KtTypeNullability get() = withValidityAssertion { coneType.nullability.asKtNullability() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
override fun hashCode() = typeHashcode()
}
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtClassLikeSymbol
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
import org.jetbrains.kotlin.analysis.api.types.KtUsualClassType
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
import org.jetbrains.kotlin.name.ClassId
internal class KtFirUsualClassType(
@@ -40,7 +40,7 @@ internal class KtFirUsualClassType(
}
override val nullability: KtTypeNullability get() = withValidityAssertion { coneType.nullability.asKtNullability() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
override fun hashCode() = typeHashcode()
}
@@ -52,8 +52,8 @@ abstract class AbstractFirLazyBodiesCalculatorTest : AbstractLowLevelApiSingleFi
bodyBuildingMode = BodyBuildingMode.NORMAL
).buildFirFile(ktFile)
val laziedFirFileDump = StringBuilder().also { FirRenderer(it).visitFile(laziedFirFile) }.toString()
val fullFirFileDump = StringBuilder().also { FirRenderer(it).visitFile(fullFirFile) }.toString()
val laziedFirFileDump = StringBuilder().also { FirRenderer(it).Visitor().visitFile(laziedFirFile) }.toString()
val fullFirFileDump = StringBuilder().also { FirRenderer(it).Visitor().visitFile(fullFirFile) }.toString()
TestCase.assertEquals(laziedFirFileDump, fullFirFileDump)
}
@@ -37,14 +37,14 @@ abstract class AbstractFirLoadBinariesTest : AbstractFirResolveWithSessionTestCa
for (name in declarationNames) {
for (symbol in provider.getTopLevelCallableSymbols(packageFqName, name)) {
symbol.fir.accept(firRenderer)
symbol.fir.accept(firRenderer.Visitor())
builder.appendLine()
}
}
for (name in declarationNames) {
val classLikeSymbol = provider.getClassLikeSymbolByClassId(ClassId.topLevel(packageFqName.child(name))) ?: continue
classLikeSymbol.fir.accept(firRenderer)
classLikeSymbol.fir.accept(firRenderer.Visitor())
builder.appendLine()
}
@@ -24,9 +24,10 @@ fun renderJavaClass(renderer: FirRenderer, javaClass: FirJavaClass, session: Fir
val memberScope = javaClass.unsubstitutedScope(session, ScopeSession(), withForcedTypeCalculator = true)
val staticScope = javaClass.scopeProvider.getStaticScope(javaClass, session, ScopeSession())
val visitor = renderer.Visitor()
renderer.renderAnnotations(javaClass)
renderer.visitMemberDeclaration(javaClass)
visitor.visitMemberDeclaration(javaClass)
renderer.renderSupertypes(javaClass)
renderer.renderInBraces {
val renderedDeclarations = mutableListOf<FirDeclaration>()
@@ -34,7 +35,7 @@ fun renderJavaClass(renderer: FirRenderer, javaClass: FirJavaClass, session: Fir
fun renderAndCache(symbol: FirCallableSymbol<*>) {
val enhanced = symbol.fir
if (enhanced !in renderedDeclarations) {
enhanced.accept(renderer, null)
enhanced.accept(visitor, null)
renderer.newLine()
renderedDeclarations += enhanced
}
@@ -55,7 +56,7 @@ fun renderJavaClass(renderer: FirRenderer, javaClass: FirJavaClass, session: Fir
is FirJavaField -> scopeToUse!!.processPropertiesByName(declaration.name, ::renderAndCache)
is FirEnumEntry -> scopeToUse!!.processPropertiesByName(declaration.name, ::renderAndCache)
else -> {
declaration.accept(renderer, null)
declaration.accept(visitor, null)
renderer.newLine()
renderedDeclarations += declaration
}
@@ -23,10 +23,7 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateErr
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableWrongReceiver
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.types.ConeErrorType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.types.AbstractTypeChecker
@@ -72,7 +69,7 @@ object FirDelegatedPropertyChecker : FirPropertyChecker() {
val errorNamedReference = functionCall.calleeReference as? FirErrorNamedReference ?: return false
if (errorNamedReference.source?.kind != KtFakeSourceElementKind.DelegatedPropertyAccessor) return false
val expectedFunctionSignature =
(if (isGet) "getValue" else "setValue") + "(${functionCall.arguments.joinToString(", ") { it.typeRef.coneType.render() }})"
(if (isGet) "getValue" else "setValue") + "(${functionCall.arguments.joinToString(", ") { it.typeRef.coneType.renderReadable() }})"
val delegateDescription = if (isGet) "delegate" else "delegate for var (read-write property)"
fun reportInapplicableDiagnostics(candidates: Collection<FirBasedSymbol<*>>) {
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.types.ConeErrorType
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderForDebugging
class ErrorNodeDiagnosticCollectorComponent(
session: FirSession,
@@ -42,7 +42,7 @@ class ErrorNodeDiagnosticCollectorComponent(
override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef, data: CheckerContext) {
assert(resolvedTypeRef.type !is ConeErrorType) {
"Instead use FirErrorTypeRef for ${resolvedTypeRef.type.render()}"
"Instead use FirErrorTypeRef for ${resolvedTypeRef.type.renderForDebugging()}"
}
}
@@ -19,7 +19,7 @@ import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.render
import org.jetbrains.kotlin.fir.types.renderReadableWithFqNames
import org.jetbrains.kotlin.name.CallableId
object FirDiagnosticRenderers {
@@ -89,7 +89,7 @@ object FirDiagnosticRenderers {
val RENDER_TYPE = Renderer { t: ConeKotlinType ->
// TODO: need a way to tune granuality, e.g., without parameter names in functional types.
t.render()
t.renderReadableWithFqNames()
}
val FQ_NAMES_IN_TYPES = Renderer { symbol: FirBasedSymbol<*> ->
@@ -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)
}
}
}
}
@@ -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("|")
}
}
@@ -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
}
@@ -685,15 +685,15 @@ class FirElementSerializer private constructor(
typeApproximator.approximateToSubType(type, TypeApproximatorConfiguration.PublicDeclaration)
}
assert(approximatedType != type && approximatedType is ConeKotlinType) {
"Approximation failed: ${type.render()}"
"Approximation failed: ${type.renderForDebugging()}"
}
return typeProto(approximatedType as ConeKotlinType)
}
is ConeIntegerLiteralType -> {
throw IllegalStateException("Integer literal types should not persist up to the serializer: ${type.render()}")
throw IllegalStateException("Integer literal types should not persist up to the serializer: ${type.renderForDebugging()}")
}
is ConeCapturedType -> {
throw IllegalStateException("Captured types should not persist up to the serializer: ${type.render()}")
throw IllegalStateException("Captured types should not persist up to the serializer: ${type.renderForDebugging()}")
}
else -> {
throw AssertionError("Should not be here: ${type::class.java}")
@@ -121,7 +121,7 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
val toIndex = firClass.typeParameters.count { it is FirTypeParameter } + index
if (!firClass.isInner) {
assert(toIndex == typeArguments.size || firClass.isLocal) {
"${typeArguments.size - toIndex} trailing arguments were found in this type: ${render()}"
"${typeArguments.size - toIndex} trailing arguments were found in this type: ${renderForDebugging()}"
}
return PossiblyInnerConeType(classifier, typeArguments.toList().subList(index, typeArguments.size), null)
}
@@ -519,7 +519,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
override fun KotlinTypeMarker.getFunctionalTypeFromSupertypes(): KotlinTypeMarker {
require(this is ConeKotlinType)
assert(this.isBuiltinFunctionalTypeOrSubtype()) {
"Not a function type or subtype: ${this.render()}"
"Not a function type or subtype: ${this.renderForDebugging()}"
}
return fullyExpandedType(session).let {
@@ -122,7 +122,7 @@ fun <T : ConeKotlinType> T.withArguments(arguments: Array<out ConeTypeProjection
is ConeErrorType -> this
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, arguments, nullability.isNullable) as T
is ConeDefinitelyNotNullType -> ConeDefinitelyNotNullType(original.withArguments(arguments)) as T
else -> error("Not supported: $this: ${this.render()}")
else -> error("Not supported: $this: ${this.renderForDebugging()}")
}
}
@@ -154,7 +154,7 @@ fun <T : ConeKotlinType> T.withAttributes(attributes: ConeAttributes): T {
// Attributes for stub types are not supported, and it's not obvious if it should
is ConeStubType -> this
is ConeIntegerLiteralType -> this
else -> error("Not supported: $this: ${this.render()}")
else -> error("Not supported: $this: ${this.renderForDebugging()}")
} as T
}
@@ -33,7 +33,7 @@ class TotalKotlinTest : AbstractRawFirBuilderTestCase() {
DebugUtil.psiTreeToString(ktFile, false)
} else {
val firFile = ktFile.toFirFile()
StringBuilder().also { FirRenderer(it).visitFile(firFile) }.toString()
StringBuilder().also { FirRenderer(it).Visitor().visitFile(firFile) }.toString()
}
}
@@ -46,7 +46,7 @@ class TotalKotlinTest : AbstractRawFirBuilderTestCase() {
DebugUtil.lightTreeToString(lightTree, false)
} else {
val firFile = converter.buildFirFile(text, sourceFile, linesMapping)
StringBuilder().also { FirRenderer(it).visitFile(firFile) }.toString()
StringBuilder().also { FirRenderer(it).Visitor().visitFile(firFile) }.toString()
}
}
@@ -72,12 +72,12 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
//psi
val ktFile = createPsiFile(FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.path)), text.toString().trim()) as KtFile
val firFileFromPsi = ktFile.toFirFile()
val treeFromPsi = StringBuilder().also { FirRenderer(it).visitFile(firFileFromPsi) }.toString()
val treeFromPsi = StringBuilder().also { FirRenderer(it).Visitor().visitFile(firFileFromPsi) }.toString()
.replace("<ERROR TYPE REF:.*?>".toRegex(), "<ERROR TYPE REF>")
//light tree
val firFileFromLightTree = lightTreeConverter.buildFirFile(text, KtIoFileSourceFile(file), linesMapping)
val treeFromLightTree = StringBuilder().also { FirRenderer(it).visitFile(firFileFromLightTree) }.toString()
val treeFromLightTree = StringBuilder().also { FirRenderer(it).Visitor().visitFile(firFileFromLightTree) }.toString()
.replace("<ERROR TYPE REF:.*?>".toRegex(), "<ERROR TYPE REF>")
return@compareBase treeFromLightTree == treeFromPsi
@@ -104,7 +104,7 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
//psi
val ktFile = createPsiFile(FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.path)), text) as KtFile
val firFileFromPsi = ktFile.toFirFile()
val treeFromPsi = StringBuilder().also { FirRenderer(it).visitFile(firFileFromPsi) }.toString()
val treeFromPsi = StringBuilder().also { FirRenderer(it).Visitor().visitFile(firFileFromPsi) }.toString()
.replace("<Unsupported LValue.*?>".toRegex(), "<Unsupported LValue>")
.replace("<ERROR TYPE REF:.*?>".toRegex(), "<ERROR TYPE REF>")
@@ -115,7 +115,7 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
KtInMemoryTextSourceFile(file.name, file.path, text),
text.toSourceLinesMapping()
)
val treeFromLightTree = StringBuilder().also { FirRenderer(it).visitFile(firFileFromLightTree) }.toString()
val treeFromLightTree = StringBuilder().also { FirRenderer(it).Visitor().visitFile(firFileFromLightTree) }.toString()
.replace("<Unsupported LValue.*?>".toRegex(), "<Unsupported LValue>")
.replace("<ERROR TYPE REF:.*?>".toRegex(), "<ERROR TYPE REF>")
@@ -13,7 +13,7 @@ abstract class AbstractRawFirBuilderLazyBodiesTestCase : AbstractRawFirBuilderTe
override fun doRawFirTest(filePath: String) {
val file = createKtFile(filePath)
val firFile = file.toFirFile(BodyBuildingMode.LAZY_BODIES)
val firFileDump = StringBuilder().also { FirRenderer(it).visitFile(firFile) }.toString()
val firFileDump = StringBuilder().also { FirRenderer(it).Visitor().visitFile(firFile) }.toString()
val expectedPath = filePath.replace(".kt", ".lazyBodies.txt")
KotlinTestUtils.assertEqualsToFile(File(expectedPath), firFileDump)
}
@@ -65,7 +65,9 @@ abstract class AbstractRawFirBuilderTestCase : KtParsingTestCase(
protected open fun doRawFirTest(filePath: String) {
val file = createKtFile(filePath)
val firFile = file.toFirFile(BodyBuildingMode.NORMAL)
val firFileDump = StringBuilder().also { FirRenderer(it, mode = FirRenderer.RenderMode.WithDeclarationAttributes).visitFile(firFile) }.toString()
val firFileDump = StringBuilder().also {
FirRenderer(it, mode = FirRenderer.RenderMode.WithDeclarationAttributes).Visitor().visitFile(firFile)
}.toString()
val expectedPath = filePath.replace(".kt", ".txt")
KotlinTestUtils.assertEqualsToFile(File(expectedPath), firFileDump)
}
@@ -191,7 +193,7 @@ private fun throwTwiceVisitingError(element: FirElement) {
if (psiParent is KtPropertyDelegate || psiParent?.parent is KtPropertyDelegate) return
}
val elementDump = StringBuilder().also { element.accept(FirRenderer(it)) }.toString()
val elementDump = StringBuilder().also { element.accept(FirRenderer(it).Visitor()) }.toString()
throw AssertionError("FirElement ${element.javaClass} is visited twice: $elementDump")
}
@@ -65,7 +65,7 @@ class RawFirBuilderTotalKotlinTestCase : AbstractRawFirBuilderTestCase() {
time += measureNanoTime {
firFile = ktFile.toFirFile()
}
totalLength += StringBuilder().also { FirRenderer(it).visitFile(firFile) }.length
totalLength += StringBuilder().also { FirRenderer(it).Visitor().visitFile(firFile) }.length
counter++
firFile.accept(object : FirVisitorVoid() {
override fun visitElement(element: FirElement) {
@@ -20,7 +20,7 @@ abstract class FirLookupTrackerComponent : FirSessionComponent {
fun FirLookupTrackerComponent.recordCallLookup(callInfo: AbstractCallInfo, inType: ConeKotlinType) {
if (inType.classId?.isLocal == true) return
val scopes = SmartList(inType.render().replace('/', '.'))
val scopes = SmartList(inType.renderForDebugging().replace('/', '.'))
if (inType.classId?.shortClassName?.asString() == "Companion") {
scopes.add(inType.classId!!.outerClassId!!.asString().replace('/', '.'))
}
@@ -16,6 +16,7 @@ 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.fir.types.renderForDebugging
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.fir.contracts.description
import org.jetbrains.kotlin.fir.types.renderForDebugging
class ConeContractRenderer(private val builder: StringBuilder) : ConeContractDescriptionVisitor<Unit, Nothing?>() {
override fun visitConditionalEffectDeclaration(conditionalEffect: ConeConditionalEffectDeclaration, data: Nothing?) {
conditionalEffect.effect.accept(this, data)
@@ -37,7 +39,7 @@ class ConeContractRenderer(private val builder: StringBuilder) : ConeContractDes
override fun visitIsInstancePredicate(isInstancePredicate: ConeIsInstancePredicate, data: Nothing?) {
isInstancePredicate.arg.accept(this, data)
builder.append(" ${if (isInstancePredicate.isNegated) "!" else ""}is ${isInstancePredicate.type}")
builder.append(" ${if (isInstancePredicate.isNegated) "!" else ""}is ${isInstancePredicate.type.renderForDebugging()}")
}
override fun visitIsNullPredicate(isNullPredicate: ConeIsNullPredicate, data: Nothing?) {
@@ -23,7 +23,7 @@ open class FirAnnotationRenderer internal constructor(components: FirRendererCom
}
}
private fun renderAnnotation(annotation: FirAnnotation) {
internal fun renderAnnotation(annotation: FirAnnotation) {
printer.print("@")
annotation.useSiteTarget?.let {
printer.print(it.name)
@@ -20,7 +20,7 @@ class FirBodyRenderer internal constructor(components: FirRendererComponents) :
if (block == null) return
when (block) {
is FirLazyBlock -> {
println(" { LAZY_BLOCK }")
printer.println(" { LAZY_BLOCK }")
}
else -> {
annotationRenderer?.render(block)
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.fir.renderer
import org.jetbrains.kotlin.utils.Printer
abstract class FirPrinter internal constructor(builder: StringBuilder) {
open class FirPrinter(builder: StringBuilder) {
private val printer = Printer(builder)
private var lineBeginning = true
@@ -47,6 +47,8 @@ open class FirRenderer private constructor(
override var bodyRenderer: FirBodyRenderer? = null
override lateinit var typeRenderer: ConeTypeRenderer
override lateinit var visitor: Visitor
override lateinit var printer: FirPrinter
@@ -154,12 +156,18 @@ open class FirRenderer private constructor(
else ->
null
}
private val bodyRenderer = if (mode.renderBodies) FirBodyRenderer(components) else null
private val bodyRenderer =
if (mode.renderBodies) FirBodyRenderer(components) else null
@Suppress("LeakingThis")
private val typeRenderer =
if (mode.renderDetailedTypeReferences) ConeTypeRendererForDebugging(builder) else ConeTypeRenderer(builder)
init {
components.visitor = visitor
components.annotationRenderer = annotationRenderer
components.bodyRenderer = bodyRenderer
components.typeRenderer = typeRenderer
@Suppress("LeakingThis")
components.printer = this
}
@@ -188,7 +196,7 @@ open class FirRenderer private constructor(
if (index > 0) {
print(", ")
}
print(element.render())
print(element.renderForDebugging())
}
}
@@ -318,6 +326,10 @@ open class FirRenderer private constructor(
}
}
fun renderAnnotations(annotationContainer: FirAnnotationContainer) {
annotationRenderer?.render(annotationContainer)
}
protected open fun renderClassDeclarations(regularClass: FirRegularClass) {
if (mode.renderNestedDeclarations) {
regularClass.declarations.renderDeclarations()
@@ -346,6 +358,14 @@ open class FirRenderer private constructor(
popIndent()
}
override fun visitAnnotation(annotation: FirAnnotation) {
annotationRenderer?.renderAnnotation(annotation)
}
override fun visitAnnotationCall(annotationCall: FirAnnotationCall) {
annotationRenderer?.renderAnnotation(annotationCall)
}
override fun visitCallableDeclaration(callableDeclaration: FirCallableDeclaration) {
renderContexts(callableDeclaration.contextReceivers)
annotationRenderer?.render(callableDeclaration)
@@ -603,7 +623,7 @@ open class FirRenderer private constructor(
constructor.renderDeclarationData()
constructor.dispatchReceiverType?.let {
print(it.render())
typeRenderer.render(it)
print(".")
}
print("constructor")
@@ -1051,15 +1071,7 @@ open class FirRenderer private constructor(
}
override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef) {
val kind = resolvedTypeRef.functionTypeKind
if (mode.renderDetailedTypeReferences) {
print("R|")
}
val coneType = resolvedTypeRef.type
print(coneType.renderFunctionType(kind, renderFqNames = mode.renderDetailedTypeReferences))
if (mode.renderDetailedTypeReferences) {
print("|")
}
typeRenderer.renderAsPossibleFunctionType(resolvedTypeRef.type)
}
override fun visitUserTypeRef(userTypeRef: FirUserTypeRef) {
@@ -10,4 +10,5 @@ internal interface FirRendererComponents {
val printer: FirPrinter
val annotationRenderer: FirAnnotationRenderer?
val bodyRenderer: FirBodyRenderer?
val typeRenderer: ConeTypeRenderer
}
@@ -75,13 +75,13 @@ object ConeKotlinTypeComparator : Comparator<ConeKotlinType> {
when (a) {
is ConeErrorType -> {
require(b is ConeErrorType) {
"priority is inconsistent: ${a.render()} v.s. ${b.render()}"
"priority is inconsistent: ${a.renderForDebugging()} v.s. ${b.renderForDebugging()}"
}
return a.hashCode() - b.hashCode()
}
is ConeLookupTagBasedType -> {
require(b is ConeLookupTagBasedType) {
"priority is inconsistent: ${a.render()} v.s. ${b.render()}"
"priority is inconsistent: ${a.renderForDebugging()} v.s. ${b.renderForDebugging()}"
}
val nameDiff = a.lookupTag.name.compareTo(b.lookupTag.name)
if (nameDiff != 0) {
@@ -95,7 +95,7 @@ object ConeKotlinTypeComparator : Comparator<ConeKotlinType> {
}
is ConeFlexibleType -> {
require(b is ConeFlexibleType) {
"priority is inconsistent: ${a.render()} v.s. ${b.render()}"
"priority is inconsistent: ${a.renderForDebugging()} v.s. ${b.renderForDebugging()}"
}
val lowerBoundDiff = compare(a.lowerBound, b.lowerBound)
if (lowerBoundDiff != 0) {
@@ -105,7 +105,7 @@ object ConeKotlinTypeComparator : Comparator<ConeKotlinType> {
}
is ConeCapturedType -> {
require(b is ConeCapturedType) {
"priority is inconsistent: ${a.render()} v.s. ${b.render()}"
"priority is inconsistent: ${a.renderForDebugging()} v.s. ${b.renderForDebugging()}"
}
val aHasLowerType = if (a.lowerType != null) 1 else 0
val bHasLowerType = if (b.lowerType != null) 1 else 0
@@ -127,13 +127,13 @@ object ConeKotlinTypeComparator : Comparator<ConeKotlinType> {
}
is ConeDefinitelyNotNullType -> {
require(b is ConeDefinitelyNotNullType) {
"priority is inconsistent: ${a.render()} v.s. ${b.render()}"
"priority is inconsistent: ${a.renderForDebugging()} v.s. ${b.renderForDebugging()}"
}
return compare(a.original, b.original)
}
is ConeIntersectionType -> {
require(b is ConeIntersectionType) {
"priority is inconsistent: ${a.render()} v.s. ${b.render()}"
"priority is inconsistent: ${a.renderForDebugging()} v.s. ${b.renderForDebugging()}"
}
val sizeDiff = a.intersectedTypes.size - b.intersectedTypes.size
if (sizeDiff != 0) {
@@ -144,7 +144,7 @@ object ConeKotlinTypeComparator : Comparator<ConeKotlinType> {
}
is ConeStubType -> {
require(b is ConeStubType) {
"priority is inconsistent: ${a.render()} v.s. ${b.render()}"
"priority is inconsistent: ${a.renderForDebugging()} v.s. ${b.renderForDebugging()}"
}
val nameDiff = a.constructor.variable.typeConstructor.name.compareTo(b.constructor.variable.typeConstructor.name)
if (nameDiff != 0) {
@@ -154,7 +154,7 @@ object ConeKotlinTypeComparator : Comparator<ConeKotlinType> {
}
is ConeIntegerLiteralConstantType -> {
require(b is ConeIntegerLiteralConstantType) {
"priority is inconsistent: ${a.render()} v.s. ${b.render()}"
"priority is inconsistent: ${a.renderForDebugging()} v.s. ${b.renderForDebugging()}"
}
val valueDiff = a.value - b.value
if (valueDiff != 0L) {
@@ -171,7 +171,7 @@ object ConeKotlinTypeComparator : Comparator<ConeKotlinType> {
return compare(a.nullability, b.nullability)
}
else ->
error("Unsupported type comparison: ${a.render()} v.s. ${b.render()}")
error("Unsupported type comparison: ${a.renderForDebugging()} v.s. ${b.renderForDebugging()}")
}
}
}
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
@@ -15,6 +14,8 @@ import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
import org.jetbrains.kotlin.fir.extensions.extensionService
import org.jetbrains.kotlin.fir.extensions.typeAttributeExtensions
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.renderer.ConeTypeRenderer
import org.jetbrains.kotlin.fir.renderer.ConeTypeRendererForDebugging
import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds
@@ -195,13 +196,3 @@ val ConeKotlinType.canBeNull: Boolean
}
}
val ConeKotlinType?.functionTypeKind: FunctionClassKind?
get() {
val classId = (this as? ConeClassLikeType)?.lookupTag?.classId ?: return null
return FunctionClassKind.getFunctionalClassKind(
classId.shortClassName.asString(), classId.packageFqName
)
}
val FirResolvedTypeRef.functionTypeKind: FunctionClassKind?
get() = type.functionTypeKind
@@ -103,7 +103,7 @@ abstract class AbstractKtDiagnosticsTest : AbstractFirBaseDiagnosticsTest() {
}
fun checkFir(testDataFile: File, firFiles: List<FirFile>) {
val firFileDump = StringBuilder().apply { firFiles.forEach { it.accept(FirRenderer(this), null) } }.toString()
val firFileDump = StringBuilder().apply { firFiles.forEach { it.accept(FirRenderer(this).Visitor(), null) } }.toString()
val expectedPath = testDataFile.absolutePath.replace(".kt", ".txt")
KotlinTestUtils.assertEqualsToFile(
File(expectedPath),
@@ -5,9 +5,7 @@
package org.jetbrains.kotlin.fir
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtIoFileSourceFile
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
@@ -339,7 +337,7 @@ class FirResolveBench(val withProgress: Boolean, val listener: BenchListener? =
return
}
val psi = resolvedTypeRef.psi ?: return
val problem = "${resolvedTypeRef::class.simpleName} -> ${type::class.simpleName}: ${type.render()}"
val problem = "${resolvedTypeRef::class.simpleName} -> ${type::class.simpleName}: ${type.renderForDebugging()}"
reportProblem(problem, psi)
}
}
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.renderer.ConeTypeRendererForDebugging
import org.jetbrains.kotlin.fir.renderer.FirPrinter
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
@@ -322,10 +324,22 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
private val filePackage = firFile.packageFqName.toString()
private val filePackageWithSlash = filePackage.replace(".", "/")
private fun ConeTypeProjection.tryToRenderConeAsFunctionType(): String {
if (this !is ConeKotlinType) return localTypeRenderer()
val functionType = renderFunctionType(functionTypeKind, renderFqNames = true) { localTypeRenderer() }
return functionType.removeCurrentFilePackage()
private fun ConeTypeProjection.tryToRenderConeAsFunctionTypeString(): String {
return buildString {
tryToRenderConeAsFunctionType(this@buildString)
}
}
private fun ConeTypeProjection.tryToRenderConeAsFunctionType(builder: StringBuilder) {
if (this !is ConeKotlinType) {
localTypeProjectionRenderer(builder)
return
}
val projectionBuilder = StringBuilder()
ConeTypeRendererForDebugging(projectionBuilder).renderAsPossibleFunctionType(this) {
localTypeProjectionRenderer(projectionBuilder)
}
builder.append(projectionBuilder.toString().removeCurrentFilePackage())
}
// TODO rewrite or extract in common utils
@@ -384,33 +398,49 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
return null
}
private fun ConeTypeProjection.localTypeRenderer(): String {
private fun ConeTypeProjection.localTypeProjectionRenderer(builder: StringBuilder) {
val nullabilitySuffix = when {
this is ConeKotlinType && this !is ConeErrorType && this !is ConeErrorType -> nullability.suffix
this is ConeKotlinType && this !is ConeErrorType -> nullability.suffix
else -> ""
}
return when (this) {
is ConeKotlinTypeProjectionIn -> "in ${type.tryToRenderConeAsFunctionType()}"
is ConeKotlinTypeProjectionOut -> "out ${type.tryToRenderConeAsFunctionType()}"
when (this) {
is ConeKotlinTypeProjectionIn -> {
builder.append("in ")
type.tryToRenderConeAsFunctionType(builder)
}
is ConeKotlinTypeProjectionOut -> {
builder.append("out ")
type.tryToRenderConeAsFunctionType(builder)
}
is ConeStarProjection -> builder.append("*")
is ConeClassLikeType -> {
val type = this.fullyExpandedType(session)
if (type != this) return type.tryToRenderConeAsFunctionType()
val classId = type.lookupTag.classId
buildString {
when {
classId.isLocal -> append(classId.shortClassName.asString().let { stack.getPathByName(it) + it })
else -> append(classId.asString())
}
if (type.typeArguments.isNotEmpty()) {
append(type.typeArguments.joinToString(prefix = "<", postfix = ">") { it.tryToRenderConeAsFunctionType() })
}
append(nullabilitySuffix)
if (type != this) {
type.tryToRenderConeAsFunctionType(builder)
return
}
val classId = type.lookupTag.classId
when {
classId.isLocal -> builder.append(classId.shortClassName.asString().let { stack.getPathByName(it) + it })
else -> builder.append(classId.asString())
}
if (type.typeArguments.isNotEmpty()) {
builder.append("<")
for ((index, typeArgument) in type.typeArguments.withIndex()) {
if (index != 0) {
builder.append(", ")
}
typeArgument.tryToRenderConeAsFunctionType(builder)
}
builder.append(">")
}
builder.append(nullabilitySuffix)
}
is ConeLookupTagBasedType -> lookupTag.name.asString() + nullabilitySuffix
is ConeLookupTagBasedType -> builder.append(lookupTag.name.asString() + nullabilitySuffix)
is ConeFlexibleType -> {
val lowerRendered = lowerBound.tryToRenderConeAsFunctionType()
val lowerBuilder = StringBuilder()
lowerBound.tryToRenderConeAsFunctionType(lowerBuilder)
if (lowerBound.nullability == ConeNullability.NOT_NULL && upperBound.nullability == ConeNullability.NULLABLE &&
AbstractStrictEqualityTypeChecker
.strictEqualTypes(
@@ -419,17 +449,20 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
upperBound.withNullability(ConeNullability.NOT_NULL, session.typeContext)
)
) {
"$lowerRendered!"
builder.append(lowerBuilder)
} else {
val upperRendered = upperBound.tryToRenderConeAsFunctionType()
tryToSquashFlexibleType(lowerRendered, upperRendered) ?: "$lowerRendered..$upperRendered"
val upperBuilder = StringBuilder()
upperBound.tryToRenderConeAsFunctionType(upperBuilder)
builder.append(
tryToSquashFlexibleType(lowerBuilder.toString(), upperBuilder.toString()) ?: "$lowerBuilder..$upperBuilder"
)
}
}
is ConeIntersectionType -> {
intersectedTypes.map { it.render().replace("/", ".").replace("kotlin.", "") }.sorted()
intersectedTypes.map { it.renderForDebugging().replace("/", ".").replace("kotlin.", "") }.sorted()
.joinToString(separator = " & ", prefix = "{", postfix = "}")
}
else -> this.render(renderFqNames = true)
else -> builder.append((this as ConeKotlinType).renderForDebugging())
}
}
@@ -485,11 +518,11 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
if (original.isVal) data.append("val ") else if (original.isVar) data.append("var ")
data.append("(")
.append(original.dispatchReceiverType?.tryToRenderConeAsFunctionType())
.append(").")
original.dispatchReceiverType?.tryToRenderConeAsFunctionType(data)
data.append(").")
.append(original.name)
.append(": ")
.append(original.returnTypeRef.coneType.tryToRenderConeAsFunctionType())
original.returnTypeRef.coneType.tryToRenderConeAsFunctionType(data)
}
private fun renderVariable(variable: FirVariable, data: StringBuilder) {
@@ -513,15 +546,15 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
}
val receiver = when (variable) {
is FirField -> variable.dispatchReceiverType?.tryToRenderConeAsFunctionType()
is FirField -> variable.dispatchReceiverType?.tryToRenderConeAsFunctionTypeString()
else -> getSymbolId(variable.symbol)
}
data.append(receiver)
.append(variable.symbol.callableId.callableName)
.append(": ")
.append(returnType?.tryToRenderConeAsFunctionType())
.append((variable as? FirValueParameter)?.defaultValue?.let { " = ..." } ?: "")
returnType?.tryToRenderConeAsFunctionType(data)
data.append((variable as? FirValueParameter)?.defaultValue?.let { " = ..." } ?: "")
}
private fun renderPropertySymbol(symbol: FirPropertySymbol, data: StringBuilder) {
@@ -533,7 +566,7 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
when {
receiver != null -> data.append(receiver).append(".").append(symbol.callableId.callableName)
fir.dispatchReceiverType != null -> {
val dispatchReceiver = fir.dispatchReceiverType?.tryToRenderConeAsFunctionType()
val dispatchReceiver = fir.dispatchReceiverType?.tryToRenderConeAsFunctionTypeString()
data.append("($dispatchReceiver)").append(".").append(symbol.callableId.callableName)
}
else -> {
@@ -578,7 +611,7 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
}
call.dispatchReceiver !is FirNoReceiverExpression -> {
data.append("(")
val dispatch = fir.dispatchReceiverType!!.tryToRenderConeAsFunctionType()
val dispatch = fir.dispatchReceiverType!!.tryToRenderConeAsFunctionTypeString()
.let { if (it.endsWith("!")) it.dropLast(1) else it } // this hack drop flexible annotation for receiver
data.append(dispatch).append(").").append(callableName)
}
@@ -654,7 +687,7 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
override fun visitValueParameter(valueParameter: FirValueParameter, data: StringBuilder) {
if (valueParameter.isVararg) {
data.append("vararg ")
valueParameter.returnTypeRef.coneType.arrayElementType()?.let { data.append(it.localTypeRenderer()) }
valueParameter.returnTypeRef.coneType.arrayElementType()?.localTypeProjectionRenderer(data)
} else {
valueParameter.returnTypeRef.accept(this, data)
}
@@ -683,7 +716,7 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
data.append(stack.getPathByName(resolvedNamedReference.name.asString()))
.append(resolvedNamedReference.name)
.append(": ")
.append(fir.dispatchReceiverType?.tryToRenderConeAsFunctionType())
fir.dispatchReceiverType?.tryToRenderConeAsFunctionType(data)
}
symbol is FirFieldSymbol -> renderField(symbol.fir, data)
else -> (symbol.fir as? FirVariable)?.let { renderVariable(it, data) }
@@ -813,7 +846,7 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
}
override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef, data: StringBuilder) {
data.append(resolvedTypeRef.type.tryToRenderConeAsFunctionType())
resolvedTypeRef.type.tryToRenderConeAsFunctionType(data)
}
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef, data: StringBuilder) {
@@ -836,7 +869,9 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
val typeParameters = if (typeArguments.isEmpty()) "" else " <T>"
data.append("fun$typeParameters ${name.replaceFirstChar(Char::lowercaseChar)}Of")
typeArguments.firstOrNull()?.let {
data.append("<").append(it.tryToRenderConeAsFunctionType()).append(">")
data.append("<")
it.tryToRenderConeAsFunctionType(data)
data.append(">")
}
val valueParameter = name.replace("Array", "").takeIf { it.isNotEmpty() } ?: "T"
data.append("(vararg $valueParameter): $name${typeParameters.trim()}") // TODO change "T" to concrete type is array is primitive