[Analysis API] remove old renderer code

This commit is contained in:
Ilya Kirillov
2022-10-28 10:59:21 +02:00
parent 10b593ba8c
commit b47675916f
13 changed files with 33 additions and 1753 deletions
@@ -1,375 +0,0 @@
/*
* Copyright 2010-2021 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.analysis.api.fir.renderer
import org.jetbrains.kotlin.analysis.api.components.KtTypeRendererOptions
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.tryCollectDesignation
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.containingClassForLocal
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.diagnostics.ConeCannotInferParameterType
import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedError
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.LookupTagInternals
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.utils.addToStdlib.applyIf
internal class ConeTypeIdeRenderer(
private val session: FirSession,
private val options: KtTypeRendererOptions,
) {
companion object {
const val ERROR_TYPE_TEXT = "ERROR_TYPE"
}
private fun StringBuilder.appendError(message: String? = null) {
append(ERROR_TYPE_TEXT)
if (message != null) append(" <$message>")
}
private fun StringBuilder.renderAnnotationList(type: ConeKotlinType) {
if (options.renderTypeAnnotations) {
renderAnnotations(this@ConeTypeIdeRenderer, type.customAnnotations, session, isSingleLineAnnotations = true)
}
}
fun renderType(type: ConeTypeProjection): String = buildString {
when (type) {
is ConeErrorType -> {
renderErrorType(type)
}
is ConeClassLikeType -> {
if (options.renderFunctionType && shouldRenderAsPrettyFunctionType(type)) {
renderAnnotationList(type)
renderFunctionType(type)
} else {
renderAnnotationList(type)
renderTypeConstructorAndArguments(type)
}
}
is ConeTypeParameterType -> {
renderAnnotationList(type)
append(type.lookupTag.name.asString())
renderNullability(type.type)
}
is ConeIntersectionType -> {
renderAnnotationList(type)
type.intersectedTypes.joinTo(this, "&", prefix = "(", postfix = ")") {
renderType(it)
}
renderNullability(type.type)
}
is ConeDynamicType -> {
append("dynamic")
}
is ConeFlexibleType -> {
renderFlexibleType(type)
}
is ConeCapturedType -> {
renderAnnotationList(type)
append(type.renderReadableWithFqNames())
renderNullability(type.type)
}
is ConeDefinitelyNotNullType -> {
renderAnnotationList(type)
append(renderType(type.original))
append("!!")
}
else -> appendError("Unexpected cone type ${type::class.qualifiedName}")
}
}
private fun StringBuilder.renderFlexibleType(type: ConeFlexibleType) {
renderAnnotationList(type)
append(renderFlexibleType(renderType(type.lowerBound), renderType(type.upperBound)))
}
private fun StringBuilder.renderErrorType(type: ConeErrorType) {
val diagnostic = type.diagnostic
if (options.renderUnresolvedTypeAsResolved) {
when (diagnostic) {
is ConeUnresolvedError -> {
val qualifierRendered = diagnostic.qualifier.let { FqName(it).render() }
append(qualifierRendered)
}
is ConeCannotInferParameterType -> {
append(diagnostic.typeParameter.name.render())
}
else -> {
appendError(diagnostic.reason)
}
}
} else {
appendError(diagnostic.reason)
}
}
private fun StringBuilder.renderNullability(type: ConeKotlinType) {
if (type.nullability == ConeNullability.NULLABLE) {
append("?")
}
}
fun shouldRenderAsPrettyFunctionType(type: ConeKotlinType): Boolean {
return type.type.isBuiltinFunctionalType(session) && type.typeArguments.none { it.kind == ProjectionKind.STAR }
}
private fun differsOnlyInNullability(lower: String, upper: String) =
lower == upper.replace("?", "") || upper.endsWith("?") && ("$lower?") == upper || "($lower)?" == upper
private fun renderFlexibleType(lowerRendered: String, upperRendered: String): String {
if (differsOnlyInNullability(lowerRendered, upperRendered)) {
if (upperRendered.startsWith("(")) {
// the case of complex type, e.g. (() -> Unit)?
return "($lowerRendered)!"
}
return "$lowerRendered!"
}
val kotlinCollectionsPrefix = "kotlin.collections."
val mutablePrefix = "Mutable"
// java.util.List<Foo> -> (Mutable)List<Foo!>!
val simpleCollection = replacePrefixes(
lowerRendered,
kotlinCollectionsPrefix + mutablePrefix,
upperRendered,
kotlinCollectionsPrefix,
"$kotlinCollectionsPrefix($mutablePrefix)"
)
if (simpleCollection != null) return simpleCollection
// java.util.Map.Entry<Foo, Bar> -> (Mutable)Map.(Mutable)Entry<Foo!, Bar!>!
val mutableEntry = replacePrefixes(
lowerRendered,
kotlinCollectionsPrefix + "MutableMap.MutableEntry",
upperRendered,
kotlinCollectionsPrefix + "Map.Entry",
"$kotlinCollectionsPrefix(Mutable)Map.(Mutable)Entry"
)
if (mutableEntry != null) return mutableEntry
val kotlinPrefix = "kotlin."
// Foo[] -> Array<(out) Foo!>!
val array = replacePrefixes(
lowerRendered,
kotlinPrefix + "Array<",
upperRendered,
kotlinPrefix + "Array<out ",
kotlinPrefix + "Array<(out) "
)
if (array != null) return array
return "($lowerRendered..$upperRendered)"
}
private fun replacePrefixes(
lowerRendered: String,
lowerPrefix: String,
upperRendered: String,
upperPrefix: String,
foldedPrefix: String
): String? {
if (lowerRendered.startsWith(lowerPrefix) && upperRendered.startsWith(upperPrefix)) {
val lowerWithoutPrefix = lowerRendered.substring(lowerPrefix.length)
val upperWithoutPrefix = upperRendered.substring(upperPrefix.length)
val flexibleCollectionName = foldedPrefix + lowerWithoutPrefix
if (lowerWithoutPrefix == upperWithoutPrefix) return flexibleCollectionName
if (differsOnlyInNullability(lowerWithoutPrefix, upperWithoutPrefix)) {
return "$flexibleCollectionName!"
}
}
return null
}
private fun FirRegularClass.collectForLocal(): List<FirClassLikeDeclaration> {
require(isLocal)
var containingClassLookUp = containingClassForLocal()
val designation = mutableListOf<FirClassLikeDeclaration>(this)
@OptIn(LookupTagInternals::class)
while (containingClassLookUp != null && containingClassLookUp.classId.isLocal) {
val currentClass = containingClassLookUp.toFirRegularClass(moduleData.session) ?: break
designation.add(currentClass)
containingClassLookUp = currentClass.containingClassForLocal()
}
return designation
}
private fun collectDesignationPathForLocal(declaration: FirDeclaration): List<FirDeclaration>? {
@OptIn(LookupTagInternals::class)
val containingClass = when (declaration) {
is FirCallableDeclaration -> declaration.containingClassLookupTag()?.toFirRegularClass(declaration.moduleData.session)
is FirAnonymousObject -> return listOf(declaration)
is FirClassLikeDeclaration -> declaration.let {
if (!declaration.isLocal) return null
(it as? FirRegularClass)?.containingClassForLocal()?.toFirRegularClass(declaration.moduleData.session)
}
else -> error("Invalid declaration ${declaration.renderWithType()}")
} ?: return listOf(declaration)
return if (containingClass.isLocal) {
containingClass.collectForLocal().reversed()
} else null
}
private fun StringBuilder.renderTypeConstructorAndArguments(type: ConeClassLikeType) {
fun renderTypeArguments(typeArguments: Array<out ConeTypeProjection>, range: IntRange) {
if (range.any()) {
typeArguments.slice(range).joinTo(this, ", ", prefix = "<", postfix = ">") {
renderTypeProjection(it)
}
}
}
val classSymbolToRender = type.lookupTag.toSymbol(session)
// To be able to render unresolved types like java.io.Serializable
val classId = classSymbolToRender?.classId ?: type.lookupTag.classId
if (!options.shortQualifiedNames && !classId.isLocal) {
val packageName = classId.packageFqName.asString()
if (packageName.isNotEmpty()) {
append(packageName).append(".")
}
}
if (classSymbolToRender !is FirRegularClassSymbol) {
append(classId.shortClassName)
if (type.typeArguments.any()) {
type.typeArguments.joinTo(this, ", ", prefix = "<", postfix = ">") {
renderTypeProjection(it)
}
}
return
}
val designation = classSymbolToRender.fir.let {
val nonLocalDesignation = it.tryCollectDesignation()
nonLocalDesignation?.toSequence(includeTarget = true)?.toList()
?: collectDesignationPathForLocal(it)
?: emptyList()
}
var typeParametersLeft = type.typeArguments.count()
fun needToRenderTypeParameters(index: Int): Boolean {
if (typeParametersLeft <= 0) return false
return index == designation.lastIndex ||
(designation[index] as? FirRegularClass)?.isInner == true ||
(designation[index + 1] as? FirRegularClass)?.isInner == true
}
val classParentFqName = classId.relativeClassName.parent()
if (!classParentFqName.isRoot && designation.size == 1) {
// This code is added for a case we can't build designation (e.g. nested Java class),
// but still wish to render full class name
append(classParentFqName)
append(".")
}
designation.filterIsInstance<FirRegularClass>().forEachIndexed { index, currentClass ->
if (index != 0) append(".")
append(currentClass.name)
if (needToRenderTypeParameters(index)) {
val typeParametersCount = currentClass.typeParameters.count { it is FirTypeParameter }
val begin = typeParametersLeft - typeParametersCount
val end = typeParametersLeft
check(begin >= 0)
typeParametersLeft -= typeParametersCount
renderTypeArguments(type.typeArguments, begin until end)
}
}
renderNullability(type)
}
private fun renderTypeProjection(typeProjection: ConeTypeProjection): String {
val type = typeProjection.type?.let(::renderType) ?: "???"
return when (typeProjection.kind) {
ProjectionKind.STAR -> "*"
ProjectionKind.IN -> "in $type"
ProjectionKind.OUT -> "out $type"
ProjectionKind.INVARIANT -> type
}
}
private fun StringBuilder.renderFunctionType(type: ConeClassLikeType) {
val lengthBefore = length
val hasAnnotations = length != lengthBefore
val isSuspend = type.isSuspendFunctionType(session)
val isNullable = type.isMarkedNullable
val receiverType = type.receiverType(session)
val needParenthesis = isNullable || (hasAnnotations && receiverType != null)
if (needParenthesis) {
if (isSuspend) {
insert(lengthBefore, '(')
} else {
if (hasAnnotations) {
check(last() == ' ')
if (get(lastIndex - 1) != ')') {
// last annotation rendered without parenthesis - need to add them otherwise parsing will be incorrect
insert(lastIndex, "()")
}
}
append("(")
}
}
if (isSuspend) {
append("suspend")
append(" ")
}
if (receiverType != null) {
val surroundReceiver = shouldRenderAsPrettyFunctionType(receiverType) &&
!receiverType.isMarkedNullable ||
receiverType.isSuspendFunctionType(session)
if (surroundReceiver) {
append("(")
}
append(renderType(receiverType))
if (surroundReceiver) {
append(")")
}
append(".")
}
append("(")
val notNullParametersType = type
.valueParameterTypesIncludingReceiver(session)
.applyIf(receiverType != null) { drop(1) }
notNullParametersType.forEachIndexed { index, typeProjection ->
if (index != 0) append(", ")
append(renderTypeProjection(typeProjection))
}
append(") -> ")
val returnType = type.returnType(session)
append(renderType(returnType))
if (needParenthesis) append(")")
renderNullability(type)
}
}
@@ -1,69 +0,0 @@
/*
* Copyright 2010-2021 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.analysis.api.fir.renderer
import org.jetbrains.kotlin.analysis.api.fir.annotations.mapAnnotationParameters
import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirCompileTimeConstantEvaluator
import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirAnnotationValueConverter
import org.jetbrains.kotlin.analysis.api.annotations.KtUnsupportedAnnotationValue
import org.jetbrains.kotlin.analysis.api.annotations.renderAsSourceCode
import org.jetbrains.kotlin.analysis.api.components.KtConstantEvaluationMode
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import kotlin.text.Appendable
internal fun Appendable.renderAnnotations(
coneTypeIdeRenderer: ConeTypeIdeRenderer,
annotations: List<FirAnnotation>,
session: FirSession,
isSingleLineAnnotations: Boolean,
) {
val separator = if (isSingleLineAnnotations) " " else "\n"
for (annotation in annotations) {
if (!annotation.isParameterName()) {
append(renderAnnotation(annotation, coneTypeIdeRenderer, session))
append(separator)
}
}
}
private fun FirAnnotation.isParameterName(): Boolean {
return toAnnotationClassId()?.asSingleFqName() == StandardNames.FqNames.parameterName
}
private fun renderAnnotation(annotation: FirAnnotation, coneTypeIdeRenderer: ConeTypeIdeRenderer, session: FirSession): String {
return buildString {
append('@')
val resolvedTypeRef = annotation.typeRef as? FirResolvedTypeRef
check(resolvedTypeRef != null)
append(coneTypeIdeRenderer.renderType(resolvedTypeRef.type))
val arguments = renderAndSortAnnotationArguments(annotation, session)
if (arguments.isNotEmpty()) {
arguments.joinTo(this, ", ", "(", ")")
}
}
}
private fun renderAndSortAnnotationArguments(descriptor: FirAnnotation, session: FirSession): List<String> {
val argumentList = mapAnnotationParameters(descriptor, session).entries.map { (name, value) ->
"$name = ${renderConstant(value, session)}"
}
return argumentList.sorted()
}
private fun renderConstant(value: FirExpression, useSiteSession: FirSession): String {
val evaluated = FirCompileTimeConstantEvaluator.evaluate(value, KtConstantEvaluationMode.CONSTANT_EXPRESSION_EVALUATION)
val constantValue = FirAnnotationValueConverter.toConstantValue(evaluated ?: value, useSiteSession)
?: KtUnsupportedAnnotationValue
return constantValue.renderAsSourceCode()
}
@@ -1,371 +0,0 @@
/*
* Copyright 2010-2021 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.analysis.api.fir.renderer
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.analysis.api.components.KtDeclarationRendererOptions
import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter
import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.PsiSourceNavigator.getRawName
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.hasBody
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.extensions.generatedMembers
import org.jetbrains.kotlin.fir.extensions.generatedNestedClassifiers
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.renderer.render
internal class FirIdeRenderer private constructor(
options: KtDeclarationRendererOptions,
session: FirSession,
) : FirIdeRendererBase(options, session) {
fun PrettyPrinter.renderMemberDeclaration(declaration: FirDeclaration) {
when (declaration) {
is FirAnonymousObject -> renderAnonymousObject(declaration)
is FirRegularClass -> renderRegularClass(declaration)
is FirTypeAlias -> renderTypeAlias(declaration)
is FirConstructor -> renderConstructor(declaration)
is FirPropertyAccessor -> renderPropertyAccessor(declaration)
is FirSimpleFunction -> renderSimpleFunction(declaration)
is FirBackingField -> renderBackingField()
is FirEnumEntry -> renderEnumEntry(declaration)
is FirProperty -> renderPropertyOrField(declaration)
is FirValueParameter -> renderValueParameter(declaration)
is FirField -> renderPropertyOrField(declaration)
is FirErrorFunction -> error("FirErrorFunction should not be rendered")
is FirErrorProperty -> error("FirErrorProperty should not be rendered")
is FirAnonymousInitializer -> error("FirAnonymousInitializer should not be rendered")
is FirFile -> error("FirFile should not be rendered")
is FirTypeParameter -> renderTypeParameter(declaration)
is FirAnonymousFunction -> TODO()
}
}
private fun PrettyPrinter.renderBackingField() {
append("field")
}
private fun PrettyPrinter.renderPropertyOrField(variable: FirVariable) {
check(variable is FirProperty || variable is FirField) {
"Required either FirProperty or FirField but was ${variable::class.simpleName}"
}
renderAnnotationsAndModifiers(variable)
renderValVarPrefix(variable)
renderTypeParameters(variable)
renderReceiver(variable)
renderName(variable)
append(": ")
renderType(variable.returnTypeRef, approximate = options.approximateTypes)
renderWhereSuffix(variable)
fun FirPropertyAccessor?.needToRender() = this != null && (annotations.isNotEmpty() || visibility != variable.visibility)
val needToRenderAccessors = options.renderClassMembers &&
(variable.getter.needToRender() || (variable.isVar && variable.setter.needToRender()))
if (needToRenderAccessors) {
withIndent {
variable.getter?.let { getter ->
if (getter.needToRender()) {
appendLine()
renderPropertyAccessor(getter)
}
}
variable.setter?.let { setter ->
if (setter.needToRender()) {
appendLine()
renderPropertyAccessor(setter)
}
}
}
}
}
private fun PrettyPrinter.renderPropertyAccessor(propertyAccessor: FirPropertyAccessor) {
renderAnnotationsAndModifiers(propertyAccessor)
append(if (propertyAccessor.isGetter) "get" else "set")
if (propertyAccessor.isSetter) {
append("(")
val valueParameter = propertyAccessor.valueParameters.first()
renderAnnotations(valueParameter)
append("value: ")
renderType(valueParameter.returnTypeRef)
append(")")
} else {
append("()")
}
renderFunctionBody(propertyAccessor)
}
@Suppress("unused", "UNUSED_PARAMETER")
private fun PrettyPrinter.renderFunctionBody(function: FirFunction) {
// TODO implement with some settings
}
private fun PrettyPrinter.renderSimpleFunction(simpleFunction: FirSimpleFunction) {
renderAnnotationsAndModifiers(simpleFunction)
append("fun ")
renderTypeParameters(simpleFunction)
renderReceiver(simpleFunction)
renderName(simpleFunction)
renderValueParameters(simpleFunction)
val returnType = simpleFunction.returnTypeRef
if (options.renderUnitReturnType || !returnType.isUnit) {
append(": ")
renderType(returnType, approximate = options.approximateTypes)
append(' ')
}
renderWhereSuffix(simpleFunction)
renderFunctionBody(simpleFunction)
}
private fun PrettyPrinter.renderAnonymousObject(anonymousObject: FirAnonymousObject) {
renderAnnotationsAndModifiers(anonymousObject)
append("object ")
renderSuperTypes(anonymousObject)
renderClassBody(anonymousObject)
}
private fun PrettyPrinter.renderClassBody(firClass: FirClass) {
if (!options.renderClassMembers) return
if (firClass.declarations.isEmpty()) return
val allDeclarations = buildList {
firClass.declarations.filterNotTo(this) { member ->
member.isDefaultPrimaryConstructor()
|| member.isDefaultEnumEntryMember(firClass)
|| member is FirConstructor && firClass.classKind == ClassKind.OBJECT
}
addAll(firClass.generatedNestedClassifiers(useSiteSession))
addAll(firClass.generatedMembers(useSiteSession))
}.filterIsInstance<FirMemberDeclaration>()
if (allDeclarations.isEmpty()) return
val (enumEntries, nonEnumEntries) = allDeclarations.partition { it is FirEnumEntry }
withIndentInBraces {
printCollection(sortDeclarations(enumEntries), separator = ",\n") { declaration ->
renderMemberDeclaration(declaration)
}
if (enumEntries.isNotEmpty() && nonEnumEntries.isNotEmpty()) {
appendLine(";\n")
}
printCollection(sortDeclarations(nonEnumEntries), separator = "\n\n") { declaration ->
renderMemberDeclaration(declaration)
}
}
}
private fun PrettyPrinter.renderConstructor(constructor: FirConstructor) {
renderAnnotationsAndModifiers(constructor)
append("constructor")
renderValueParameters(constructor)
renderFunctionBody(constructor)
}
private fun PrettyPrinter.renderRegularClass(regularClass: FirRegularClass) {
renderAnnotationsAndModifiers(regularClass)
renderClassifierKind(regularClass)
renderClassName(regularClass)
renderTypeParameters(regularClass)
printCharIfNotThere(' ')
renderSuperTypes(regularClass)
renderWhereSuffix(regularClass)
renderClassBody(regularClass)
}
private fun PrettyPrinter.renderClassName(regularClass: FirRegularClass) {
if (!regularClass.isCompanion) {
renderName(regularClass)
} else {
if (regularClass.name != SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) {
append(regularClass.name.render())
}
}
}
private fun PrettyPrinter.renderTypeAlias(typeAlias: FirTypeAlias) {
renderAnnotationsAndModifiers(typeAlias)
append("typealias ")
renderName(typeAlias)
renderTypeParameters(typeAlias)
printCharIfNotThere(' ')
append("= ")
renderType(typeAlias.expandedTypeRef)
}
private fun PrettyPrinter.renderEnumEntry(enumEntry: FirEnumEntry) {
renderName(enumEntry)
}
private fun PrettyPrinter.renderTypeParameter(typeParameter: FirTypeParameter) {
renderIf(typeParameter.isReified, "reified")
val variance = typeParameter.variance.label
renderIf(variance.isNotEmpty(), variance)
renderAnnotations(typeParameter)
renderName(typeParameter)
val upperBoundsCount = typeParameter.bounds.size
if (upperBoundsCount >= 1) {
val upperBound = typeParameter.bounds.first()
if (!upperBound.isNullableAny) {
append(" : ")
renderType(upperBound)
}
}
}
private fun PrettyPrinter.renderTypeParameters(declaration: FirMemberDeclaration) {
val typeParameters = declaration.typeParameters.filterIsInstance<FirTypeParameter>()
if (typeParameters.isNotEmpty()) {
append("<")
printCollection(typeParameters) {
renderTypeParameter(it)
}
append("> ")
}
}
private fun PrettyPrinter.renderReceiver(firCallableDeclaration: FirCallableDeclaration) {
val receiverType = firCallableDeclaration.receiverTypeRef
if (receiverType != null) {
if (options.renderDeclarationHeader) {
renderAnnotations(firCallableDeclaration)
}
val needBrackets =
typeIdeRenderer.shouldRenderAsPrettyFunctionType(receiverType.coneType) && receiverType.isMarkedNullable == true
if (needBrackets) append('(')
renderType(receiverType)
if (needBrackets) append(')')
append(".")
}
}
private fun PrettyPrinter.renderWhereSuffix(declaration: FirTypeParameterRefsOwner) {
val upperBoundStrings = ArrayList<String>(0)
for (typeParameter in declaration.typeParameters) {
if (typeParameter !is FirTypeParameter) continue
typeParameter.symbol.resolvedBounds
.drop(1) // first parameter is rendered by renderTypeParameter
.mapTo(upperBoundStrings) { typeParameter.name.render() + " : " + renderTypeToString(it.coneType) }
}
if (upperBoundStrings.isNotEmpty()) {
append("where ")
upperBoundStrings.joinTo(this, ", ")
append(' ')
}
}
private fun PrettyPrinter.renderValueParameters(function: FirFunction) {
printCollection(function.valueParameters, prefix = "(", postfix = ")") {
renderValueParameter(it)
}
}
private fun PrettyPrinter.renderValueParameter(valueParameter: FirValueParameter) {
if (options.renderDeclarationHeader) {
renderAnnotations(valueParameter)
}
renderIf(valueParameter.isCrossinline, "crossinline")
renderIf(valueParameter.isNoinline, "noinline")
renderVariable(valueParameter)
if (options.renderDefaultParameterValue) {
val withDefaultValue = valueParameter.defaultValue != null //TODO check if default value is inherited
if (withDefaultValue) {
append(" = ...")
}
}
}
private fun PrettyPrinter.renderValVarPrefix(variable: FirVariable, isInPrimaryConstructor: Boolean = false) {
if (!isInPrimaryConstructor || variable !is FirValueParameter) {
append(if (variable.isVar) "var" else "val")
append(' ')
}
}
private fun PrettyPrinter.renderVariable(variable: FirVariable) {
val typeToRender = variable.returnTypeRef.coneType
val isVarArg = (variable as? FirValueParameter)?.isVararg ?: false
renderIf(isVarArg, "vararg")
renderName(variable)
append(": ")
if (isVarArg) {
renderType(typeToRender.arrayElementType() ?: typeToRender)
} else {
renderType(typeToRender)
}
}
fun sortDeclarations(declarations: List<FirMemberDeclaration>): List<FirMemberDeclaration> {
if (!options.sortNestedDeclarations) return declarations
fun getDeclarationKind(declaration: FirDeclaration): Int = when (declaration) {
is FirEnumEntry -> 0
is FirConstructor -> if (declaration.isPrimary) 1 else 2
is FirProperty -> 3
is FirFunction -> 4
else -> 5
}
return declarations.sortedWith(Comparator { left, right ->
val kindResult = getDeclarationKind(left) - getDeclarationKind(right)
if (kindResult != 0) {
return@Comparator kindResult
}
val nameResult = (left.getRawName() ?: "").compareTo(right.getRawName() ?: "")
if (nameResult != 0) {
return@Comparator nameResult
}
val leftString = prettyPrint { renderMemberDeclaration(left) }
val rightString = prettyPrint { renderMemberDeclaration(right) }
return@Comparator leftString.compareTo(rightString)
})
}
companion object {
fun render(
firDeclaration: FirDeclaration,
options: KtDeclarationRendererOptions,
session: FirSession
): String {
val renderer = FirIdeRenderer(options, session)
return prettyPrint {
with(renderer) { renderMemberDeclaration(firDeclaration) }
}.trim { it.isWhitespace() }
}
}
}
private fun FirDeclaration.isDefaultEnumEntryMember(firClass: FirClass): Boolean {
if (firClass.classKind != ClassKind.ENUM_CLASS) return false
if (this is FirConstructor) return isPrimary && valueParameters.isEmpty()
return source?.kind == KtFakeSourceElementKind.EnumGeneratedDeclaration
}
private fun FirDeclaration.isDefaultPrimaryConstructor() =
this is FirConstructor &&
isPrimary &&
valueParameters.isEmpty() &&
!hasBody &&
visibility == Visibilities.DEFAULT_VISIBILITY
@@ -1,179 +0,0 @@
/*
* Copyright 2010-2021 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.analysis.api.fir.renderer
import org.jetbrains.kotlin.analysis.api.components.KtDeclarationRendererOptions
import org.jetbrains.kotlin.analysis.api.components.RendererModifier
import org.jetbrains.kotlin.analysis.api.fir.types.PublicTypeApproximator
import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.classKind
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import org.jetbrains.kotlin.utils.addToStdlib.applyIf
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
internal abstract class FirIdeRendererBase(
protected val options: KtDeclarationRendererOptions,
protected val useSiteSession: FirSession,
) {
protected val typeIdeRenderer: ConeTypeIdeRenderer = ConeTypeIdeRenderer(useSiteSession, options.typeRendererOptions)
protected fun PrettyPrinter.renderAnnotations(declaration: FirDeclaration) {
if (RendererModifier.ANNOTATIONS in options.modifiers) {
val isSingleLineAnnotations = declaration is FirValueParameter || declaration is FirTypeParameter
renderAnnotations(typeIdeRenderer, declaration.annotations, useSiteSession, isSingleLineAnnotations)
}
}
protected fun renderTypeToString(type: ConeTypeProjection): String {
return typeIdeRenderer.renderType(type)
}
protected fun PrettyPrinter.renderType(type: ConeTypeProjection) {
append(renderTypeToString(type))
}
protected fun PrettyPrinter.renderType(firRef: FirTypeRef, approximate: Boolean = false) {
val coneType = firRef.coneType
val approximatedIfNeeded = approximate.ifTrue {
PublicTypeApproximator.approximateTypeToPublicDenotable(coneType, useSiteSession, approximateLocalTypes = true)
} ?: coneType
renderType(approximatedIfNeeded)
}
protected fun PrettyPrinter.renderName(declaration: FirDeclaration) {
if (declaration is FirAnonymousObject) {
append("<no name provided>")
return
}
val name = when (declaration) {
is FirRegularClass -> declaration.name
is FirSimpleFunction -> declaration.name
is FirProperty -> declaration.name
is FirValueParameter -> declaration.name
is FirTypeParameter -> declaration.name
is FirTypeAlias -> declaration.name
is FirEnumEntry -> declaration.name
is FirField -> declaration.name
else -> TODO("Unexpected declaration ${declaration::class.qualifiedName}")
}
append(name.render())
}
private fun PrettyPrinter.renderVisibility(declaration: FirMemberDeclaration) {
if (declaration is FirConstructor && declaration.containingClassLookupTag()?.toFirRegularClassSymbol(useSiteSession)?.isEnumClass == true) {
return
}
val visibility = declaration.visibility
if (RendererModifier.VISIBILITY !in options.modifiers) return
val currentVisibility = when (visibility) {
Visibilities.Local -> Visibilities.Public
Visibilities.PrivateToThis -> Visibilities.Public
Visibilities.InvisibleFake -> Visibilities.Public
Visibilities.Inherited -> Visibilities.Public
Visibilities.Unknown -> Visibilities.Public
else -> visibility
}.applyIf(options.normalizedVisibilities) {
normalize()
}
if (currentVisibility == Visibilities.DEFAULT_VISIBILITY) return
append(currentVisibility.internalDisplayName)
append(' ')
}
private fun PrettyPrinter.renderModality(memberDeclaration: FirMemberDeclaration) {
val modality = memberDeclaration.modality ?: return
if ((memberDeclaration as? FirRegularClass)?.isInterface == true) return
if (modality == Modality.FINAL) return
if (memberDeclaration.getContainingClassSymbol(useSiteSession)?.classKind == ClassKind.INTERFACE) return
if (memberDeclaration.isOverride) return
renderIf(RendererModifier.MODALITY in options.modifiers, modality.name.toLowerCaseAsciiOnly())
}
private fun PrettyPrinter.renderOverride(callableMember: FirMemberDeclaration) {
if (RendererModifier.OVERRIDE !in options.modifiers) return
renderIf(callableMember.isOverride || options.forceRenderingOverrideModifier, "override")
}
protected fun PrettyPrinter.renderIf(value: Boolean, text: String) {
if (value) {
append(text)
append(" ")
}
}
protected fun PrettyPrinter.renderAnnotationsAndModifiers(declaration: FirMemberDeclaration) {
if (!options.renderDeclarationHeader) return
renderAnnotations(declaration)
renderVisibility(declaration)
renderOverride(declaration)
renderModality(declaration)
renderIf(declaration.isExternal, "external")
renderIf(RendererModifier.EXPECT in options.modifiers && declaration.isExpect, "expect")
renderIf(RendererModifier.ACTUAL in options.modifiers && declaration.isActual, "actual")
renderIf(declaration.isTailRec, "tailrec")
renderIf(declaration.isConst, "const")
renderIf(declaration.isInner, "inner")
renderIf(declaration.isLateInit, "lateinit")
renderIf(declaration.isSuspend, "suspend")
renderIf(declaration.isInline, "inline")
renderIf(declaration.isInfix, "infix")
renderIf(RendererModifier.OPERATOR in options.modifiers && declaration.isOperator, "operator")
}
protected fun PrettyPrinter.renderClassifierKind(classifier: FirDeclaration) {
when (classifier) {
is FirTypeAlias -> append("typealias")
is FirRegularClass ->
append(if (classifier.isCompanion) "companion object" else classifier.classKind.codeRepresentation)
is FirAnonymousObject -> append("object")
is FirEnumEntry -> append("enum entry")
else ->
throw AssertionError("Unexpected classifier: $classifier")
}
append(' ')
}
protected fun PrettyPrinter.renderSuperTypes(klass: FirClass) {
if (klass.defaultType().isNothing) return
val supertypes = klass.superTypeRefs.asSequence()
.applyIf(klass.classKind == ClassKind.ENUM_CLASS) {
filterNot {
it.coneType.classId == StandardClassIds.Enum
}
}.applyIf(klass.classKind == ClassKind.ANNOTATION_CLASS) {
filterNot {
it.coneType.classId == StandardClassIds.Annotation
}
}.toList()
if (supertypes.isEmpty() || klass.superTypeRefs.singleOrNull()?.isAny == true) return
append(": ")
printCollection(supertypes) {
renderType(it)
}
append(' ')
}
}