diff --git a/analysis/analysis-api-fe10/build.gradle.kts b/analysis/analysis-api-fe10/build.gradle.kts index 623cb983c84..33c7de38678 100644 --- a/analysis/analysis-api-fe10/build.gradle.kts +++ b/analysis/analysis-api-fe10/build.gradle.kts @@ -8,6 +8,7 @@ dependencies { implementation(project(":compiler:frontend")) implementation(project(":compiler:frontend.java")) implementation(project(":analysis:analysis-api-impl-base")) + implementation(project(":analysis:analysis-internal-utils")) implementation(project(":compiler:backend")) implementation(project(":compiler:backend.jvm")) diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SymbolDeclarationRendererProvider.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SymbolDeclarationRendererProvider.kt index f313879b82e..4cb02ccd309 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SymbolDeclarationRendererProvider.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SymbolDeclarationRendererProvider.kt @@ -15,8 +15,10 @@ import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.bas import org.jetbrains.kotlin.analysis.api.descriptors.types.base.KtFe10Type import org.jetbrains.kotlin.analysis.api.descriptors.utils.KtFe10TypeRenderer import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol +import org.jetbrains.kotlin.analysis.api.symbols.markers.KtPossibleMemberSymbol import org.jetbrains.kotlin.analysis.api.tokens.ValidityToken import org.jetbrains.kotlin.analysis.api.types.KtType +import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint internal class KtFe10SymbolDeclarationRendererProvider( override val analysisSession: KtFe10AnalysisSession @@ -24,7 +26,7 @@ internal class KtFe10SymbolDeclarationRendererProvider( override val token: ValidityToken get() = analysisSession.token - override fun render(symbol: KtSymbol, options: KtDeclarationRendererOptions): String { + override fun renderMember(symbol: KtPossibleMemberSymbol, options: KtDeclarationRendererOptions): String { val descriptor = getSymbolDescriptor(symbol) if (descriptor != null) { return descriptor.render(analysisContext, options) @@ -36,8 +38,6 @@ internal class KtFe10SymbolDeclarationRendererProvider( override fun render(type: KtType, options: KtTypeRendererOptions): String { require(type is KtFe10Type) - val consumer = StringBuilder() - KtFe10TypeRenderer(options).render(type.type, consumer) - return consumer.toString() + return prettyPrint { KtFe10TypeRenderer(options).render(type.type, this) } } } \ No newline at end of file diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/symbols/descriptorBased/base/Kt1DescUtils.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/symbols/descriptorBased/base/Kt1DescUtils.kt index ba7c256af18..7dcd2cc3951 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/symbols/descriptorBased/base/Kt1DescUtils.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/symbols/descriptorBased/base/Kt1DescUtils.kt @@ -15,10 +15,12 @@ import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.* import org.jetbrains.kotlin.analysis.api.descriptors.symbols.psiBased.base.KtFe10PsiSymbol import org.jetbrains.kotlin.analysis.api.descriptors.types.* import org.jetbrains.kotlin.analysis.api.descriptors.utils.KtFe10Renderer +import org.jetbrains.kotlin.analysis.api.descriptors.utils.KtFe10TypeRenderer import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.symbols.markers.* import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability +import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor @@ -308,7 +310,7 @@ internal fun ConstantValue<*>.toKtAnnotationValue(): KtAnnotationValue { value.annotationClass?.classId, psi = null, useSiteTarget = null, - arguments = arguments, + arguments = value.getKtNamedAnnotationArguments(), ) ) } @@ -413,9 +415,7 @@ internal fun ClassDescriptor.getSupertypesWithAny(): Collection { internal fun DeclarationDescriptor.render(analysisContext: Fe10AnalysisContext, options: KtDeclarationRendererOptions): String { val renderer = KtFe10Renderer(analysisContext, options) - val consumer = StringBuilder() - renderer.render(this, consumer) - return consumer.toString().trim() + return prettyPrint { renderer.render(this@render, this) }.trim() } internal fun CallableMemberDescriptor.getSymbolPointerSignature(analysisContext: Fe10AnalysisContext): String { diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/types/base/KtFe10Type.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/types/base/KtFe10Type.kt index 9d7c50f03ff..e3165659d89 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/types/base/KtFe10Type.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/types/base/KtFe10Type.kt @@ -11,9 +11,11 @@ import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationsList import org.jetbrains.kotlin.analysis.api.components.KtTypeRendererOptions import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisContext import org.jetbrains.kotlin.analysis.api.descriptors.annotations.KtFe10AnnotationsList +import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.render import org.jetbrains.kotlin.analysis.api.descriptors.utils.KtFe10TypeRenderer import org.jetbrains.kotlin.analysis.api.tokens.ValidityToken import org.jetbrains.kotlin.analysis.api.withValidityAssertion +import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.UnwrappedType @@ -32,8 +34,6 @@ interface KtFe10Type : ValidityTokenOwner, KtAnnotated { } internal fun KotlinType.asStringForDebugging(): String { - val builder = StringBuilder() val renderer = KtFe10TypeRenderer(KtTypeRendererOptions.DEFAULT, isDebugText = true) - renderer.render(this, builder) - return builder.toString() + return prettyPrint { renderer.render(this@asStringForDebugging, this) } } \ No newline at end of file diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/KtFe10Renderer.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/KtFe10Renderer.kt index 8147e235476..53927311152 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/KtFe10Renderer.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/KtFe10Renderer.kt @@ -12,32 +12,31 @@ import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.bas import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.isExplicitOverride import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.ktModality import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.ktVisibility +import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter +import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.StandardClassIds -import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.renderer.render -import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces -import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeApproximator import org.jetbrains.kotlin.types.typeUtil.isNullableAny import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly -internal typealias KtFe10RendererConsumer = StringBuilder +internal typealias KtFe10RendererConsumer = PrettyPrinter internal class KtFe10Renderer( private val analysisContext: Fe10AnalysisContext, @@ -45,8 +44,6 @@ internal class KtFe10Renderer( isDebugText: Boolean = false ) { private companion object { - private val INDENTATION = " ".repeat(4) - val IGNORED_VISIBILITIES: Set = setOf( Visibilities.Local, Visibilities.PrivateToThis, @@ -69,22 +66,6 @@ internal class KtFe10Renderer( analysisContext.resolveSession.languageVersionSettings ) - private var indentation = 0 - - private fun KtFe10RendererConsumer.curlyBlock(body: KtFe10RendererConsumer.() -> Unit) { - append(" {") - indentedBlock(body) - appendLine() - renderIndentation() - append('}') - } - - private fun KtFe10RendererConsumer.indentedBlock(body: KtFe10RendererConsumer.() -> Unit) { - indentation += 1 - body() - indentation -= 1 - } - fun render(descriptor: DeclarationDescriptor, consumer: KtFe10RendererConsumer) { consumer.renderDeclaration(descriptor) } @@ -138,7 +119,7 @@ internal class KtFe10Renderer( private fun KtFe10RendererConsumer.renderTypeAlias(descriptor: TypeAliasDescriptor) { if (options.renderDeclarationHeader) { - renderAnnotations(descriptor.annotations) + renderAnnotations(descriptor) renderModifiers(descriptor) append("typealias ") } @@ -149,7 +130,9 @@ internal class KtFe10Renderer( } private fun KtFe10RendererConsumer.renderTypeParameters(typeParameters: List) { - renderList(typeParameters, separator = ", ", prefix = "<", postfix = ">", renderWhenEmpty = false) { renderTypeParameter(it) } + if (typeParameters.isNotEmpty()) append('<') + printCollection(typeParameters, separator = ", ") { renderTypeParameter(it) } + if (typeParameters.isNotEmpty()) append('>') } private fun KtFe10RendererConsumer.renderTypeParameter(descriptor: TypeParameterDescriptor) { @@ -158,19 +141,16 @@ internal class KtFe10Renderer( val variance = descriptor.variance.label renderModifier(variance, variance.isNotEmpty()) - renderAnnotations(descriptor.annotations) + renderAnnotations(descriptor) renderName(descriptor) val upperBounds = descriptor.upperBounds.filterNot { it.isNullableAny() } - renderList(upperBounds, separator = " & ", prefix = " : ", postfix = "", renderWhenEmpty = false) { renderType(it) } + printCollectionIfNotEmpty(upperBounds, separator = " & ", prefix = " : ") { renderType(it) } } private fun KtFe10RendererConsumer.renderClass(descriptor: ClassDescriptor) { - appendLine() - renderIndentation() - if (options.renderDeclarationHeader) { - renderAnnotations(descriptor.annotations) + renderAnnotations(descriptor) renderModifiers(descriptor) @@ -200,7 +180,7 @@ internal class KtFe10Renderer( renderTypeParameters(descriptor.declaredTypeParameters) renderSupertypes(descriptor) - if (options.renderContainingDeclarations) { + if (options.renderClassMembers) { val (enumEntries, otherDeclarations) = descriptor.unsubstitutedMemberScope.getContributedDescriptors() .filter { shouldRenderNestedDeclaration(descriptor, it) } .partition { it is ClassDescriptor && it.kind == ClassKind.ENUM_ENTRY } @@ -209,10 +189,19 @@ internal class KtFe10Renderer( .filter { shouldRenderNestedDeclaration(descriptor, it) } if (enumEntries.isNotEmpty() || otherDeclarations.isNotEmpty() || constructors.isNotEmpty()) { - curlyBlock { - enumEntries.forEach { renderEnumEntry(it as ClassDescriptor) } - sortDeclarations(constructors).forEach { renderConstructor(it) } - sortDeclarations(otherDeclarations).forEach { renderDeclaration(it) } + append(' ') + withIndentInBraces { + printCollection(sortDeclarations(enumEntries), separator = ",\n") { + renderEnumEntry(it as ClassDescriptor) + } + + if (enumEntries.isNotEmpty() && (constructors.isNotEmpty() || otherDeclarations.isNotEmpty())) { + appendLine(";\n") + } + + printCollection(sortDeclarations(constructors + otherDeclarations), separator = "\n\n") { + renderDeclaration(it) + } } } } @@ -268,8 +257,8 @@ internal class KtFe10Renderer( return@Comparator nameResult } - val leftString = buildString { renderDeclaration(left) } - val rightString = buildString { renderDeclaration(right) } + val leftString = prettyPrint { renderDeclaration(left) } + val rightString = prettyPrint { renderDeclaration(right) } return@Comparator leftString.compareTo(rightString) }) } @@ -279,7 +268,7 @@ internal class KtFe10Renderer( .filterTo(HashSet()) { it.classId !in IGNORED_SUPERTYPES } val supertypes = descriptor.typeConstructor.supertypes.filter { it.constructor.declarationDescriptor in allowedSuperClasses } - renderList(supertypes, separator = ", ", prefix = " : ", postfix = "", renderWhenEmpty = false) { renderType(it) } + printCollectionIfNotEmpty(supertypes, separator = ", ", prefix = " : ") { renderType(it) } } private fun KtFe10RendererConsumer.renderCallable(descriptor: CallableDescriptor) { @@ -296,47 +285,35 @@ internal class KtFe10Renderer( } private fun KtFe10RendererConsumer.renderPropertyAccessor(descriptor: PropertyAccessorDescriptor) { - appendLine() - indentedBlock { - renderIndentation() - if (options.renderDeclarationHeader) { - renderAnnotations(descriptor.annotations) - renderModifiers(descriptor) - } + if (options.renderDeclarationHeader) { + renderAnnotations(descriptor) + renderModifiers(descriptor) + } - when (descriptor) { - is PropertyGetterDescriptor -> { - append("get()") - } - is PropertySetterDescriptor -> { - append("set(") - val valueParameter = descriptor.valueParameters.singleOrNull() - if (valueParameter != null) { - val name = valueParameter.name.takeIf { !it.isSpecial } ?: Name.identifier("value") - renderValueParameter(valueParameter, name) - } - append(")") + when (descriptor) { + is PropertyGetterDescriptor -> { + append("get()") + } + is PropertySetterDescriptor -> { + append("set(") + val valueParameter = descriptor.valueParameters.singleOrNull() + if (valueParameter != null) { + val name = valueParameter.name.takeIf { !it.isSpecial } ?: Name.identifier("value") + renderValueParameter(valueParameter, name) } + append(")") } } } private fun KtFe10RendererConsumer.renderEnumEntry(descriptor: ClassDescriptor) { assert(descriptor.kind == ClassKind.ENUM_ENTRY) - - appendLine() - renderIndentation() - renderName(descriptor) - append(',') } private fun KtFe10RendererConsumer.renderLocalVariable(descriptor: LocalVariableDescriptor) { - appendLine() - renderIndentation() - if (options.renderDeclarationHeader) { - renderAnnotations(descriptor.annotations) + renderAnnotations(descriptor) append(if (descriptor.isVar) "var" else "val").append(' ') } @@ -346,11 +323,8 @@ internal class KtFe10Renderer( } private fun KtFe10RendererConsumer.renderProperty(descriptor: PropertyDescriptor) { - appendLine() - renderIndentation() - if (options.renderDeclarationHeader) { - renderAnnotations(descriptor.annotations) + renderAnnotations(descriptor) renderModifiers(descriptor) append(if (descriptor.isVar) "var" else "val").append(' ') renderTypeParameters(descriptor.typeParameters) @@ -364,11 +338,9 @@ internal class KtFe10Renderer( append(": ") renderType(descriptor.type, shouldApproximate = options.approximateTypes) - if (options.renderContainingDeclarations) { + if (options.renderClassMembers) { fun shouldRenderAccessor(accessor: PropertyAccessorDescriptor): Boolean { - return descriptor.isDelegated - || accessor.hasBody() - || !accessor.annotations.isEmpty() + return !accessor.annotations.isEmpty() || accessor.visibility != descriptor.visibility } @@ -377,11 +349,15 @@ internal class KtFe10Renderer( val shouldRenderAccessors = (getter != null && shouldRenderAccessor(getter)) || (setter != null && shouldRenderAccessor(setter)) if (shouldRenderAccessors) { - if (getter != null) { - renderPropertyAccessor(getter) - } - if (setter != null) { - renderPropertyAccessor(setter) + withIndent { + if (getter != null) { + appendLine() + renderPropertyAccessor(getter) + } + if (setter != null) { + appendLine() + renderPropertyAccessor(setter) + } } } } @@ -401,23 +377,16 @@ internal class KtFe10Renderer( } private fun KtFe10RendererConsumer.renderConstructor(descriptor: ConstructorDescriptor) { - appendLine() - renderIndentation() if (options.renderDeclarationHeader) { - renderAnnotations(descriptor.annotations) + renderAnnotations(descriptor) } append("constructor") renderValueParameters(descriptor.valueParameters) - - renderLocalDeclarations(descriptor) } private fun KtFe10RendererConsumer.renderFunction(descriptor: FunctionDescriptor) { - appendLine() - renderIndentation() - if (options.renderDeclarationHeader) { - renderAnnotations(descriptor.annotations) + renderAnnotations(descriptor) renderModifiers(descriptor) append("fun ") renderTypeParameters(descriptor.typeParameters) @@ -435,17 +404,15 @@ internal class KtFe10Renderer( append(": ") renderType(returnType, shouldApproximate = options.approximateTypes) } - - renderLocalDeclarations(descriptor) } private fun KtFe10RendererConsumer.renderValueParameters(valueParameters: List) { - renderList(valueParameters, separator = ", ", prefix = "(", postfix = ")", renderWhenEmpty = true) { renderValueParameter(it) } + printCollection(valueParameters, separator = ", ", prefix = "(", postfix = ")") { renderValueParameter(it) } } private fun KtFe10RendererConsumer.renderValueParameter(descriptor: ValueParameterDescriptor, name: Name = descriptor.name) { if (options.renderDeclarationHeader) { - renderAnnotations(descriptor.annotations) + renderAnnotations(descriptor) } renderModifiers(descriptor) @@ -458,50 +425,16 @@ internal class KtFe10Renderer( } } - private fun KtFe10RendererConsumer.renderLocalDeclarations(descriptor: CallableMemberDescriptor) { - if (!options.renderContainingDeclarations) { - return - } - - val body = when (val declaration = descriptor.source.getPsi()) { - is KtFunction -> declaration.bodyExpression - is KtPropertyAccessor -> declaration.bodyExpression - else -> null - } - - val collectedChildren = mutableListOf() - - body?.accept(object : KtTreeVisitorVoid() { - override fun visitDeclaration(declaration: KtDeclaration) { - if (declaration is KtFunction || declaration is KtVariableDeclaration || declaration is KtClassOrObject) { - val bindingContext = analysisContext.analyze(declaration) - val childDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] - if (childDescriptor != null) { - collectedChildren += childDescriptor - } - } else { - super.visitDeclaration(declaration) - } - } - }) - - if (collectedChildren.isNotEmpty()) { - curlyBlock { - collectedChildren.forEach { renderDeclaration(it) } - } - } - } - private fun KtFe10RendererConsumer.renderName(descriptor: DeclarationDescriptor) { append(descriptor.name.render()) } - private fun KtFe10RendererConsumer.renderAnnotations(annotations: Annotations, predicate: (ClassId) -> Boolean = { true }) { + private fun KtFe10RendererConsumer.renderAnnotations(declaration: Annotated, predicate: (ClassId) -> Boolean = { true }) { if (RendererModifier.ANNOTATIONS !in options.modifiers) { return } - - renderFe10Annotations(annotations, predicate) + val isSingleLineAnnotations = declaration is ValueParameterDescriptor || declaration is TypeParameterDescriptor + renderFe10Annotations(declaration.annotations, isSingleLineAnnotations, predicate) } private fun KtFe10RendererConsumer.renderModifiers(descriptor: DeclarationDescriptor) { @@ -595,30 +528,4 @@ internal class KtFe10Renderer( append(text).append(' ') } } - - private fun KtFe10RendererConsumer.renderIndentation() { - append(INDENTATION.repeat(indentation)) - } -} - -internal fun KtFe10RendererConsumer.renderList( - list: Collection, - separator: String, - prefix: String = "", - postfix: String = "", - renderWhenEmpty: Boolean = true, - renderItem: KtFe10RendererConsumer.(T) -> Unit -) { - if (!renderWhenEmpty && list.isEmpty()) { - return - } - - append(prefix) - list.forEachIndexed { index, item -> - if (index > 0) { - append(separator) - } - renderItem(item) - } - append(postfix) } \ No newline at end of file diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/KtFe10TypeRenderer.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/KtFe10TypeRenderer.kt index b999723577d..f2abe946fa8 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/KtFe10TypeRenderer.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/KtFe10TypeRenderer.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.bas import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.getKtNamedAnnotationArguments import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.maybeLocalClassId import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtConstantValue +import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType @@ -38,7 +39,7 @@ internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, pr const val ERROR_TYPE_TEXT = "ERROR_TYPE" } - fun render(type: KotlinType, consumer: KtFe10RendererConsumer) { + fun render(type: KotlinType, consumer: PrettyPrinter) { consumer.renderType(type) } @@ -46,7 +47,7 @@ internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, pr if (isDebugText) { renderTypeAnnotationsDebug(type) } else { - renderFe10Annotations(type.annotations) { classId -> + renderFe10Annotations(type.annotations, isSingleLineAnnotations = true) { classId -> classId != StandardClassIds.Annotations.ExtensionFunctionType } } @@ -86,7 +87,7 @@ internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, pr val annotations = type.annotations .filter { it.annotationClass?.classId != StandardClassIds.Annotations.ExtensionFunctionType } - renderList(annotations, separator = " ", postfix = " ", renderWhenEmpty = false) { renderTypeAnnotationDebug(it) } + printCollectionIfNotEmpty(annotations, separator = " ", postfix = " ") { renderTypeAnnotationDebug(it) } } private fun KtFe10RendererConsumer.renderTypeAnnotationDebug(annotation: AnnotationDescriptor) { @@ -105,7 +106,7 @@ internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, pr print("") } - renderList(namedValues, separator = ", ", prefix = "(", postfix = ")") { (name, value) -> + printCollection(namedValues, separator = ", ", prefix = "(", postfix = ")") { (name, value) -> append(name).append(" = ") renderConstantValueDebug(value) } @@ -115,9 +116,9 @@ internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, pr when (value) { is KtAnnotationApplicationValue -> renderAnnotationDebug(value.annotationValue.classId, value.annotationValue.arguments) is KtArrayAnnotationValue -> - renderList(value.values, separator = ", ", prefix = "[", postfix = "]") { renderConstantValueDebug(it) } - is KtEnumEntryAnnotationValue -> append(value.callableId) - is KtConstantAnnotationValue -> append(value.constantValue.constantValueKind.asString).append("(").append(value.constantValue.value).append(")") + printCollection(value.values, separator = ", ", prefix = "[", postfix = "]") { renderConstantValueDebug(it) } + is KtEnumEntryAnnotationValue -> append(value.callableId?.asSingleFqName()?.render()) + is KtConstantAnnotationValue -> append(value.constantValue.constantValueKind.asString).append("(").append(value.constantValue.value.toString()).append(")") KtUnsupportedAnnotationValue -> append(KtUnsupportedAnnotationValue::class.java.simpleName) is KtKClassAnnotationValue -> append(value.render()) } @@ -168,7 +169,7 @@ internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, pr if (isDebugText) { append("it") } - renderList(typeConstructor.supertypes, separator = " & ", prefix = "(", postfix = ")") { renderType(it) } + printCollection(typeConstructor.supertypes, separator = " & ", prefix = "(", postfix = ")") { renderType(it) } } private fun KtFe10RendererConsumer.renderFunctionType(type: SimpleType) { @@ -180,7 +181,7 @@ internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, pr renderType(receiverType) append(".") } - renderList(valueParameters, separator = ", ", prefix = "(", postfix = ")") { renderTypeProjection(it) } + printCollection(valueParameters, separator = ", ", prefix = "(", postfix = ")") { renderTypeProjection(it) } append(" -> ") renderType(returnType) } @@ -192,7 +193,7 @@ internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, pr private fun KtFe10RendererConsumer.renderOrdinaryType(type: SimpleType) { val nestedType = KtFe10JvmTypeMapperContext.getNestedType(type) renderTypeSegment(nestedType.root, isRoot = true) - renderList(nestedType.nested, separator = ".", prefix = ".", postfix = "", renderWhenEmpty = false) { renderTypeSegment(it) } + printCollectionIfNotEmpty(nestedType.nested, separator = ".", prefix = ".", postfix = "") { renderTypeSegment(it) } } private fun KtFe10RendererConsumer.renderTypeSegment(typeSegment: PossiblyInnerType, isRoot: Boolean = false) { @@ -212,11 +213,11 @@ internal class KtFe10TypeRenderer(private val options: KtTypeRendererOptions, pr } val arguments = typeSegment.arguments - renderList(arguments, separator = ", ", prefix = "<", postfix = ">", renderWhenEmpty = false) { renderTypeProjection(it) } + printCollectionIfNotEmpty(arguments, separator = ", ", prefix = "<", postfix = ">") { renderTypeProjection(it) } } private fun KtFe10RendererConsumer.renderFqName(fqName: FqName) { - renderList(fqName.pathSegments(), separator = ".") { append(it.render()) } + printCollection(fqName.pathSegments(), separator = ".") { append(it.render()) } } private fun KtFe10RendererConsumer.renderTypeProjection(projection: TypeProjection) { diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/renderAnnotations.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/renderAnnotations.kt index f0eb3aa4d98..a192da8ed11 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/renderAnnotations.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/utils/renderAnnotations.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.bas import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtConstantValue import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationValueRenderer import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtAnnotationValue +import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.name.ClassId @@ -16,7 +17,12 @@ import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -internal fun KtFe10RendererConsumer.renderFe10Annotations(annotations: Annotations, predicate: (ClassId) -> Boolean = { true }) { +internal fun PrettyPrinter.renderFe10Annotations( + annotations: Annotations, + isSingleLineAnnotations: Boolean, + predicate: (ClassId) -> Boolean = { true } +) { + val separator = if (isSingleLineAnnotations) " " else "\n" for (annotation in annotations) { val annotationClass = annotation.annotationClass ?: continue val classId = annotationClass.classId @@ -29,13 +35,13 @@ internal fun KtFe10RendererConsumer.renderFe10Annotations(annotations: Annotatio append(annotation.fqName?.shortName()?.asString() ?: "ERROR") val valueArguments = annotation.allValueArguments.entries.sortedBy { it.key.asString() } - renderList(valueArguments, separator = ", ", prefix = "(", postfix = ")", renderWhenEmpty = false) { (name, value) -> + printCollectionIfNotEmpty(valueArguments, separator = ", ", prefix = "(", postfix = ")") { (name, value) -> append(name.render()) append(" = ") append(KtAnnotationValueRenderer.render(value.toKtAnnotationValue())) } - append(' ') + append(separator) } } } \ No newline at end of file diff --git a/analysis/analysis-api-fir/build.gradle.kts b/analysis/analysis-api-fir/build.gradle.kts index b4e792543ec..6b7fa936f9e 100644 --- a/analysis/analysis-api-fir/build.gradle.kts +++ b/analysis/analysis-api-fir/build.gradle.kts @@ -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")) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolDeclarationRendererProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolDeclarationRendererProvider.kt index 6234089ff58..fcd9cae0a8c 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolDeclarationRendererProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolDeclarationRendererProvider.kt @@ -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) } - } } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/ConeTypeIdeRenderer.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/ConeTypeIdeRenderer.kt index 68d934e41f5..47d6ebf16dd 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/ConeTypeIdeRenderer.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/ConeTypeIdeRenderer.kt @@ -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) } } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirAnnotationsIdeRenderer.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirAnnotationsIdeRenderer.kt index 102890bc9a7..2a600b95ab1 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirAnnotationsIdeRenderer.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirAnnotationsIdeRenderer.kt @@ -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, - 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 } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirIdeRenderer.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirIdeRenderer.kt index e3d6acd055b..a7c67608b79 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirIdeRenderer.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirIdeRenderer.kt @@ -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() { - - 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("") - 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() - 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() + 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, withSpace: Boolean) = - renderTypeParameters(typeParameters.map { it.symbol.fir }, withSpace) - - private fun StringBuilder.renderTypeParameters(typeParameters: List, withSpace: Boolean) { + private fun PrettyPrinter.renderTypeParameters(declaration: FirMemberDeclaration) { + val typeParameters = declaration.typeParameters.filterIsInstance() if (typeParameters.isNotEmpty()) { append("<") - renderTypeParameterList(typeParameters) - append(">") - if (withSpace) { - append(" ") + printCollection(typeParameters) { + renderTypeParameter(it) } + append("> ") } } - private fun StringBuilder.renderTypeParameterList(typeParameters: List) { - 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) { - + private fun PrettyPrinter.renderWhereSuffix(declaration: FirTypeParameterRefsOwner) { val upperBoundStrings = ArrayList(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) { - 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): List { + fun sortDeclarations(declarations: List): List { 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 diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirIdeRendererBase.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirIdeRendererBase.kt new file mode 100644 index 00000000000..4bbfb426183 --- /dev/null +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/renderer/FirIdeRendererBase.kt @@ -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("") + 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(' ') + } +} + diff --git a/analysis/analysis-api-impl-base/build.gradle.kts b/analysis/analysis-api-impl-base/build.gradle.kts index 9412d13a14b..e5228dd058c 100644 --- a/analysis/analysis-api-impl-base/build.gradle.kts +++ b/analysis/analysis-api-impl-base/build.gradle.kts @@ -8,6 +8,7 @@ dependencies { api(project(":analysis:analysis-api")) api(project(":analysis:analysis-api-impl-barebone")) api(intellijCoreDep()) { includeJars("intellij-core", rootProject = rootProject) } + implementation(project(":analysis:analysis-internal-utils")) testApiJUnit5() testApi(project(":kotlin-test:kotlin-test-junit")) diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/symbolDeclarationRenderer/AbstractRendererTest.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/symbolDeclarationRenderer/AbstractRendererTest.kt index 4f77b1ac8e1..0ce8e977fda 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/symbolDeclarationRenderer/AbstractRendererTest.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/symbolDeclarationRenderer/AbstractRendererTest.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.analysis.api.components.KtDeclarationRendererOptions import org.jetbrains.kotlin.analysis.api.components.KtTypeRendererOptions import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleFileTest +import org.jetbrains.kotlin.analysis.api.symbols.markers.KtPossibleMemberSymbol import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.services.TestServices @@ -18,16 +19,18 @@ abstract class AbstractRendererTest(configurator: FrontendApiTestConfiguratorSer override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) { val options = KtDeclarationRendererOptions.DEFAULT.copy( approximateTypes = true, - renderContainingDeclarations = true, + renderClassMembers = true, typeRendererOptions = KtTypeRendererOptions.SHORT_NAMES, sortNestedDeclarations = true ) val actual = executeOnPooledThreadInReadAction { buildString { - ktFile.declarations.forEach { - analyseForTest(it) { - append(it.getSymbol().render(options)) + ktFile.declarations.forEach { declaration -> + analyseForTest(declaration) { + val symbol = declaration.getSymbol() as? KtPossibleMemberSymbol ?: return@analyseForTest + append(symbol.render(options)) + appendLine() appendLine() } } diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSymbolDeclarationRendererProvider.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSymbolDeclarationRendererProvider.kt index 8488cdf29ca..316d7d98dd2 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSymbolDeclarationRendererProvider.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSymbolDeclarationRendererProvider.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.analysis.api.components import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol +import org.jetbrains.kotlin.analysis.api.symbols.markers.KtPossibleMemberSymbol import org.jetbrains.kotlin.analysis.api.types.KtType /** @@ -68,9 +69,9 @@ public data class KtDeclarationRendererOptions( */ val normalizedVisibilities: Boolean = false, /** - * Render containing declarations + * Render members of a classes and objects if any */ - val renderContainingDeclarations: Boolean = false, + val renderClassMembers: Boolean = false, /** * Approximate Kotlin not-denotable types into denotable for declarations return type */ @@ -124,7 +125,7 @@ public enum class RendererModifier(public val includeByDefault: Boolean) { } public abstract class KtSymbolDeclarationRendererProvider : KtAnalysisSessionComponent() { - public abstract fun render(symbol: KtSymbol, options: KtDeclarationRendererOptions): String + public abstract fun renderMember(symbol: KtPossibleMemberSymbol, options: KtDeclarationRendererOptions): String public abstract fun render(type: KtType, options: KtTypeRendererOptions): String } @@ -135,8 +136,8 @@ public interface KtSymbolDeclarationRendererMixIn : KtAnalysisSessionMixIn { /** * Render symbol into the representable Kotlin string */ - public fun KtSymbol.render(options: KtDeclarationRendererOptions = KtDeclarationRendererOptions.DEFAULT): String = - analysisSession.symbolDeclarationRendererProvider.render(this, options) + public fun KtPossibleMemberSymbol.render(options: KtDeclarationRendererOptions = KtDeclarationRendererOptions.DEFAULT): String = + analysisSession.symbolDeclarationRendererProvider.renderMember(this, options) /** * Render kotlin type into the representable Kotlin type string diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt index 3370a93f2ab..988994d0de7 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt @@ -42,6 +42,7 @@ val FirTypeRef.isNullableNothing: Boolean get() = isBuiltinType(StandardClassIds val FirTypeRef.isUnit: Boolean get() = isBuiltinType(StandardClassIds.Unit, false) val FirTypeRef.isBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, false) val FirTypeRef.isInt: Boolean get() = isBuiltinType(StandardClassIds.Int, false) +val FirTypeRef.isString: Boolean get() = isBuiltinType(StandardClassIds.String, false) val FirTypeRef.isEnum: Boolean get() = isBuiltinType(StandardClassIds.Enum, false) val FirTypeRef.isArrayType: Boolean get() = diff --git a/core/compiler.common/src/org/jetbrains/kotlin/descriptors/ClassKind.kt b/core/compiler.common/src/org/jetbrains/kotlin/descriptors/ClassKind.kt index aaeb8bd69af..68fde07bdf8 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/descriptors/ClassKind.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/descriptors/ClassKind.kt @@ -15,13 +15,13 @@ */ package org.jetbrains.kotlin.descriptors -enum class ClassKind { - CLASS, - INTERFACE, - ENUM_CLASS, - ENUM_ENTRY, - ANNOTATION_CLASS, - OBJECT; +enum class ClassKind(val codeRepresentation: String?) { + CLASS( "class"), + INTERFACE("interface"), + ENUM_CLASS( "enum class"), + ENUM_ENTRY( null), + ANNOTATION_CLASS( "annotation class"), + OBJECT("object"); val isSingleton: Boolean get() = this == OBJECT || this == ENUM_ENTRY