FIR IDE: allow type rendering only in analysis session

This commit is contained in:
Ilya Kirillov
2020-12-09 19:52:06 +01:00
parent f30c6bf86a
commit 170928f498
8 changed files with 132 additions and 122 deletions
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.psi.*
*
* To create analysis session consider using [analyze]
*/
abstract class KtAnalysisSession(override val token: ValidityToken) : ValidityTokenOwner {
abstract class KtAnalysisSession(final override val token: ValidityToken) : ValidityTokenOwner {
protected abstract val smartCastProvider: KtSmartCastProvider
protected abstract val typeProvider: KtTypeProvider
protected abstract val diagnosticProvider: KtDiagnosticProvider
@@ -43,6 +43,8 @@ abstract class KtAnalysisSession(override val token: ValidityToken) : ValidityTo
protected abstract val callResolver: KtCallResolver
protected abstract val completionCandidateChecker: KtCompletionCandidateChecker
protected abstract val symbolDeclarationOverridesProvider: KtSymbolDeclarationOverridesProvider
@Suppress("LeakingThis")
protected open val typeRenderer: KtTypeRenderer = KtDefaultTypeRenderer(this, token)
/// TODO: get rid of
@Deprecated("Used only in completion now, temporary")
@@ -143,4 +145,7 @@ abstract class KtAnalysisSession(override val token: ValidityToken) : ValidityTo
psiFakeCompletionExpression,
psiReceiverExpression
)
fun KtType.render(options: KtTypeRendererOptions = KtTypeRendererOptions.DEFAULT): String =
typeRenderer.render(this, options)
}
@@ -10,7 +10,6 @@ import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import org.jetbrains.kotlin.idea.frontend.api.types.render
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.utils.PrintingLogger
@@ -0,0 +1,115 @@
/*
* 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.idea.frontend.api.components
import org.jetbrains.kotlin.idea.frontend.api.*
import org.jetbrains.kotlin.idea.frontend.api.types.*
import org.jetbrains.kotlin.name.ClassId
abstract class KtTypeRenderer : KtAnalysisSessionComponent() {
abstract fun render(type: KtType, options: KtTypeRendererOptions): String
}
data class KtTypeRendererOptions(
val renderFqNames: Boolean,
) {
companion object {
val DEFAULT = KtTypeRendererOptions(
renderFqNames = true
)
}
}
class KtDefaultTypeRenderer(override val analysisSession: KtAnalysisSession, override val token: ValidityToken) : KtTypeRenderer() {
override fun render(type: KtType, options: KtTypeRendererOptions): String = type.withValidityAssertion {
buildString { render(type, options) }
}
private fun StringBuilder.render(type: KtType, options: KtTypeRendererOptions) {
when (type) {
is KtDenotableType -> when (type) {
is KtClassType -> {
render(type.classId, options)
renderTypeArgumentsIfNotEmpty(type.typeArguments, options)
renderNullability(type.nullability)
}
is KtTypeParameterType -> {
append(type.name.asString())
renderNullability(type.nullability)
}
}
is KtNonDenotableType -> when (type) {
is KtFlexibleType -> inParens {
render(type.lowerBound, options)
append("..")
render(type.upperBound, options)
}
is KtIntersectionType -> inParens {
type.conjuncts.forEachIndexed { index, conjunct ->
render(conjunct, options)
if (index != type.conjuncts.lastIndex) {
append("&")
}
}
}
}
is KtErrorType -> {
append(type.error)
}
else -> error("Unsupported type ${type::class}")
}
}
private fun StringBuilder.renderTypeArgumentsIfNotEmpty(typeArguments: List<KtTypeArgument>, options: KtTypeRendererOptions) {
if (typeArguments.isNotEmpty()) {
append("<")
typeArguments.forEachIndexed { index, typeArgument ->
render(typeArgument, options)
if (index != typeArguments.lastIndex) {
append(", ")
}
}
append(">")
}
}
private fun StringBuilder.render(typeArgument: KtTypeArgument, options: KtTypeRendererOptions) {
when (typeArgument) {
KtStarProjectionTypeArgument -> {
append("*")
}
is KtTypeArgumentWithVariance -> {
val varianceWithSpace = when (typeArgument.variance) {
KtTypeArgumentVariance.COVARIANT -> "out "
KtTypeArgumentVariance.CONTRAVARIANT -> "in "
KtTypeArgumentVariance.INVARIANT -> ""
}
append(varianceWithSpace)
render(typeArgument.type, options)
}
}
}
private fun StringBuilder.renderNullability(nullability: KtTypeNullability) {
if (nullability == KtTypeNullability.NULLABLE) {
append("?")
}
}
private fun StringBuilder.render(classId: ClassId, options: KtTypeRendererOptions) {
if (options.renderFqNames) {
append(classId.asString().replace('/', '.'))
} else {
append(classId.shortClassName.asString())
}
}
private inline fun StringBuilder.inParens(render: StringBuilder.() -> Unit) {
append("(")
render()
append(")")
}
}
@@ -7,109 +7,3 @@ package org.jetbrains.kotlin.idea.frontend.api.types
import org.jetbrains.kotlin.idea.frontend.api.*
import org.jetbrains.kotlin.name.ClassId
private object KtTypeRenderer {
fun render(type: KtType, options: KtTypeRendererOptions = KtTypeRendererOptions.DEFAULT): String = type.withValidityAssertion {
buildString { render(type, options) }
}
private fun StringBuilder.render(type: KtType, options: KtTypeRendererOptions) {
when (type) {
is KtDenotableType -> when (type) {
is KtClassType -> {
render(type.classId, options)
renderTypeArgumentsIfNotEmpty(type.typeArguments, options)
renderNullability(type.nullability)
}
is KtTypeParameterType -> {
append(type.name.asString())
renderNullability(type.nullability)
}
}
is KtNonDenotableType -> when (type) {
is KtFlexibleType -> inParens {
render(type.lowerBound, options)
append("..")
render(type.upperBound, options)
}
is KtIntersectionType -> inParens {
type.conjuncts.forEachIndexed { index, conjunct ->
render(conjunct, options)
if (index != type.conjuncts.lastIndex) {
append("&")
}
}
}
}
is KtErrorType -> {
append(type.error)
}
else -> error("Unsupported type ${type::class}")
}
}
private fun StringBuilder.renderTypeArgumentsIfNotEmpty(typeArguments: List<KtTypeArgument>, options: KtTypeRendererOptions) {
if (typeArguments.isNotEmpty()) {
append("<")
typeArguments.forEachIndexed { index, typeArgument ->
render(typeArgument, options)
if (index != typeArguments.lastIndex) {
append(", ")
}
}
append(">")
}
}
private fun StringBuilder.render(typeArgument: KtTypeArgument, options: KtTypeRendererOptions) {
when (typeArgument) {
KtStarProjectionTypeArgument -> {
append("*")
}
is KtTypeArgumentWithVariance -> {
val varianceWithSpace = when (typeArgument.variance) {
KtTypeArgumentVariance.COVARIANT -> "out "
KtTypeArgumentVariance.CONTRAVARIANT -> "in "
KtTypeArgumentVariance.INVARIANT -> ""
}
append(varianceWithSpace)
render(typeArgument.type, options)
}
}
}
private fun StringBuilder.renderNullability(nullability: KtTypeNullability) {
if (nullability == KtTypeNullability.NULLABLE) {
append("?")
}
}
private fun StringBuilder.render(classId: ClassId, options: KtTypeRendererOptions) {
if (options.renderFqNames) {
append(classId.asString().replace('/', '.'))
} else {
append(classId.shortClassName.asString())
}
}
private inline fun StringBuilder.inParens(render: StringBuilder.() -> Unit) {
append("(")
render()
append(")")
}
}
data class KtTypeRendererOptions(
val renderFqNames: Boolean,
) {
companion object {
val DEFAULT = KtTypeRendererOptions(
renderFqNames = true
)
}
}
fun KtType.render(options: KtTypeRendererOptions = KtTypeRendererOptions.DEFAULT): String =
KtTypeRenderer.render(this, options)