FIR IDE: render KtFunctionalType in KtTypeRenderer

This commit is contained in:
Ilya Kirillov
2021-02-02 21:13:34 +01:00
parent a10f54befa
commit b114a45f23
2 changed files with 126 additions and 7 deletions
@@ -16,10 +16,16 @@ abstract class KtTypeRenderer : KtAnalysisSessionComponent() {
data class KtTypeRendererOptions(
val renderFqNames: Boolean,
val renderFunctionTypes: Boolean
) {
companion object {
val DEFAULT = KtTypeRendererOptions(
renderFqNames = true
renderFqNames = true,
renderFunctionTypes = true,
)
val SHORT_NAMES = KtTypeRendererOptions(
renderFqNames = false,
renderFunctionTypes = true,
)
}
}
@@ -33,9 +39,7 @@ class KtDefaultTypeRenderer(override val analysisSession: KtAnalysisSession, ove
when (type) {
is KtDenotableType -> when (type) {
is KtClassType -> {
render(type.classId, options)
renderTypeArgumentsIfNotEmpty(type.typeArguments, options)
renderNullability(type.nullability)
renderClassType(type, options)
}
is KtTypeParameterType -> {
append(type.name.asString())
@@ -64,6 +68,67 @@ class KtDefaultTypeRenderer(override val analysisSession: KtAnalysisSession, ove
}
}
private fun StringBuilder.renderClassType(
type: KtClassType,
options: KtTypeRendererOptions
) {
when {
type is KtUsualClassType || !options.renderFunctionTypes -> {
renderClassTypeAsNonFunctional(type, options)
}
type is KtFunctionalType -> {
renderFunctionalType(type, options)
}
}
}
private fun StringBuilder.renderClassTypeAsNonFunctional(
type: KtClassType,
options: KtTypeRendererOptions
) {
render(type.classId, options)
renderTypeArgumentsIfNotEmpty(type.typeArguments, options)
renderNullability(type.nullability)
}
private fun StringBuilder.renderFunctionalType(
type: KtFunctionalType,
options: KtTypeRendererOptions
) {
wrapIf(type.nullability == KtTypeNullability.NULLABLE, left = "(", right = ")?") {
if (type.isSuspend) append("suspend ")
renderReceiverTypeOfFunctionalType(type, options)
renderParametersOfFunctionalType(type, options)
append(" -> ")
render(type.typeArguments.last(), options)
}
}
private fun StringBuilder.renderReceiverTypeOfFunctionalType(
type: KtFunctionalType,
options: KtTypeRendererOptions
) {
type.receiverType?.let { receiverType ->
render(receiverType, options)
append(".")
}
}
private fun StringBuilder.renderParametersOfFunctionalType(
type: KtFunctionalType,
options: KtTypeRendererOptions
) {
append("(")
val parameterTypes = type.parameterTypes
type.parameterTypes.forEachIndexed { index, parameterType ->
render(parameterType, options)
if (index != parameterTypes.lastIndex) {
append(", ")
}
}
append(")")
}
private fun StringBuilder.renderTypeArgumentsIfNotEmpty(typeArguments: List<KtTypeArgument>, options: KtTypeRendererOptions) {
if (typeArguments.isNotEmpty()) {
append("<")
@@ -113,4 +178,10 @@ class KtDefaultTypeRenderer(override val analysisSession: KtAnalysisSession, ove
render()
append(")")
}
private inline fun StringBuilder.wrapIf(condition: Boolean, left: String, right: String, render: StringBuilder.() -> Unit) {
if (condition) append(left)
render()
if (condition) append(right)
}
}
@@ -83,7 +83,7 @@ class KtTypeRendererTest : KotlinLightCodeInsightFixtureTestCase() {
type = "Map<List<Int?>, V?>?",
expected = "Map<List<Int?>, V?>?",
typeArguments = listOf("V"),
rendererOptions = KtTypeRendererOptions(renderFqNames = false)
rendererOptions = KtTypeRendererOptions.SHORT_NAMES
)
}
@@ -91,7 +91,55 @@ class KtTypeRendererTest : KotlinLightCodeInsightFixtureTestCase() {
doTestByExpression(
expression = "java.lang.String.CASE_INSENSITIVE_ORDER",
expected = "(Comparator<(String..String?)>..Comparator<(String..String?)>?)",
rendererOptions = KtTypeRendererOptions(renderFqNames = false)
rendererOptions = KtTypeRendererOptions.SHORT_NAMES
)
}
fun testFunctionalType() {
doTestByTypeText(
type = "(String, Int?) -> Long",
expected = "(String, Int?) -> Long",
rendererOptions = KtTypeRendererOptions.SHORT_NAMES
)
}
fun testNullableFunctionalType() {
doTestByTypeText(
type = "((String, Int?) -> Long)?",
expected = "((String, Int?) -> Long)?",
rendererOptions = KtTypeRendererOptions.SHORT_NAMES
)
}
fun testFunctionalTypeNoArguments() {
doTestByTypeText(
type = "() -> Long",
expected = "() -> Long",
rendererOptions = KtTypeRendererOptions.SHORT_NAMES
)
}
fun testFunctionalTypeReceiver() {
doTestByTypeText(
type = "Int.(String) -> Long",
expected = "Int.(String) -> Long",
rendererOptions = KtTypeRendererOptions.SHORT_NAMES
)
}
fun testFunctionalTypeUnitReturnType() {
doTestByTypeText(
type = "(String) -> Unit",
expected = "(String) -> Unit",
rendererOptions = KtTypeRendererOptions.SHORT_NAMES
)
}
fun testFunctionalTypeRenderAsUsualClassType() {
doTestByTypeText(
type = "Int.(String, Long) -> Char",
expected = "Function3<Int, String, Long, Char>",
rendererOptions = KtTypeRendererOptions.SHORT_NAMES.copy(renderFunctionTypes = false)
)
}
@@ -103,7 +151,7 @@ class KtTypeRendererTest : KotlinLightCodeInsightFixtureTestCase() {
if (x is String && x is Int) x else null
}""".trimIndent(),
expected = "(String?&Int?)",
rendererOptions = KtTypeRendererOptions(renderFqNames = false)
rendererOptions = KtTypeRendererOptions.SHORT_NAMES
)
}
}