Analysis API: rework declaration renderer

- refactor code to use PrettyPrinter instead of ad-hoc one
- do not print nested local declarations as this does not seem to have use-cases
This commit is contained in:
Ilya Kirillov
2021-11-29 15:10:40 +01:00
parent 908a47810b
commit 19dec92247
18 changed files with 533 additions and 815 deletions
@@ -20,6 +20,7 @@ dependencies {
api(project(":compiler:light-classes"))
api(intellijCoreDep())
implementation(project(":analysis:analysis-api-providers"))
implementation(project(":analysis:analysis-internal-utils"))
testApi(projectTests(":analysis:low-level-api-fir"))
testApi(projectTests(":compiler:tests-common"))
@@ -16,9 +16,11 @@ import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirSymbol
import org.jetbrains.kotlin.analysis.api.fir.types.KtFirType
import org.jetbrains.kotlin.analysis.api.symbols.KtPackageSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtPossibleMemberSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind
import org.jetbrains.kotlin.analysis.api.tokens.ValidityToken
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
internal class KtFirSymbolDeclarationRendererProvider(
@@ -31,33 +33,10 @@ internal class KtFirSymbolDeclarationRendererProvider(
return ConeTypeIdeRenderer(analysisSession.firResolveState.rootModuleSession, options).renderType(type.coneType)
}
override fun render(symbol: KtSymbol, options: KtDeclarationRendererOptions): String {
return when (symbol) {
is KtPackageSymbol -> {
"package ${symbol.fqName.asString()}"
}
is KtFirSymbol<*> -> {
val containingSymbol = with(analysisSession) {
(symbol as? KtSymbolWithKind)?.getContainingSymbol()
}
check(containingSymbol is KtFirSymbol<*>?)
symbol.firRef.withFir(FirResolvePhase.BODY_RESOLVE) { fir ->
val containingFir = containingSymbol?.firRef?.withFirUnsafe { it }
FirIdeRenderer.render(fir, containingFir, options, fir.moduleData.session)
}
}
is KtFirReceiverParameterSymbol -> {
val containingDeclaration = with(analysisSession) {
symbol.getContainingSymbol() ?: error("expects receiver parameter symbol to return the containing declaration")
}
"extension receiver of " + render(containingDeclaration, options)
}
else -> {
error("Unexpected Fir Symbol ${symbol::class.simpleName}")
}
override fun renderMember(symbol: KtPossibleMemberSymbol, options: KtDeclarationRendererOptions): String {
require(symbol is KtFirSymbol<*>)
return symbol.firRef.withFir(FirResolvePhase.BODY_RESOLVE) { fir ->
FirIdeRenderer.render(fir as FirMemberDeclaration, options, fir.moduleData.session)
}
}
}
@@ -41,7 +41,7 @@ internal class ConeTypeIdeRenderer(
private fun StringBuilder.renderAnnotationList(type: ConeKotlinType) {
if (options.renderTypeAnnotations) {
renderAnnotations(this@ConeTypeIdeRenderer, type.customAnnotations, session)
renderAnnotations(this@ConeTypeIdeRenderer, type.customAnnotations, session, isSingleLineAnnotations = true)
}
}
@@ -10,26 +10,31 @@ import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirCompileTimeConstantEval
import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirAnnotationValueConverter
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationValueRenderer
import org.jetbrains.kotlin.analysis.api.annotations.KtUnsupportedAnnotationValue
import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter
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 StringBuilder.renderAnnotations(
internal fun Appendable.renderAnnotations(
coneTypeIdeRenderer: ConeTypeIdeRenderer,
annotations: List<FirAnnotation>,
session: FirSession
session: FirSession,
isSingleLineAnnotations: Boolean,
) {
val separator = if (isSingleLineAnnotations) " " else "\n"
for (annotation in annotations) {
if (!annotation.isParameterName()) {
append(renderAnnotation(annotation, coneTypeIdeRenderer, session))
append(" ")
append(separator)
}
}
}
private fun FirAnnotation.isParameterName(): Boolean {
return toAnnotationClassId()?.asSingleFqName() == StandardNames.FqNames.parameterName
}
@@ -6,575 +6,225 @@
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.analysis.utils.printer.prettyPrint
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.FirElement
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.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirBlock
import org.jetbrains.kotlin.fir.resolve.defaultType
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.fir.visitors.FirVisitor
import org.jetbrains.kotlin.name.SpecialNames
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 class FirIdeRenderer private constructor(
private var containingDeclaration: FirDeclaration?,
private val options: KtDeclarationRendererOptions,
private val session: FirSession
) : FirVisitor<Unit, StringBuilder>() {
private val typeIdeRenderer: ConeTypeIdeRenderer = ConeTypeIdeRenderer(session, options.typeRendererOptions)
private fun StringBuilder.renderAnnotations(annotated: FirDeclaration) {
if (RendererModifier.ANNOTATIONS in options.modifiers) {
renderAnnotations(typeIdeRenderer, annotated.annotations, session)
options: KtDeclarationRendererOptions,
session: FirSession,
) : FirIdeRendererBase(options, session) {
fun PrettyPrinter.renderMemberDeclaration(declaration: FirMemberDeclaration) {
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 FirEnumEntry -> renderEnumEntry(declaration)
is FirProperty -> renderProperty(declaration)
is FirValueParameter -> renderValueParameter(declaration)
is FirField -> TODO()
is FirAnonymousFunction -> TODO()
is FirErrorFunction -> Unit
is FirErrorProperty -> Unit
is FirBackingField -> Unit
}
}
private fun renderType(type: ConeTypeProjection): String =
typeIdeRenderer.renderType(type)
private fun renderType(firRef: FirTypeRef, approximate: Boolean = false): String {
require(firRef is FirResolvedTypeRef)
val approximatedIfNeeded = approximate.ifTrue {
PublicTypeApproximator.approximateTypeToPublicDenotable(firRef.coneType, session, approximateLocalTypes = true)
} ?: firRef.coneType
return renderType(approximatedIfNeeded)
}
private fun StringBuilder.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
else -> TODO("Unexpected declaration ${declaration::class.qualifiedName}")
}
append(name.render())
}
private fun StringBuilder.renderCompanionObjectName(firClass: FirRegularClass) {
if (firClass.name != SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) {
tabRightBySpace()
append(firClass.name.render())
}
}
private fun StringBuilder.renderVisibility(visibility: 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 StringBuilder.renderModality(modality: Modality, defaultModality: Modality) {
if (modality == defaultModality) return
renderModifier(RendererModifier.MODALITY in options.modifiers, modality.name.toLowerCaseAsciiOnly())
}
private fun FirMemberDeclaration.implicitModalityWithoutExtensions(containingDeclaration: FirDeclaration?): Modality {
if (this is FirRegularClass) {
return if (classKind == ClassKind.INTERFACE) Modality.ABSTRACT else Modality.FINAL
}
val containingFirClass = containingDeclaration as? FirRegularClass ?: return Modality.FINAL
if (this !is FirCallableDeclaration) return Modality.FINAL
if (isOverride) {
if (containingFirClass.modality != Modality.FINAL) return Modality.OPEN
}
return if (containingFirClass.classKind == ClassKind.INTERFACE && this.visibility != Visibilities.Private) {
if (this.modality == Modality.ABSTRACT) Modality.ABSTRACT else Modality.OPEN
} else
Modality.FINAL
}
private fun StringBuilder.renderModalityForCallable(
callable: FirCallableDeclaration,
containingDeclaration: FirDeclaration?
) {
val modality = callable.modality ?: return
val isTopLevel = containingDeclaration == null
if (!isTopLevel || modality != Modality.FINAL) {
if (callable.isOverride) return
renderModality(modality, callable.implicitModalityWithoutExtensions(containingDeclaration))
}
}
private fun StringBuilder.renderOverride(callableMember: FirCallableDeclaration) {
if (RendererModifier.OVERRIDE !in options.modifiers) return
renderModifier(callableMember.isOverride || options.forceRenderingOverrideModifier, "override")
}
private fun StringBuilder.renderModifier(value: Boolean, modifier: String) {
if (value) {
append(modifier)
append(" ")
}
}
private fun StringBuilder.renderMemberModifiers(declaration: FirMemberDeclaration) {
renderModifier(declaration.isExternal, "external")
renderModifier(RendererModifier.EXPECT in options.modifiers && declaration.isExpect, "expect")
renderModifier(RendererModifier.ACTUAL in options.modifiers && declaration.isActual, "actual")
}
private fun StringBuilder.renderAdditionalModifiers(firMember: FirMemberDeclaration) {
val isOperator =
firMember.isOperator//TODO make similar to functionDescriptor.overriddenDescriptors.none { it.isOperator }
val isInfix =
firMember.isInfix//TODO make similar to functionDescriptor.overriddenDescriptors.none { it.isInfix }
renderModifier(firMember.isTailRec, "tailrec")
renderSuspendModifier(firMember)
renderModifier(firMember.isInline, "inline")
renderModifier(isInfix, "infix")
renderModifier(RendererModifier.OPERATOR in options.modifiers && isOperator, "operator")
}
private fun StringBuilder.renderSuspendModifier(functionDescriptor: FirMemberDeclaration) {
renderModifier(functionDescriptor.isSuspend, "suspend")
}
override fun visitValueParameter(valueParameter: FirValueParameter, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
append("value-parameter").append(" ")
renderValueParameter(valueParameter)
}
}
override fun visitProperty(property: FirProperty, data: StringBuilder) = with(data) {
appendLine()
appendTabs()
if (options.renderDeclarationHeader) {
renderAnnotations(property)
renderVisibility(property.visibility)
renderModifier(RendererModifier.CONST in options.modifiers && property.isConst, "const")
renderMemberModifiers(property)
renderModalityForCallable(property, containingDeclaration)
renderOverride(property)
renderModifier(RendererModifier.LATEINIT in options.modifiers && property.isLateInit, "lateinit")
renderValVarPrefix(property)
renderTypeParameters(property.typeParameters, true)
}
private fun PrettyPrinter.renderProperty(property: FirProperty) {
renderAnnotationsAndModifiers(property)
renderValVarPrefix(property)
renderTypeParameters(property)
renderReceiver(property)
renderName(property)
append(": ").append(renderType(property.returnTypeRef, approximate = options.approximateTypes))
append(": ")
renderType(property.returnTypeRef, approximate = options.approximateTypes)
renderWhereSuffix(property.typeParameters)
renderWhereSuffix(property)
fun FirPropertyAccessor?.needToRender() = this != null && (hasBody || visibility != property.visibility)
val needToRenderAccessors = options.renderContainingDeclarations &&
fun FirPropertyAccessor?.needToRender() = this != null && (annotations.isNotEmpty() || visibility != property.visibility)
val needToRenderAccessors = options.renderClassMembers &&
(property.getter.needToRender() || (property.isVar && property.setter.needToRender()))
fun FirPropertyAccessor?.render(isGetterByDefault: Boolean) {
if (this == null) {
appendLine()
appendTabs()
append(if (isGetterByDefault) "get" else "set")
} else {
visitPropertyAccessor(this, data)
}
}
if (needToRenderAccessors) {
underBlockDeclaration(property, withBrackets = false) {
property.getter.render(isGetterByDefault = true)
if (property.isVar) property.setter.render(isGetterByDefault = false)
}
}
}
override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: StringBuilder) {
require(containingDeclaration is FirProperty) { "Invalid containing declaration" }
with(data) {
appendLine()
appendTabs()
if (options.renderDeclarationHeader) {
renderAnnotations(propertyAccessor)
renderVisibility(propertyAccessor.visibility)
renderModalityForCallable(propertyAccessor, containingDeclaration)
renderMemberModifiers(propertyAccessor)
renderAdditionalModifiers(propertyAccessor)
}
append(if (propertyAccessor.isGetter) "get" else "set")
if (propertyAccessor.isSetter) {
append("(value: ")
val renderedType = propertyAccessor.valueParameters.singleOrNull()?.returnTypeRef?.let { renderType(it) }
if (renderedType != null) append(renderedType) else append(ConeTypeIdeRenderer.ERROR_TYPE_TEXT)
append(")")
} else {
append("()")
}
}
if (options.renderContainingDeclarations) {
propertyAccessor.body?.let {
underBlockDeclaration(propertyAccessor, it, data)
}
}
}
override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
if (options.renderDeclarationHeader) {
renderAnnotations(simpleFunction)
renderVisibility(simpleFunction.visibility)
renderModalityForCallable(simpleFunction, containingDeclaration)
renderMemberModifiers(simpleFunction)
renderOverride(simpleFunction)
renderAdditionalModifiers(simpleFunction)
append("fun ")
renderTypeParameters(simpleFunction.typeParameters, true)
}
renderReceiver(simpleFunction)
renderName(simpleFunction)
renderValueParameters(simpleFunction.valueParameters)
val returnType = simpleFunction.returnTypeRef
if (options.renderUnitReturnType || (!returnType.isUnit)) {
append(": ")
append(renderType(returnType, approximate = options.approximateTypes))
}
renderWhereSuffix(simpleFunction.typeParameters)
}
if (options.renderContainingDeclarations) {
simpleFunction.body?.let {
underBlockDeclaration(simpleFunction, it, data)
}
}
}
override fun visitAnonymousObject(anonymousObject: FirAnonymousObject, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
if (options.renderDeclarationHeader) {
renderAnnotations(anonymousObject)
}
append(getClassifierKindPrefix(anonymousObject))
renderSuperTypes(anonymousObject)
}
if (options.renderContainingDeclarations) {
data.underBlockDeclaration(anonymousObject) {
anonymousObject.declarations.forEach {
it.accept(this, data)
}
}
}
}
override fun visitConstructor(constructor: FirConstructor, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
val containingClass = containingDeclaration
check(containingClass is FirDeclaration && (containingClass is FirClass || containingClass is FirEnumEntry)) {
"Invalid renderer containing declaration for constructor"
}
if (options.renderDeclarationHeader) {
renderAnnotations(constructor)
}
append("constructor")
renderValueParameters(constructor.valueParameters)
}
if (options.renderContainingDeclarations) {
constructor.body?.let {
underBlockDeclaration(constructor, it, data)
}
}
}
override fun visitTypeParameter(typeParameter: FirTypeParameter, data: StringBuilder) {
data.renderTypeParameter(typeParameter, true)
}
override fun visitRegularClass(regularClass: FirRegularClass, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
if (options.renderDeclarationHeader) {
renderAnnotations(regularClass)
if (regularClass.classKind != ClassKind.ENUM_ENTRY) {
renderVisibility(regularClass.visibility)
}
val haveNotModality = regularClass.classKind == ClassKind.INTERFACE && regularClass.modality == Modality.ABSTRACT ||
regularClass.classKind.isSingleton && regularClass.modality == Modality.FINAL
if (!haveNotModality) {
regularClass.modality?.let {
renderModality(it, regularClass.implicitModalityWithoutExtensions(containingDeclaration))
withIndent {
property.getter?.let { getter ->
if (getter.needToRender()) {
appendLine()
renderPropertyAccessor(getter)
}
}
renderMemberModifiers(regularClass)
renderModifier(RendererModifier.INNER in options.modifiers && regularClass.isInner, "inner")
renderModifier(RendererModifier.DATA in options.modifiers && regularClass.isData, "data")
renderModifier(RendererModifier.INLINE in options.modifiers && regularClass.isInline, "inline")
//TODO renderModifier(data, RendererModifier.VALUE in modifiers && regularClass.isValue, "value")
renderModifier(RendererModifier.FUN in options.modifiers && regularClass.isFun, "fun")
append(getClassifierKindPrefix(regularClass))
}
if (!regularClass.isCompanion) {
tabRightBySpace()
renderName(regularClass)
} else {
renderCompanionObjectName(regularClass)
}
if (regularClass.classKind == ClassKind.ENUM_ENTRY) return
val typeParameters = regularClass.typeParameters.filterIsInstance<FirTypeParameter>()
renderTypeParameterRefs(typeParameters, false)
renderSuperTypes(regularClass)
renderWhereSuffix(typeParameters)
}
fun FirDeclaration.isDefaultPrimaryConstructor() =
this is FirConstructor &&
isPrimary &&
valueParameters.isEmpty() &&
!hasBody &&
(visibility == Visibilities.DEFAULT_VISIBILITY || regularClass.classKind == ClassKind.OBJECT)
fun FirDeclaration.skipDeclarationForEnumClass(): Boolean {
if (this is FirConstructor) return isPrimary && valueParameters.isEmpty()
if (this !is FirSimpleFunction) return false
if (name.asString() == "values" && valueParameters.isEmpty()) return true
if (name.asString() == "valueOf") {
return valueParameters.count() == 1 && (valueParameters[0].returnTypeRef.coneType).classId == StandardClassIds.String
}
return false
}
fun renderDeclarationForEnumClass() {
check(regularClass.isEnumClass)
val partitioned = regularClass.declarations.partition { it is FirEnumEntry }
partitioned.first.forEach { enumEntry ->
check(enumEntry is FirEnumEntry)
visitEnumEntry(enumEntry, data)
data.append(",")
}
sortDeclarations(partitioned.second).forEach {
if (!it.skipDeclarationForEnumClass()) {
it.accept(this, data)
}
}
}
fun renderDeclarationForNotEnumClass() {
check(!regularClass.isEnumClass)
sortDeclarations(regularClass.declarations).forEach {
if (!it.isDefaultPrimaryConstructor()) {
it.accept(this, data)
}
}
}
if (options.renderContainingDeclarations) {
data.underBlockDeclaration(regularClass) {
if (regularClass.isEnumClass) {
renderDeclarationForEnumClass()
} else {
renderDeclarationForNotEnumClass()
property.setter?.let { setter ->
if (setter.needToRender()) {
appendLine()
renderPropertyAccessor(setter)
}
}
}
}
}
private var tabbedString = ""
private inline fun underTabbedBlock(body: () -> Unit) {
val oldTabbedString = tabbedString
tabbedString = " ".repeat(tabbedString.length + 4)
body()
tabbedString = oldTabbedString
}
private inline fun underContainingDeclaration(firDeclaration: FirDeclaration, body: () -> Unit) {
val oldContainingDeclaration = containingDeclaration
containingDeclaration = firDeclaration
body()
containingDeclaration = oldContainingDeclaration
}
private inline fun StringBuilder.underBlockDeclaration(firDeclaration: FirDeclaration, withBrackets: Boolean = true, body: () -> Unit) {
val oldLength = length
if (withBrackets) append(" {")
val unchangedLength = length
underContainingDeclaration(firDeclaration) {
underTabbedBlock(body)
}
if (unchangedLength != length) {
if (withBrackets) {
appendLine()
appendTabs()
append("}")
}
private fun PrettyPrinter.renderPropertyAccessor(propertyAccessor: FirPropertyAccessor) {
renderAnnotationsAndModifiers(propertyAccessor)
append(if (propertyAccessor.isGetter) "get" else "set")
if (propertyAccessor.isSetter) {
append("(value: ")
renderType(propertyAccessor.valueParameters.first().returnTypeRef)
append(")")
} else {
delete(oldLength, unchangedLength)
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 StringBuilder.appendTabs() = append(tabbedString)
private fun PrettyPrinter.renderConstructor(constructor: FirConstructor) {
renderAnnotationsAndModifiers(constructor)
append("constructor")
renderValueParameters(constructor)
renderFunctionBody(constructor)
}
private fun underBlockDeclaration(firDeclaration: FirDeclaration, firBlock: FirBlock, data: StringBuilder) {
data.underBlockDeclaration(firDeclaration) {
firBlock.accept(this, data)
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())
}
}
}
override fun visitTypeAlias(typeAlias: FirTypeAlias, data: StringBuilder) = with(data) {
if (options.renderDeclarationHeader) {
renderAnnotations(typeAlias)
renderVisibility(typeAlias.visibility)
renderMemberModifiers(typeAlias)
append("typealias").append(" ")
}
private fun PrettyPrinter.renderTypeAlias(typeAlias: FirTypeAlias) {
renderAnnotationsAndModifiers(typeAlias)
append("typealias ")
renderName(typeAlias)
renderTypeParameters(typeAlias.typeParameters, false)
append(" = ").append(renderType(typeAlias.expandedTypeRef))
Unit
renderTypeParameters(typeAlias)
printCharIfNotThere(' ')
append("= ")
renderType(typeAlias.expandedTypeRef)
}
override fun visitEnumEntry(enumEntry: FirEnumEntry, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
renderName(enumEntry)
}
private fun PrettyPrinter.renderEnumEntry(enumEntry: FirEnumEntry) {
renderName(enumEntry)
}
override fun visitElement(element: FirElement, data: StringBuilder) {
element.acceptChildren(this, data)
}
companion object {
fun render(
firDeclaration: FirDeclaration,
containingDeclaration: FirDeclaration?,
options: KtDeclarationRendererOptions,
session: FirSession
): String {
val renderer = FirIdeRenderer(
containingDeclaration,
options,
session,
)
return buildString {
firDeclaration.accept(renderer, this)
}.trim(' ', '\n', '\t')
}
}
/* TYPE PARAMETERS */
private fun StringBuilder.renderTypeParameter(typeParameter: FirTypeParameter, topLevel: Boolean) {
if (topLevel) {
append("<")
}
renderModifier(typeParameter.isReified, "reified")
private fun PrettyPrinter.renderTypeParameter(typeParameter: FirTypeParameter) {
renderIf(typeParameter.isReified, "reified")
val variance = typeParameter.variance.label
renderModifier(variance.isNotEmpty(), variance)
renderIf(variance.isNotEmpty(), variance)
renderAnnotations(typeParameter)
renderName(typeParameter)
val upperBoundsCount = typeParameter.bounds.size
if ((upperBoundsCount > 1 && !topLevel) || upperBoundsCount == 1) {
if (upperBoundsCount >= 1) {
val upperBound = typeParameter.bounds.first()
if (!upperBound.isNullableAny) {
append(" : ").append(renderType(upperBound))
append(" : ")
renderType(upperBound)
}
} else if (topLevel) {
typeParameter.bounds.filterNot { it.isNullableAny }.forEachIndexed { index, upperBound ->
val separator = if (index == 0) " : " else " & "
append(separator)
append(renderType(upperBound))
}
} else {
// rendered with "where"
}
if (topLevel) {
append(">")
}
}
private fun StringBuilder.renderTypeParameterRefs(typeParameters: List<FirTypeParameterRef>, withSpace: Boolean) =
renderTypeParameters(typeParameters.map { it.symbol.fir }, withSpace)
private fun StringBuilder.renderTypeParameters(typeParameters: List<FirTypeParameter>, withSpace: Boolean) {
private fun PrettyPrinter.renderTypeParameters(declaration: FirMemberDeclaration) {
val typeParameters = declaration.typeParameters.filterIsInstance<FirTypeParameter>()
if (typeParameters.isNotEmpty()) {
append("<")
renderTypeParameterList(typeParameters)
append(">")
if (withSpace) {
append(" ")
printCollection(typeParameters) {
renderTypeParameter(it)
}
append("> ")
}
}
private fun StringBuilder.renderTypeParameterList(typeParameters: List<FirTypeParameter>) {
val iterator = typeParameters.iterator()
while (iterator.hasNext()) {
val firTypeParameter = iterator.next()
renderTypeParameter(firTypeParameter, false)
if (iterator.hasNext()) {
append(", ")
}
}
}
private fun StringBuilder.renderReceiver(firCallableDeclaration: FirCallableDeclaration) {
private fun PrettyPrinter.renderReceiver(firCallableDeclaration: FirCallableDeclaration) {
val receiverType = firCallableDeclaration.receiverTypeRef
if (receiverType != null) {
if (options.renderDeclarationHeader) {
@@ -584,46 +234,42 @@ internal class FirIdeRenderer private constructor(
val needBrackets =
typeIdeRenderer.shouldRenderAsPrettyFunctionType(receiverType.coneType) && receiverType.isMarkedNullable == true
val result = renderType(receiverType).applyIf(needBrackets) { "($this)" }
append(result)
if (needBrackets) append('(')
renderType(receiverType)
if (needBrackets) append(')')
append(".")
}
}
private fun StringBuilder.renderWhereSuffix(typeParameters: List<FirTypeParameterRef>) {
private fun PrettyPrinter.renderWhereSuffix(declaration: FirTypeParameterRefsOwner) {
val upperBoundStrings = ArrayList<String>(0)
for (typeParameter in typeParameters) {
val typeParameterFir = typeParameter.symbol.fir
typeParameterFir.bounds
for (typeParameter in declaration.typeParameters) {
if (typeParameter !is FirTypeParameter) continue
typeParameter.bounds
.drop(1) // first parameter is rendered by renderTypeParameter
.mapTo(upperBoundStrings) { typeParameterFir.name.render() + " : " + renderType(it) }
.mapTo(upperBoundStrings) { typeParameter.name.render() + " : " + renderTypeToString(it.coneType) }
}
if (upperBoundStrings.isNotEmpty()) {
append(" where ")
append("where ")
upperBoundStrings.joinTo(this, ", ")
append(' ')
}
}
/* VARIABLES */
private fun StringBuilder.renderValueParameters(valueParameters: List<FirValueParameter>) {
append("(")
valueParameters.forEachIndexed { index, valueParameter ->
if (index != 0) append(", ")
renderValueParameter(valueParameter)
private fun PrettyPrinter.renderValueParameters(function: FirFunction) {
printCollection(function.valueParameters, prefix = "(", postfix = ")") {
renderValueParameter(it)
}
append(")")
}
private fun StringBuilder.renderValueParameter(valueParameter: FirValueParameter) {
private fun PrettyPrinter.renderValueParameter(valueParameter: FirValueParameter) {
if (options.renderDeclarationHeader) {
renderAnnotations(valueParameter)
}
renderModifier(valueParameter.isCrossinline, "crossinline")
renderModifier(valueParameter.isNoinline, "noinline")
renderIf(valueParameter.isCrossinline, "crossinline")
renderIf(valueParameter.isNoinline, "noinline")
renderVariable(valueParameter)
if (options.renderDefaultParameterValue) {
@@ -634,48 +280,27 @@ internal class FirIdeRenderer private constructor(
}
}
private fun StringBuilder.renderValVarPrefix(variable: FirVariable, isInPrimaryConstructor: Boolean = false) {
private fun PrettyPrinter.renderValVarPrefix(variable: FirVariable, isInPrimaryConstructor: Boolean = false) {
if (!isInPrimaryConstructor || variable !is FirValueParameter) {
append(if (variable.isVar) "var" else "val").append(" ")
append(if (variable.isVar) "var" else "val")
append(' ')
}
}
private fun StringBuilder.renderVariable(variable: FirVariable) {
val typeToRender = variable.returnTypeRef
private fun PrettyPrinter.renderVariable(variable: FirVariable) {
val typeToRender = variable.returnTypeRef.coneType
val isVarArg = (variable as? FirValueParameter)?.isVararg ?: false
renderModifier(isVarArg, "vararg")
renderIf(isVarArg, "vararg")
renderName(variable)
append(": ")
val parameterType = typeToRender.coneType
if (isVarArg) {
append(renderType(parameterType.arrayElementType() ?: parameterType))
renderType(typeToRender.arrayElementType() ?: typeToRender)
} else {
append(renderType(typeToRender))
renderType(typeToRender)
}
}
private fun StringBuilder.renderSuperTypes(klass: FirClass) {
if (klass.defaultType().isNothing) return
val supertypes = klass.superTypeRefs.applyIf(klass.classKind == ClassKind.ENUM_CLASS) {
filterNot {
(it as? FirResolvedTypeRef)?.coneType?.classId == StandardClassIds.Enum
}
}.applyIf(klass.classKind == ClassKind.ANNOTATION_CLASS) {
filterNot {
(it as? FirResolvedTypeRef)?.coneType?.classId == StandardClassIds.Annotation
}
}
if (supertypes.isEmpty() || klass.superTypeRefs.singleOrNull()?.let { it.isAny || it.isNullableAny } == true) return
tabRightBySpace()
append(": ")
supertypes.joinTo(this, ", ") { renderType(it) }
}
fun sortDeclarations(declarations: List<FirDeclaration>): List<FirDeclaration> {
fun sortDeclarations(declarations: List<FirMemberDeclaration>): List<FirMemberDeclaration> {
if (!options.sortNestedDeclarations) return declarations
fun getDeclarationKind(declaration: FirDeclaration): Int = when (declaration) {
@@ -697,34 +322,43 @@ internal class FirIdeRenderer private constructor(
return@Comparator nameResult
}
val leftString = StringBuilder().also { builder -> left.accept(this, builder) }.toString()
val rightString = StringBuilder().also { builder -> right.accept(this, builder) }.toString()
val leftString = prettyPrint { renderMemberDeclaration(left) }
val rightString = prettyPrint { renderMemberDeclaration(right) }
return@Comparator leftString.compareTo(rightString)
})
}
private fun getClassifierKindPrefix(classifier: FirDeclaration): String = when (classifier) {
is FirTypeAlias -> "typealias"
is FirRegularClass ->
if (classifier.isCompanion) {
"companion object"
} else {
when (classifier.classKind) {
ClassKind.CLASS -> "class"
ClassKind.INTERFACE -> "interface"
ClassKind.ENUM_CLASS -> "enum class"
ClassKind.OBJECT -> "object"
ClassKind.ANNOTATION_CLASS -> "annotation class"
ClassKind.ENUM_ENTRY -> "enum entry"
}
}
is FirAnonymousObject -> "object"
is FirEnumEntry -> "enum entry"
else ->
throw AssertionError("Unexpected classifier: $classifier")
}
private fun StringBuilder.tabRightBySpace() {
if (length == 0 || last() != ' ') append(' ')
companion object {
fun render(
firDeclaration: FirMemberDeclaration,
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()
if (this !is FirSimpleFunction) return false
if (name == StandardNames.ENUM_VALUES && valueParameters.isEmpty()) return true
if (name == StandardNames.ENUM_VALUE_OF) {
return valueParameters.singleOrNull()?.returnTypeRef?.isString == true
}
return false
}
private fun FirDeclaration.isDefaultPrimaryConstructor() =
this is FirConstructor &&
isPrimary &&
valueParameters.isEmpty() &&
!hasBody &&
visibility == Visibilities.DEFAULT_VISIBILITY
@@ -0,0 +1,178 @@
/*
* 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.containingClass
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
else -> TODO("Unexpected declaration ${declaration::class.qualifiedName}")
}
append(name.render())
}
private fun PrettyPrinter.renderVisibility(declaration: FirMemberDeclaration) {
if (declaration is FirConstructor && declaration.containingClass()?.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(' ')
}
}