[Analysis API] Prototype new rendering engine
diff --git a/analysis/analysis-api-fir/resources/META-INF/analysis-api/analysis-api-fir.xml b/analysis/analysis-api-fir/resources/META-INF/analysis-api/analysis-api-fir.xml index 5836635..303499f 100644 --- a/analysis/analysis-api-fir/resources/META-INF/analysis-api/analysis-api-fir.xml +++ b/analysis/analysis-api-fir/resources/META-INF/analysis-api/analysis-api-fir.xml
@@ -44,6 +44,11 @@ serviceImplementation="org.jetbrains.kotlin.analysis.api.fir.projectStructure.KaFirDanglingFileResolutionModeProvider" /> + <applicationService + serviceInterface="org.jetbrains.kotlin.analysis.api.internals.KaRendererProvider" + serviceImplementation="org.jetbrains.kotlin.analysis.api.fir.rendering.KaRendererProviderImpl" + /> + <projectService serviceInterface="org.jetbrains.kotlin.references.utils.KotlinKDocResolutionStrategyProviderService" serviceImplementation="org.jetbrains.kotlin.analysis.api.fir.references.KotlinFirKDocResolutionStrategyProviderService"
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/rendering/KaFirRendererProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/rendering/KaFirRendererProvider.kt new file mode 100644 index 0000000..ddfc8ed --- /dev/null +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/rendering/KaFirRendererProvider.kt
@@ -0,0 +1,117 @@ +/* + * Copyright 2010-2026 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.rendering + +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.base.KaConstantValue +import org.jetbrains.kotlin.analysis.api.rendering.KaPiece +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderer +import org.jetbrains.kotlin.analysis.api.rendering.KaRendererBuilder +import org.jetbrains.kotlin.analysis.api.rendering.KaPieceRenderer +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderingOption +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderingOutput +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderingContext +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.analysis.api.types.KaType + +@Suppress("UNCHECKED_CAST") +internal class KaRendererImpl(val renderers: KaPieceRendererMap, val options: KaRenderingOptionMap) : KaRenderer { + context(session: KaSession) + override fun render(symbol: KaSymbol, output: KaRenderingOutput) { + render(symbol, KaPiece.Symbol, createContext(output), output) + } + + context(session: KaSession) + override fun render(type: KaType, output: KaRenderingOutput) { + render(type, KaPiece.Type, createContext(output), output) + } + + context(session: KaSession) + override fun render(constantValue: KaConstantValue, output: KaRenderingOutput) { + render(constantValue, KaPiece.ConstantValue, createContext(output), output) + } + + private fun createContext(output: KaRenderingOutput): KaRenderingContext { + return object : KaRenderingContext { + context(session: KaSession) + override fun <T> render(value: T, piece: KaPiece<T>) { + render(value, piece, this, output) + } + + override fun <T> valueFor(option: KaRenderingOption<T>): T { + return (options[option] as T?) ?: option.defaultValue + } + } + } + + context(session: KaSession) + private fun <T> render(value: T, piece: KaPiece<T>, context: KaRenderingContext, output: KaRenderingOutput) { + context(output, context) { + @Suppress("UNCHECKED_CAST") + val rendererTower = renderers.getValue(piece) as List<KaPieceRenderer<T>> + render(value, rendererTower, rendererTower.lastIndex) + } + } + + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + private fun <T> render(value: T, rendererTower: List<KaPieceRenderer<T>>, topIndex: Int) { + var index = topIndex + + val next = object : () -> Unit { + var nextIndex = 0 + + override fun invoke() { + if (nextIndex < 0) { + error("The built-in renderer must not delegate") + } + render(value, rendererTower, nextIndex) + } + } + + while (index >= 0) { + next.nextIndex = index - 1 + val renderer = rendererTower[index] + if (renderer.render(value, next)) { + return // Value is handled + } + index -= 1 + } + } + + override fun copy(block: KaRendererBuilder.() -> Unit) = buildRenderer(this, block) +} + +internal typealias KaPieceRendererMap = Map<KaPiece<*>, List<KaPieceRenderer<*>>> +internal typealias KaRenderingOptionMap = Map<KaRenderingOption<*>, *> + +internal fun buildRenderer(origin: KaRendererImpl?, block: KaRendererBuilder.() -> Unit): KaRendererImpl { + val renderers: MutableMap<KaPiece<*>, List<KaPieceRenderer<*>>> = origin?.renderers?.toMutableMap() ?: HashMap() + val options: MutableMap<KaRenderingOption<*>, Any?> = origin?.options?.toMutableMap() ?: HashMap() + + val builder = object : KaRendererBuilder { + override fun <T> push(renderer: KaPieceRenderer<T>) { + renderers.compute(renderer.piece) { _, oldValue -> + if (oldValue == null) { + listOf(renderer) + } else { + oldValue + renderer + } + } + } + + override fun <T> set(option: KaRenderingOption<T>, value: T) { + options[option] = value + } + + override fun <T> unset(option: KaRenderingOption<T>) { + options.remove(option) + } + } + + builder.block() + + return KaRendererImpl(renderers, options) +}
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/rendering/KaRendererProviderImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/rendering/KaRendererProviderImpl.kt new file mode 100644 index 0000000..6363bb5 --- /dev/null +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/rendering/KaRendererProviderImpl.kt
@@ -0,0 +1,14 @@ +/* + * Copyright 2010-2026 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.rendering + +import org.jetbrains.kotlin.analysis.api.internals.KaRendererProvider +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderer + +internal class KaRendererProviderImpl : KaRendererProvider { + override val defaultRenderer: KaRenderer + get() = DEFAULT_RENDERER +}
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/rendering/defaultRenderers.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/rendering/defaultRenderers.kt new file mode 100644 index 0000000..6bba8e0 --- /dev/null +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/rendering/defaultRenderers.kt
@@ -0,0 +1,1489 @@ +/* + * Copyright 2010-2026 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.rendering + +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi +import org.jetbrains.kotlin.analysis.api.KaNonPublicApi +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotated +import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotation +import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotationValue +import org.jetbrains.kotlin.analysis.api.base.KaConstantValue +import org.jetbrains.kotlin.analysis.api.rendering.KaClassTypeQualification +import org.jetbrains.kotlin.analysis.api.rendering.KaPiece +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderer +import org.jetbrains.kotlin.analysis.api.rendering.KaPieceRenderer +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderingOption +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderingOutput +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderingContext +import org.jetbrains.kotlin.analysis.api.rendering.KaTextAttribute +import org.jetbrains.kotlin.analysis.api.rendering.KaTypeApproximation +import org.jetbrains.kotlin.analysis.api.rendering.render +import org.jetbrains.kotlin.analysis.api.scopes.declaredMemberScope +import org.jetbrains.kotlin.analysis.api.symbols.KaAnonymousFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaAnonymousObjectSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaBackingFieldSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind +import org.jetbrains.kotlin.analysis.api.symbols.KaClassLikeSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaContextParameterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaDeclarationSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaEnumEntrySymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaJavaFieldSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaKotlinPropertySymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaLocalVariableSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaNamedClassSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaNamedFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaPackageSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaParameterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaPropertyAccessorSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaPropertyGetterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaPropertySetterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaPropertySymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaReceiverParameterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSamConstructorSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolModality +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolVisibility +import org.jetbrains.kotlin.analysis.api.symbols.KaTypeAliasSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaTypeParameterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.containingDeclaration +import org.jetbrains.kotlin.analysis.api.symbols.findPackage +import org.jetbrains.kotlin.analysis.api.symbols.markers.KaNamedSymbol +import org.jetbrains.kotlin.analysis.api.types.KaCapturedType +import org.jetbrains.kotlin.analysis.api.types.KaClassErrorType +import org.jetbrains.kotlin.analysis.api.types.KaClassType +import org.jetbrains.kotlin.analysis.api.types.KaDefinitelyNotNullType +import org.jetbrains.kotlin.analysis.api.types.KaDynamicType +import org.jetbrains.kotlin.analysis.api.types.KaErrorType +import org.jetbrains.kotlin.analysis.api.types.KaFlexibleType +import org.jetbrains.kotlin.analysis.api.types.KaFunctionType +import org.jetbrains.kotlin.analysis.api.types.KaIntersectionType +import org.jetbrains.kotlin.analysis.api.types.KaResolvedClassTypeQualifier +import org.jetbrains.kotlin.analysis.api.types.KaStarTypeProjection +import org.jetbrains.kotlin.analysis.api.types.KaType +import org.jetbrains.kotlin.analysis.api.types.KaTypeArgumentWithVariance +import org.jetbrains.kotlin.analysis.api.types.KaTypeParameterType +import org.jetbrains.kotlin.analysis.api.types.KaTypeProjection +import org.jetbrains.kotlin.analysis.api.types.approximateToDenotableSubtypeOrSelf +import org.jetbrains.kotlin.analysis.api.types.approximateToDenotableSupertypeOrSelf +import org.jetbrains.kotlin.analysis.api.types.classId +import org.jetbrains.kotlin.analysis.api.types.expandedSymbol +import org.jetbrains.kotlin.analysis.api.types.isMarkedNullable +import org.jetbrains.kotlin.analysis.api.types.isNullable +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.name.render +import org.jetbrains.kotlin.types.Variance + +internal val DEFAULT_RENDERER: KaRenderer = buildRenderer(null) { + // Top-level dispatchers. + push(SymbolRenderer) + push(TypeRenderer) + + // Shared building blocks. + push(SymbolNameRenderer) + push(SymbolModifiersRenderer) + push(VisibilityRenderer) + push(ModalityRenderer) + push(AnnotationsRenderer) + push(AnnotationRenderer) + push(AnnotationValuesRenderer) + push(AnnotationValueRenderer) + push(ConstantValueRenderer) + push(TypeAnnotationsRenderer) + push(TypeNullabilityRenderer) + + // Types. + push(ClassTypeRenderer) + push(FunctionTypeRenderer) + push(TypeParameterTypeRenderer) + push(CapturedTypeRenderer) + push(DefinitelyNotNullTypeRenderer) + push(FlexibleTypeRenderer) + push(IntersectionTypeRenderer) + push(DynamicTypeRenderer) + push(ErrorTypeRenderer) + push(TypeArgumentListRenderer) + push(TypeProjectionRenderer) + + // Type parameters. + push(TypeParameterListRenderer) + push(CallableTypeParameterListRenderer) + push(ClassifierTypeParameterListRenderer) + push(TypeParameterRenderer) + push(WhereClauseRenderer) + push(CallableWhereClauseRenderer) + push(ClassifierWhereClauseRenderer) + + // Context parameters. + push(ContextParameterListRenderer) + push(CallableContextParameterListRenderer) + push(ContextParameterRenderer) + push(ContextParameterAnnotationsRenderer) + push(ContextParameterTypeRenderer) + + // Value parameters. + push(ValueParameterListRenderer) + push(ValueParameterRenderer) + push(ValueParameterAnnotationsRenderer) + push(ValueParameterTypeRenderer) + push(ValueParameterDefaultValueRenderer) + + // Functions. + push(FunctionRenderer) + push(FunctionModifiersRenderer) + push(FunctionAnnotationsRenderer) + push(FunctionReceiverRenderer) + push(FunctionValueParameterListRenderer) + push(FunctionReturnTypeRenderer) + push(NamedFunctionReturnTypeRenderer) + push(NamedFunctionRenderer) + push(ConstructorRenderer) + push(PropertyAccessorRenderer) + + // Properties and variables. + push(PropertyRenderer) + push(PropertyModifiersRenderer) + push(PropertyAnnotationsRenderer) + push(PropertyReturnTypeRenderer) + push(PropertyAccessorsRenderer) + push(LocalVariableRenderer) + push(JavaFieldRenderer) + push(BackingFieldRenderer) + push(EnumEntryRenderer) + + // Classifiers. + push(NamedClassRenderer) + push(AnonymousObjectRenderer) + push(TypeAliasRenderer) + push(PrimaryConstructorRenderer) + push(ClassModifiersRenderer) + push(ClassAnnotationsRenderer) + push(SupertypeListRenderer) + push(SupertypeRenderer) + push(ClassBodyRenderer) + + // Packages. + push(PackageRenderer) +} + +// ------------------------------------------------------------------------------------------------- +// Shared helpers. +// ------------------------------------------------------------------------------------------------- + +context(output: KaRenderingOutput) +private fun keyword(text: String) { + output.append(text, KaTextAttribute.Keyword).space() +} + +/** + * The visibility of [symbol] as it is written in source code. A primary constructor of an enum class or an object is implicitly private, + * so its visibility is reported as public and is not rendered. + */ +context(session: KaSession) +private fun effectiveVisibility(symbol: KaDeclarationSymbol): KaSymbolVisibility { + if (symbol is KaConstructorSymbol && symbol.isPrimary) { + val containingClass = symbol.containingDeclaration as? KaClassSymbol + if (containingClass != null && (containingClass.classKind == KaClassKind.ENUM_CLASS || containingClass.classKind.isObject)) { + return KaSymbolVisibility.PUBLIC + } + } + return symbol.visibility +} + +context(output: KaRenderingOutput) +private fun identifier(name: String, symbol: KaSymbol) { + output.append(name, setOf(KaTextAttribute.Identifier, KaTextAttribute.Symbol(symbol))) +} + +context(output: KaRenderingOutput) +private fun identifier(name: Name, symbol: KaSymbol) { + // `Name.render()` wraps keywords and names with special characters (e.g. `<set-?>`) in backticks. + identifier(name.render(), symbol) +} + +/** + * Renders a package fully-qualified name segment by segment (e.g. `org.example`), linking each segment to the [KaPackageSymbol] of the + * package it forms, when that package can be resolved. + */ +context(session: KaSession, output: KaRenderingOutput) +private fun renderPackageQualifier(packageFqName: FqName) { + var current = FqName.ROOT + packageFqName.pathSegments().forEachIndexed { index, segment -> + if (index > 0) output.append(".", KaTextAttribute.Punctuation) + current = current.child(segment) + val packageSymbol = findPackage(current) + if (packageSymbol != null) { + identifier(segment, packageSymbol) + } else { + output.append(segment.render(), KaTextAttribute.Identifier) + } + } +} + +/** + * Renders a receiver type of extension callable or a function type, wrapping it in parentheses when required by the Kotlin grammar + * (function types, definitely non-nullable types, and nullable types). + */ +context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) +private fun renderReceiverType(type: KaType) { + val parenthesized = type is KaFunctionType || type is KaDefinitelyNotNullType || type.isMarkedNullable + if (parenthesized) output.append("(", KaTextAttribute.GroupStart) + render(type, KaPiece.Type) + if (parenthesized) output.append(")", KaTextAttribute.GroupEnd) +} + +// ------------------------------------------------------------------------------------------------- +// Symbols: top-level dispatcher. +// ------------------------------------------------------------------------------------------------- + +private object SymbolRenderer : KaPieceRenderer<KaSymbol>(KaPiece.Symbol) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaSymbol, next: () -> Unit): Boolean { + when (value) { + is KaNamedFunctionSymbol -> render(value, KaPiece.NamedFunction) + is KaConstructorSymbol -> render(value, KaPiece.Constructor) + is KaPropertyAccessorSymbol -> render(value, KaPiece.PropertyAccessor) + is KaSamConstructorSymbol -> render(value, KaPiece.Function) + is KaAnonymousFunctionSymbol -> render(value, KaPiece.Function) + is KaPropertySymbol -> render(value, KaPiece.Property) + is KaLocalVariableSymbol -> render(value, KaPiece.LocalVariable) + is KaJavaFieldSymbol -> render(value, KaPiece.JavaField) + is KaBackingFieldSymbol -> render(value, KaPiece.BackingField) + is KaEnumEntrySymbol -> render(value, KaPiece.EnumEntry) + is KaValueParameterSymbol -> render(value, KaPiece.ValueParameter) + is KaContextParameterSymbol -> render(value, KaPiece.ContextParameter) + is KaReceiverParameterSymbol -> render(value, KaPiece.FunctionReceiver) + is KaNamedClassSymbol -> render(value, KaPiece.NamedClass) + is KaAnonymousObjectSymbol -> render(value, KaPiece.AnonymousObject) + is KaTypeAliasSymbol -> render(value, KaPiece.TypeAlias) + is KaTypeParameterSymbol -> render(value, KaPiece.TypeParameter) + is KaPackageSymbol -> render(value, KaPiece.Package) + else -> render(value, KaPiece.SymbolName) + } + return true + } +} + +private object SymbolNameRenderer : KaPieceRenderer<KaSymbol>(KaPiece.SymbolName) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaSymbol, next: () -> Unit): Boolean { + when (value) { + // A package is declared by its fully qualified name, e.g. `package org.example`. + is KaPackageSymbol -> identifier(value.fqName.render(), value) + is KaNamedSymbol -> identifier(value.name, value) + else -> output.append("<symbol>", KaTextAttribute.Identifier) + } + return true + } +} + +private object SymbolModifiersRenderer : KaPieceRenderer<KaDeclarationSymbol>(KaPiece.SymbolModifiers) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaDeclarationSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Visibility) + if (value.isExpect) keyword("expect") + if (value.isActual) keyword("actual") + if (value.isExternal) keyword("external") + return true + } +} + +private object VisibilityRenderer : KaPieceRenderer<KaDeclarationSymbol>(KaPiece.Visibility) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaDeclarationSymbol, next: () -> Unit): Boolean { + val text = when (effectiveVisibility(value)) { + KaSymbolVisibility.PRIVATE -> "private" + KaSymbolVisibility.PROTECTED -> "protected" + KaSymbolVisibility.INTERNAL -> "internal" + else -> return true + } + keyword(text) + return true + } +} + +private object ModalityRenderer : KaPieceRenderer<KaDeclarationSymbol>(KaPiece.Modality) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaDeclarationSymbol, next: () -> Unit): Boolean { + if (!shouldRenderModality(value)) return true + + val text = when (value.modality) { + KaSymbolModality.ABSTRACT -> "abstract" + KaSymbolModality.OPEN -> "open" + KaSymbolModality.SEALED -> "sealed" + KaSymbolModality.FINAL -> return true + } + keyword(text) + return true + } + + /** + * Whether the modality of [symbol] is meaningful: + * - only plain classes carry an explicit modality; interfaces, objects, enums, and annotation classes have an implicit one; + * - interface members are implicitly `abstract`/`open`, so their modality is never rendered; + * - `open` is redundant when the containing class is `final` (a final class or object cannot be inherited from). + */ + context(session: KaSession) + private fun shouldRenderModality(symbol: KaDeclarationSymbol): Boolean { + if (symbol is KaClassSymbol) { + return symbol.classKind == KaClassKind.CLASS + } + + if (symbol !is KaCallableSymbol) return true + + val containingClass = symbol.containingDeclaration as? KaClassSymbol + if (containingClass?.classKind == KaClassKind.INTERFACE) return false + if (symbol.modality == KaSymbolModality.OPEN && containingClass?.modality == KaSymbolModality.FINAL) return false + + return true + } +} + +private object AnnotationsRenderer : KaPieceRenderer<KaAnnotated>(KaPiece.Annotations) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaAnnotated, next: () -> Unit): Boolean { + val annotations = value.annotations + if (annotations.isEmpty()) return true + + val onNewLine = context.valueFor(KaRenderingOption.AnnotationsOnNewLine)(session, context, value) + for (annotation in annotations) { + render(annotation, KaPiece.Annotation) + if (onNewLine) output.newLine() else output.space() + } + return true + } +} + +private object AnnotationRenderer : KaPieceRenderer<KaAnnotation>(KaPiece.Annotation) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaAnnotation, next: () -> Unit): Boolean { + output.append("@", KaTextAttribute.Punctuation) + renderAnnotationName(value.classId) + render(value, KaPiece.AnnotationValues) + return true + } + + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + private fun renderAnnotationName(classId: ClassId?) { + if (classId == null) { + output.append("ERROR", KaTextAttribute.Identifier) + return + } + + val qualification = context.valueFor(KaRenderingOption.ClassTypeQualification) + + if (qualification == KaClassTypeQualification.FULLY_QUALIFIED) { + val packageFqName = classId.packageFqName + if (!packageFqName.isRoot && packageFqName != CallableId.PACKAGE_FQ_NAME_FOR_LOCAL) { + renderPackageQualifier(packageFqName) + output.append(".", KaTextAttribute.Punctuation) + } + } + + // `SIMPLE` keeps only the innermost segment; the other modes render all outer classifiers. + val segments = when (qualification) { + KaClassTypeQualification.SIMPLE -> listOf(classId.shortClassName) + else -> classId.relativeClassName.pathSegments() + } + + segments.forEachIndexed { index, segment -> + if (index > 0) output.append(".", KaTextAttribute.Punctuation) + output.append(segment.render(), KaTextAttribute.Identifier) + } + } +} + +private object AnnotationValuesRenderer : KaPieceRenderer<KaAnnotation>(KaPiece.AnnotationValues) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaAnnotation, next: () -> Unit): Boolean { + val arguments = value.arguments + if (arguments.isEmpty()) return true + + output.append("(", KaTextAttribute.GroupStart) + arguments.forEachIndexed { index, argument -> + if (index > 0) output.append(", ", KaTextAttribute.Punctuation) + output.append(argument.name.asString(), KaTextAttribute.Identifier) + output.append(" = ", KaTextAttribute.Punctuation) + render(argument.expression, KaPiece.AnnotationValue) + } + output.append(")", KaTextAttribute.GroupEnd) + return true + } +} + +private object AnnotationValueRenderer : KaPieceRenderer<KaAnnotationValue>(KaPiece.AnnotationValue) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaAnnotationValue, next: () -> Unit): Boolean { + when (value) { + is KaAnnotationValue.ConstantValue -> render(value.value, KaPiece.ConstantValue) + is KaAnnotationValue.EnumEntryValue -> { + val callableId = value.callableId + if (callableId != null) { + callableId.className?.let { className -> + output.append(className.shortName().asString(), KaTextAttribute.Identifier) + output.append(".", KaTextAttribute.Punctuation) + } + output.append(callableId.callableName.asString(), KaTextAttribute.Identifier) + } else { + output.append("ERROR", KaTextAttribute.Identifier) + } + } + is KaAnnotationValue.ClassLiteralValue -> { + render(value.type, KaPiece.Type) + output.append("::", KaTextAttribute.Punctuation) + output.append("class", KaTextAttribute.Keyword) + } + is KaAnnotationValue.NestedAnnotationValue -> { + render(value.annotation, KaPiece.Annotation) + } + is KaAnnotationValue.ArrayValue -> { + output.append("[", KaTextAttribute.GroupStart) + value.values.forEachIndexed { index, element -> + if (index > 0) output.append(", ", KaTextAttribute.Punctuation) + render(element, KaPiece.AnnotationValue) + } + output.append("]", KaTextAttribute.GroupEnd) + } + is KaAnnotationValue.UnsupportedValue -> { + output.append("ERROR", KaTextAttribute.Identifier) + } + } + return true + } +} + +private object ConstantValueRenderer : KaPieceRenderer<KaConstantValue>(KaPiece.ConstantValue) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaConstantValue, next: () -> Unit): Boolean { + when (value) { + is KaConstantValue.NullValue, is KaConstantValue.BooleanValue -> { + output.append(value.render(), KaTextAttribute.Keyword) + } + is KaConstantValue.StringValue, is KaConstantValue.CharValue -> { + output.append(value.render(), KaTextAttribute.StringLiteral) + } + is KaConstantValue.ErrorValue -> { + output.append("ERROR", KaTextAttribute.Identifier) + } + else -> { + output.append(value.render(), KaTextAttribute.NumberLiteral) + } + } + return true + } +} + +// ------------------------------------------------------------------------------------------------- +// Functions. +// ------------------------------------------------------------------------------------------------- + +private object NamedFunctionRenderer : KaPieceRenderer<KaNamedFunctionSymbol>(KaPiece.NamedFunction) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaNamedFunctionSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.FunctionAnnotations) + render(value, KaPiece.CallableContextParameterList) + render(value, KaPiece.FunctionModifiers) + keyword("fun") + + if (value.typeParameters.isNotEmpty()) { + render(value, KaPiece.CallableTypeParameterList) + output.space() + } + + value.receiverParameter?.let { render(it, KaPiece.FunctionReceiver) } + + render(value, KaPiece.SymbolName) + + render(value, KaPiece.FunctionValueParameterList) + + render(value, KaPiece.NamedFunctionReturnType) + + render(value, KaPiece.CallableWhereClause) + + return true + } +} + +/** Generic function renderer used for anonymous functions and SAM constructors. */ +private object FunctionRenderer : KaPieceRenderer<KaFunctionSymbol>(KaPiece.Function) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaFunctionSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.FunctionModifiers) + keyword("fun") + + value.receiverParameter?.let { render(it, KaPiece.FunctionReceiver) } + + if (value is KaNamedSymbol) { + render(value, KaPiece.SymbolName) + } + + render(value, KaPiece.FunctionValueParameterList) + + render(value, KaPiece.FunctionReturnType) + return true + } +} + +private object ConstructorRenderer : KaPieceRenderer<KaConstructorSymbol>(KaPiece.Constructor) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaConstructorSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Annotations) + render(value, KaPiece.Visibility) + output.append("constructor", KaTextAttribute.Keyword) + render(value, KaPiece.FunctionValueParameterList) + return true + } +} + +private object PrimaryConstructorRenderer : KaPieceRenderer<KaConstructorSymbol>(KaPiece.PrimaryConstructor) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaConstructorSymbol, next: () -> Unit): Boolean { + // The `constructor` keyword is rendered when the primary constructor carries its own annotations or an explicit, + // non-default (non-public) visibility. + if (value.annotations.isNotEmpty() || effectiveVisibility(value) != KaSymbolVisibility.PUBLIC) { + output.space() + render(value, KaPiece.Annotations) + render(value, KaPiece.Visibility) + output.append("constructor", KaTextAttribute.Keyword) + } + + val parameters = value.valueParameters + if (parameters.isNotEmpty()) { + output.append("(", KaTextAttribute.GroupStart) + parameters.forEachIndexed { index, parameter -> + if (index > 0) output.append(", ", KaTextAttribute.Punctuation) + renderSignatureParameter(parameter) + } + output.append(")", KaTextAttribute.GroupEnd) + } + return true + } + + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + private fun renderSignatureParameter(parameter: KaValueParameterSymbol) { + if (parameter.isVararg) keyword("vararg") + render(parameter, KaPiece.SymbolName) + output.append(": ", KaTextAttribute.Punctuation) + render(parameter, KaPiece.ValueParameterType) + } +} + +private object PropertyAccessorRenderer : KaPieceRenderer<KaPropertyAccessorSymbol>(KaPiece.PropertyAccessor) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaPropertyAccessorSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Visibility) + val isGetter = value is KaPropertyGetterSymbol + output.append(if (isGetter) "get" else "set", KaTextAttribute.Keyword) + output.append("(", KaTextAttribute.GroupStart) + if (value is KaPropertySetterSymbol) { + render(value.parameter, KaPiece.ValueParameter) + } + output.append(")", KaTextAttribute.GroupEnd) + return true + } +} + +private object FunctionModifiersRenderer : KaPieceRenderer<KaFunctionSymbol>(KaPiece.FunctionModifiers) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaFunctionSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.SymbolModifiers) + render(value, KaPiece.Modality) + if (value is KaNamedFunctionSymbol) { + if (value.isOverride) keyword("override") + if (value.isTailRec) keyword("tailrec") + if (value.isSuspend) keyword("suspend") + if (value.isInline) keyword("inline") + if (value.isInfix) keyword("infix") + if (value.isOperator) keyword("operator") + } + return true + } +} + +private object FunctionAnnotationsRenderer : KaPieceRenderer<KaFunctionSymbol>(KaPiece.FunctionAnnotations) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaFunctionSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Annotations) + return true + } +} + +private object FunctionReceiverRenderer : KaPieceRenderer<KaParameterSymbol>(KaPiece.FunctionReceiver) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaParameterSymbol, next: () -> Unit): Boolean { + renderReceiverType(value.returnType) + output.append(".", KaTextAttribute.Punctuation) + return true + } +} + + +private object FunctionValueParameterListRenderer : KaPieceRenderer<KaFunctionSymbol>(KaPiece.FunctionValueParameterList) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaFunctionSymbol, next: () -> Unit): Boolean { + render(value.valueParameters, KaPiece.ValueParameterList) + return true + } +} + +private object FunctionReturnTypeRenderer : KaPieceRenderer<KaFunctionSymbol>(KaPiece.FunctionReturnType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaFunctionSymbol, next: () -> Unit): Boolean { + output.append(": ", KaTextAttribute.Punctuation) + render(value.returnType, KaPiece.Type) + return true + } +} + +private object NamedFunctionReturnTypeRenderer : KaPieceRenderer<KaNamedFunctionSymbol>(KaPiece.NamedFunctionReturnType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaNamedFunctionSymbol, next: () -> Unit): Boolean { + // An implicit `Unit` return type is omitted in source code. A nullable or annotated `Unit` is not implicit, so it is rendered. + val returnType = value.returnType + val isImplicitUnit = returnType.classId == StandardClassIds.Unit && + !returnType.isNullable && + returnType.annotations.isEmpty() + + if (!isImplicitUnit) { + render(value, KaPiece.FunctionReturnType) + } + return true + } +} + +// ------------------------------------------------------------------------------------------------- +// Value parameters. +// ------------------------------------------------------------------------------------------------- + +private object ValueParameterListRenderer : KaPieceRenderer<List<KaValueParameterSymbol>>(KaPiece.ValueParameterList) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: List<KaValueParameterSymbol>, next: () -> Unit): Boolean { + output.append("(", KaTextAttribute.GroupStart) + value.forEachIndexed { index, parameter -> + if (index > 0) output.append(", ", KaTextAttribute.Punctuation) + render(parameter, KaPiece.ValueParameter) + } + output.append(")", KaTextAttribute.GroupEnd) + return true + } +} + +private object ValueParameterRenderer : KaPieceRenderer<KaValueParameterSymbol>(KaPiece.ValueParameter) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaValueParameterSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.ValueParameterAnnotations) + + if (value.isVararg) keyword("vararg") + if (value.isCrossinline) keyword("crossinline") + if (value.isNoinline) keyword("noinline") + + value.primaryConstructorProperty?.let { property -> + keyword(if (property.isVal) "val" else "var") + } + + render(value, KaPiece.SymbolName) + output.append(": ", KaTextAttribute.Punctuation) + render(value, KaPiece.ValueParameterType) + render(value, KaPiece.ValueParameterDefaultValue) + return true + } +} + +private object ValueParameterAnnotationsRenderer : KaPieceRenderer<KaValueParameterSymbol>(KaPiece.ValueParameterAnnotations) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaValueParameterSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Annotations) + return true + } +} + +private object ValueParameterTypeRenderer : KaPieceRenderer<KaValueParameterSymbol>(KaPiece.ValueParameterType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaValueParameterSymbol, next: () -> Unit): Boolean { + render(value.returnType, KaPiece.Type) + return true + } +} + +private object ValueParameterDefaultValueRenderer : KaPieceRenderer<KaValueParameterSymbol>(KaPiece.ValueParameterDefaultValue) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaValueParameterSymbol, next: () -> Unit): Boolean { + if (!value.hasDefaultValue) return true + + // The default value expression is not part of the symbol, so it is rendered as a placeholder. + output.append(" = ", KaTextAttribute.Punctuation) + output.append("...", KaTextAttribute.Punctuation) + return true + } +} + +// ------------------------------------------------------------------------------------------------- +// Context parameters. +// ------------------------------------------------------------------------------------------------- + +private object ContextParameterListRenderer : KaPieceRenderer<List<KaContextParameterSymbol>>(KaPiece.ContextParameterList) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: List<KaContextParameterSymbol>, next: () -> Unit): Boolean { + output.append("context", KaTextAttribute.Keyword) + output.append("(", KaTextAttribute.GroupStart) + value.forEachIndexed { index, parameter -> + if (index > 0) output.append(", ", KaTextAttribute.Punctuation) + render(parameter, KaPiece.ContextParameter) + } + output.append(")", KaTextAttribute.GroupEnd) + val onNewLine = context.valueFor(KaRenderingOption.ContextReceiversOnNewLine)(session, context, value) + if (onNewLine) output.newLine() else output.space() + return true + } +} + +private object CallableContextParameterListRenderer : KaPieceRenderer<KaCallableSymbol>(KaPiece.CallableContextParameterList) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaCallableSymbol, next: () -> Unit): Boolean { + val contextParameters = value.contextParameters + if (contextParameters.isNotEmpty()) { + render(contextParameters, KaPiece.ContextParameterList) + } + return true + } +} + +private object ContextParameterRenderer : KaPieceRenderer<KaContextParameterSymbol>(KaPiece.ContextParameter) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaContextParameterSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.ContextParameterAnnotations) + render(value, KaPiece.SymbolName) + output.append(": ", KaTextAttribute.Punctuation) + render(value, KaPiece.ContextParameterType) + return true + } +} + +private object ContextParameterAnnotationsRenderer : KaPieceRenderer<KaContextParameterSymbol>(KaPiece.ContextParameterAnnotations) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaContextParameterSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Annotations) + return true + } +} + +private object ContextParameterTypeRenderer : KaPieceRenderer<KaContextParameterSymbol>(KaPiece.ContextParameterType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaContextParameterSymbol, next: () -> Unit): Boolean { + render(value.returnType, KaPiece.Type) + return true + } +} + +// ------------------------------------------------------------------------------------------------- +// Properties and variables. +// ------------------------------------------------------------------------------------------------- + +private object PropertyRenderer : KaPieceRenderer<KaPropertySymbol>(KaPiece.Property) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaPropertySymbol, next: () -> Unit): Boolean { + render(value, KaPiece.PropertyAnnotations) + render(value, KaPiece.CallableContextParameterList) + render(value, KaPiece.PropertyModifiers) + keyword(if (value.isVal) "val" else "var") + + if (value.typeParameters.isNotEmpty()) { + render(value, KaPiece.CallableTypeParameterList) + output.space() + } + + value.receiverParameter?.let { render(it, KaPiece.FunctionReceiver) } + + render(value, KaPiece.SymbolName) + output.append(": ", KaTextAttribute.Punctuation) + render(value, KaPiece.PropertyReturnType) + + render(value, KaPiece.CallableWhereClause) + + render(value, KaPiece.PropertyAccessors) + return true + } +} + +private object PropertyAccessorsRenderer : KaPieceRenderer<KaPropertySymbol>(KaPiece.PropertyAccessors) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaPropertySymbol, next: () -> Unit): Boolean { + val getter = value.getter?.takeIf { it.isNotDefault } + val setter = value.setter?.takeIf { it.isNotDefault } + if (getter == null && setter == null) return true + + output.indent() + getter?.let { output.newLine(); render(it, KaPiece.PropertyAccessor) } + setter?.let { output.newLine(); render(it, KaPiece.PropertyAccessor) } + output.unindent() + return true + } +} + +private object PropertyModifiersRenderer : KaPieceRenderer<KaPropertySymbol>(KaPiece.PropertyModifiers) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaPropertySymbol, next: () -> Unit): Boolean { + render(value, KaPiece.SymbolModifiers) + render(value, KaPiece.Modality) + if (value.isOverride) keyword("override") + if (value is KaKotlinPropertySymbol) { + if (value.isConst) keyword("const") + if (value.isLateInit) keyword("lateinit") + } + return true + } +} + +private object PropertyAnnotationsRenderer : KaPieceRenderer<KaPropertySymbol>(KaPiece.PropertyAnnotations) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaPropertySymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Annotations) + return true + } +} + +private object PropertyReturnTypeRenderer : KaPieceRenderer<KaPropertySymbol>(KaPiece.PropertyReturnType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaPropertySymbol, next: () -> Unit): Boolean { + render(value.returnType, KaPiece.Type) + return true + } +} + +private object LocalVariableRenderer : KaPieceRenderer<KaLocalVariableSymbol>(KaPiece.LocalVariable) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaLocalVariableSymbol, next: () -> Unit): Boolean { + if (value.isLateInit) keyword("lateinit") + keyword(if (value.isVal) "val" else "var") + render(value, KaPiece.SymbolName) + output.append(": ", KaTextAttribute.Punctuation) + render(value.returnType, KaPiece.Type) + return true + } +} + +private object JavaFieldRenderer : KaPieceRenderer<KaJavaFieldSymbol>(KaPiece.JavaField) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaJavaFieldSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Visibility) + if (value.isStatic) keyword("static") + keyword(if (value.isVal) "val" else "var") + render(value, KaPiece.SymbolName) + output.append(": ", KaTextAttribute.Punctuation) + render(value.returnType, KaPiece.Type) + return true + } +} + +private object BackingFieldRenderer : KaPieceRenderer<KaBackingFieldSymbol>(KaPiece.BackingField) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaBackingFieldSymbol, next: () -> Unit): Boolean { + output.append("field", KaTextAttribute.Keyword) + output.append(": ", KaTextAttribute.Punctuation) + render(value.returnType, KaPiece.Type) + return true + } +} + +private object EnumEntryRenderer : KaPieceRenderer<KaEnumEntrySymbol>(KaPiece.EnumEntry) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaEnumEntrySymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Annotations) + render(value, KaPiece.SymbolName) + // Render the anonymous object body of the entry, e.g. `ENTRY { override fun foo() }`. + value.initializer?.let { initializer -> + render(initializer, KaPiece.ClassBody) + } + return true + } +} + +// ------------------------------------------------------------------------------------------------- +// Classifiers. +// ------------------------------------------------------------------------------------------------- + +private object NamedClassRenderer : KaPieceRenderer<KaNamedClassSymbol>(KaPiece.NamedClass) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaNamedClassSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.ClassAnnotations) + render(value, KaPiece.ClassModifiers) + + val classKeyword = when (value.classKind) { + KaClassKind.INTERFACE -> "interface" + KaClassKind.OBJECT, KaClassKind.COMPANION_OBJECT -> "object" + else -> "class" + } + output.append(classKeyword, KaTextAttribute.Keyword) + + val isDefaultCompanion = value.classKind == KaClassKind.COMPANION_OBJECT && + value.name == SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT + + if (!isDefaultCompanion) { + output.space() + render(value, KaPiece.SymbolName) + } + + if (value.typeParameters.isNotEmpty()) { + render(value, KaPiece.ClassifierTypeParameterList) + } + + if (context.valueFor(KaRenderingOption.PrimaryConstructorInClassHeader)) { + value.primaryConstructor?.let { render(it, KaPiece.PrimaryConstructor) } + } + + render(value, KaPiece.ClassifierWhereClause) + render(value, KaPiece.SupertypeList) + render(value, KaPiece.ClassBody) + return true + } + + context(session: KaSession) + private val KaClassSymbol.primaryConstructor: KaConstructorSymbol? + get() = declaredMemberScope.constructors.firstOrNull { it.isPrimary } +} + +private object AnonymousObjectRenderer : KaPieceRenderer<KaAnonymousObjectSymbol>(KaPiece.AnonymousObject) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaAnonymousObjectSymbol, next: () -> Unit): Boolean { + output.append("object", KaTextAttribute.Keyword) + render(value, KaPiece.SupertypeList) + render(value, KaPiece.ClassBody) + return true + } +} + +private object TypeAliasRenderer : KaPieceRenderer<KaTypeAliasSymbol>(KaPiece.TypeAlias) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaTypeAliasSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.SymbolModifiers) + keyword("typealias") + render(value, KaPiece.SymbolName) + if (value.typeParameters.isNotEmpty()) { + render(value, KaPiece.ClassifierTypeParameterList) + } + output.append(" = ", KaTextAttribute.Punctuation) + render(value.expandedType, KaPiece.Type) + return true + } +} + +private object ClassModifiersRenderer : KaPieceRenderer<KaNamedClassSymbol>(KaPiece.ClassModifiers) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaNamedClassSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.SymbolModifiers) + render(value, KaPiece.Modality) + + when (value.classKind) { + KaClassKind.COMPANION_OBJECT -> keyword("companion") + KaClassKind.ENUM_CLASS -> keyword("enum") + KaClassKind.ANNOTATION_CLASS -> keyword("annotation") + else -> {} + } + + if (value.isInner) keyword("inner") + if (value.isData) keyword("data") + if (value.isInline) keyword("value") + if (value.isFun) keyword("fun") + return true + } +} + +private object ClassAnnotationsRenderer : KaPieceRenderer<KaNamedClassSymbol>(KaPiece.ClassAnnotations) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaNamedClassSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Annotations) + return true + } +} + + +private object SupertypeListRenderer : KaPieceRenderer<KaClassSymbol>(KaPiece.SupertypeList) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaClassSymbol, next: () -> Unit): Boolean { + val superTypes = value.superTypes.filter { !it.isTrivialSuperType() } + if (superTypes.isNotEmpty()) { + output.append(" : ", KaTextAttribute.Punctuation) + superTypes.forEachIndexed { index, superType -> + if (index > 0) output.append(", ", KaTextAttribute.Punctuation) + render(superType, KaPiece.Supertype) + } + } + return true + } + + /** `kotlin.Any` and the implicit `kotlin.Enum`/`kotlin.Annotation` supertypes are omitted from super type lists. */ + context(session: KaSession) + private fun KaType.isTrivialSuperType(): Boolean { + return when (classId) { + StandardClassIds.Any, StandardClassIds.Enum, StandardClassIds.Annotation -> true + else -> false + } + } +} + +private object SupertypeRenderer : KaPieceRenderer<KaType>(KaPiece.Supertype) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaType, next: () -> Unit): Boolean { + render(value, KaPiece.Type) + + // A class supertype is rendered with constructor-call syntax, e.g. `Base()`; interfaces are not. + if (value.expandedSymbol?.classKind?.isClass == true) { + output.append("(", KaTextAttribute.GroupStart) + output.append(")", KaTextAttribute.GroupEnd) + } + return true + } +} + +private object ClassBodyRenderer : KaPieceRenderer<KaClassSymbol>(KaPiece.ClassBody) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaClassSymbol, next: () -> Unit): Boolean { + val ordering = context.valueFor(KaRenderingOption.ClassMemberOrdering) + + val members = context.valueFor(KaRenderingOption.ClassMembers)(session, context, value) + .sortedWith { first, second -> ordering(session, context, first, second) } + + if (members.isEmpty()) { + return true + } + + val extraLineBetweenMembers = context.valueFor(KaRenderingOption.ExtraLineBetweenMembers) + + output.space().append("{", KaTextAttribute.GroupStart).indent() + + val enumEntries = members.filterIsInstance<KaEnumEntrySymbol>() + val otherMembers = members.filter { it !is KaEnumEntrySymbol } + + // Enum entries come first, separated by commas, and are terminated with a semicolon when other declarations follow. + enumEntries.forEachIndexed { index, entry -> + output.newLine() + render(entry, KaPiece.Symbol) + when { + index < enumEntries.lastIndex -> output.append(",", KaTextAttribute.Punctuation) + otherMembers.isNotEmpty() -> output.append(";", KaTextAttribute.Punctuation) + } + } + + otherMembers.forEachIndexed { index, member -> + // Separate members (and the enum entry block from the following declarations) with a blank line when requested. + if (extraLineBetweenMembers && (enumEntries.isNotEmpty() || index > 0)) { + output.newLine() + } + output.newLine() + render(member, KaPiece.Symbol) + } + + output.unindent().newLine().append("}", KaTextAttribute.GroupEnd) + return true + } +} + +private object PackageRenderer : KaPieceRenderer<KaPackageSymbol>(KaPiece.Package) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaPackageSymbol, next: () -> Unit): Boolean { + keyword("package") + render(value, KaPiece.SymbolName) + return true + } +} + +// ------------------------------------------------------------------------------------------------- +// Type parameters. +// ------------------------------------------------------------------------------------------------- + +private object TypeParameterListRenderer : KaPieceRenderer<List<KaTypeParameterSymbol>>(KaPiece.TypeParameterList) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: List<KaTypeParameterSymbol>, next: () -> Unit): Boolean { + output.append("<", KaTextAttribute.GroupStart) + value.forEachIndexed { index, typeParameter -> + if (index > 0) output.append(", ", KaTextAttribute.Punctuation) + render(typeParameter, KaPiece.TypeParameter) + } + output.append(">", KaTextAttribute.GroupEnd) + return true + } +} + +private object CallableTypeParameterListRenderer : KaPieceRenderer<KaCallableSymbol>(KaPiece.CallableTypeParameterList) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaCallableSymbol, next: () -> Unit): Boolean { + render(value.typeParameters, KaPiece.TypeParameterList) + return true + } +} + +private object ClassifierTypeParameterListRenderer : KaPieceRenderer<KaClassLikeSymbol>(KaPiece.ClassifierTypeParameterList) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaClassLikeSymbol, next: () -> Unit): Boolean { + render(value.typeParameters, KaPiece.TypeParameterList) + return true + } +} + +private object TypeParameterRenderer : KaPieceRenderer<KaTypeParameterSymbol>(KaPiece.TypeParameter) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaTypeParameterSymbol, next: () -> Unit): Boolean { + render(value, KaPiece.Annotations) + if (value.isReified) keyword("reified") + if (value.variance != Variance.INVARIANT) keyword(value.variance.label) + + render(value, KaPiece.SymbolName) + + // A single upper bound is rendered inline; multiple bounds are deferred to a `where` clause. + if (value.upperBounds.size == 1) { + output.append(" : ", KaTextAttribute.Punctuation) + render(value.upperBounds.single(), KaPiece.Type) + } + return true + } +} + +private object WhereClauseRenderer : KaPieceRenderer<List<KaTypeParameterSymbol>>(KaPiece.WhereClause) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: List<KaTypeParameterSymbol>, next: () -> Unit): Boolean { + // Only type parameters with more than one upper bound contribute to the `where` clause; single bounds are rendered inline. + val constrainedTypeParameters = value.filter { it.upperBounds.size > 1 } + if (constrainedTypeParameters.isEmpty()) return true + + output.space() + keyword("where") + var first = true + for (typeParameter in constrainedTypeParameters) { + for (bound in typeParameter.upperBounds) { + if (!first) output.append(", ", KaTextAttribute.Punctuation) + first = false + render(typeParameter, KaPiece.SymbolName) + output.append(" : ", KaTextAttribute.Punctuation) + render(bound, KaPiece.Type) + } + } + + return true + } +} + +private object CallableWhereClauseRenderer : KaPieceRenderer<KaCallableSymbol>(KaPiece.CallableWhereClause) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaCallableSymbol, next: () -> Unit): Boolean { + render(value.typeParameters, KaPiece.WhereClause) + return true + } +} + +private object ClassifierWhereClauseRenderer : KaPieceRenderer<KaClassLikeSymbol>(KaPiece.ClassifierWhereClause) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaClassLikeSymbol, next: () -> Unit): Boolean { + render(value.typeParameters, KaPiece.WhereClause) + return true + } +} + +// ------------------------------------------------------------------------------------------------- +// Types: top-level dispatcher. +// ------------------------------------------------------------------------------------------------- + +private object TypeRenderer : KaPieceRenderer<KaType>(KaPiece.Type) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaType, next: () -> Unit): Boolean { + val type = effectiveType(value) + + if (type.annotations.isNotEmpty()) { + render(type, KaPiece.TypeAnnotations) + } + + // If the type is a type-alias application, render the alias (e.g. `WithGeneric<Double>`) rather than its expansion. + val abbreviation = type.abbreviation + if (abbreviation != null) { + render(abbreviation, KaPiece.ClassType) + return true + } + + when (type) { + is KaFunctionType -> render(type, KaPiece.FunctionType) + is KaErrorType -> render(type, KaPiece.ErrorType) + is KaClassType -> render(type, KaPiece.ClassType) + is KaTypeParameterType -> render(type, KaPiece.TypeParameterType) + is KaCapturedType -> render(type, KaPiece.CapturedType) + is KaDefinitelyNotNullType -> render(type, KaPiece.DefinitelyNotNullType) + is KaFlexibleType -> render(type, KaPiece.FlexibleType) + is KaIntersectionType -> render(type, KaPiece.IntersectionType) + is KaDynamicType -> render(type, KaPiece.DynamicType) + else -> output.append("ERROR", KaTextAttribute.Identifier) + } + return true + } + + /** + * Applies [KaRenderingOption.TypeTransformation] and [KaRenderingOption.TypeApproximation] to [type]. + * + * Every rendered type passes through [KaPiece.Type], so applying the options here covers all of them, including nested ones such as + * type arguments. Approximation of an outer type already makes its arguments denotable, so the nested applications are no-ops. + */ + @OptIn(KaExperimentalApi::class) + context(session: KaSession, context: KaRenderingContext) + private fun effectiveType(type: KaType): KaType { + val transformed = context.valueFor(KaRenderingOption.TypeTransformation)(session, context, type) + + return when (context.valueFor(KaRenderingOption.TypeApproximation)) { + KaTypeApproximation.NONE -> transformed + KaTypeApproximation.TO_DENOTABLE_SUBTYPE -> transformed.approximateToDenotableSubtypeOrSelf() + KaTypeApproximation.TO_DENOTABLE_SUPERTYPE -> transformed.approximateToDenotableSupertypeOrSelf(allowLocalDenotableTypes = true) + } + } +} + +private object TypeAnnotationsRenderer : KaPieceRenderer<KaType>(KaPiece.TypeAnnotations) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaType, next: () -> Unit): Boolean { + render(value, KaPiece.Annotations) + return true + } +} + +private object TypeNullabilityRenderer : KaPieceRenderer<KaType>(KaPiece.TypeNullability) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaType, next: () -> Unit): Boolean { + if (value.isMarkedNullable) output.append("?", KaTextAttribute.Punctuation) + return true + } +} + +private object ClassTypeRenderer : KaPieceRenderer<KaClassType>(KaPiece.ClassType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaClassType, next: () -> Unit): Boolean { + val qualification = context.valueFor(KaRenderingOption.ClassTypeQualification) + val qualifiers = value.qualifiers + + if (qualifiers.isEmpty()) { + // Fallback for class types that expose no explicit qualifier segments. + identifier(value.classId.shortClassName, value.symbol) + if (value.typeArguments.isNotEmpty()) { + render(value.typeArguments, KaPiece.TypeArgumentList) + } + } else { + if (qualification == KaClassTypeQualification.FULLY_QUALIFIED) { + val packageFqName = value.classId.packageFqName + if (!packageFqName.isRoot && packageFqName != CallableId.PACKAGE_FQ_NAME_FOR_LOCAL) { + renderPackageQualifier(packageFqName) + output.append(".", KaTextAttribute.Punctuation) + } + } + + // `SIMPLE` keeps only the innermost segment; the other modes render all outer classifiers. + val renderedQualifiers = when (qualification) { + KaClassTypeQualification.SIMPLE -> listOf(qualifiers.last()) + else -> qualifiers + } + + renderedQualifiers.forEachIndexed { index, qualifier -> + if (index > 0) output.append(".", KaTextAttribute.Punctuation) + identifier(qualifier.name, qualifier.symbol) + if (qualifier.typeArguments.isNotEmpty()) { + render(qualifier.typeArguments, KaPiece.TypeArgumentList) + } + } + } + + render(value, KaPiece.TypeNullability) + return true + } +} + +private object FunctionTypeRenderer : KaPieceRenderer<KaFunctionType>(KaPiece.FunctionType) { + @OptIn(KaExperimentalApi::class) + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaFunctionType, next: () -> Unit): Boolean { + val nullable = value.isMarkedNullable + if (nullable) output.append("(", KaTextAttribute.GroupStart) + + if (value.isSuspend) keyword("suspend") + + val contextReceivers = value.contextReceivers + if (contextReceivers.isNotEmpty()) { + output.append("context", KaTextAttribute.Keyword) + output.append("(", KaTextAttribute.GroupStart) + contextReceivers.forEachIndexed { index, contextReceiver -> + if (index > 0) output.append(", ", KaTextAttribute.Punctuation) + render(contextReceiver.type, KaPiece.Type) + } + output.append(")", KaTextAttribute.GroupEnd) + output.space() + } + + value.receiverType?.let { receiverType -> + renderReceiverType(receiverType) + output.append(".", KaTextAttribute.Punctuation) + } + + output.append("(", KaTextAttribute.GroupStart) + value.parameterTypes.forEachIndexed { index, parameterType -> + if (index > 0) output.append(", ", KaTextAttribute.Punctuation) + render(parameterType, KaPiece.Type) + } + output.append(")", KaTextAttribute.GroupEnd) + + output.append(" -> ", KaTextAttribute.Punctuation) + render(value.returnType, KaPiece.Type) + + if (nullable) output.append(")", KaTextAttribute.GroupEnd) + render(value, KaPiece.TypeNullability) + return true + } +} + +private object TypeParameterTypeRenderer : KaPieceRenderer<KaTypeParameterType>(KaPiece.TypeParameterType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaTypeParameterType, next: () -> Unit): Boolean { + identifier(value.name, value.symbol) + render(value, KaPiece.TypeNullability) + return true + } +} + +private object CapturedTypeRenderer : KaPieceRenderer<KaCapturedType>(KaPiece.CapturedType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaCapturedType, next: () -> Unit): Boolean { + output.append("Captured", KaTextAttribute.Identifier) + output.append("(", KaTextAttribute.GroupStart) + render(value.projection, KaPiece.TypeProjection) + output.append(")", KaTextAttribute.GroupEnd) + return true + } +} + +private object DefinitelyNotNullTypeRenderer : KaPieceRenderer<KaDefinitelyNotNullType>(KaPiece.DefinitelyNotNullType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaDefinitelyNotNullType, next: () -> Unit): Boolean { + render(value.original, KaPiece.Type) + output.append(" & ", KaTextAttribute.Punctuation) + output.append("Any", KaTextAttribute.Identifier) + return true + } +} + +private object FlexibleTypeRenderer : KaPieceRenderer<KaFlexibleType>(KaPiece.FlexibleType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaFlexibleType, next: () -> Unit): Boolean { + val lower = value.lowerBound + val upper = value.upperBound + + // A flexible type whose bounds differ only in nullability (e.g. `String..String?`) is rendered compactly as `String!`. + if (isNullabilityFlexibleType(lower, upper)) { + render(lower, KaPiece.Type) + output.append("!", KaTextAttribute.Punctuation) + return true + } + + render(lower, KaPiece.Type) + output.append("..", KaTextAttribute.Punctuation) + render(upper, KaPiece.Type) + return true + } + + context(session: KaSession) + private fun isNullabilityFlexibleType(lower: KaType, upper: KaType): Boolean { + val isSameType = lower is KaClassType && upper is KaClassType && lower.classId == upper.classId || + lower is KaTypeParameterType && upper is KaTypeParameterType && lower.symbol == upper.symbol + if (!isSameType || lower.isMarkedNullable || !upper.isMarkedNullable) return false + + // For class types, the bounds must additionally share the same type arguments. + if (lower is KaClassType && upper is KaClassType) { + val lowerArguments = lower.typeArguments + val upperArguments = upper.typeArguments + return lowerArguments.size == upperArguments.size && + lowerArguments.indices.all { upperArguments[it].type == lowerArguments[it].type } + } + + return true + } +} + +private object IntersectionTypeRenderer : KaPieceRenderer<KaIntersectionType>(KaPiece.IntersectionType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaIntersectionType, next: () -> Unit): Boolean { + value.conjuncts.forEachIndexed { index, conjunct -> + if (index > 0) output.append(" & ", KaTextAttribute.Punctuation) + render(conjunct, KaPiece.Type) + } + return true + } +} + +private object DynamicTypeRenderer : KaPieceRenderer<KaDynamicType>(KaPiece.DynamicType) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaDynamicType, next: () -> Unit): Boolean { + output.append("dynamic", KaTextAttribute.Keyword) + return true + } +} + +private object ErrorTypeRenderer : KaPieceRenderer<KaErrorType>(KaPiece.ErrorType) { + @OptIn(KaNonPublicApi::class) + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaErrorType, next: () -> Unit): Boolean { + // For unresolved class types, render the written form (with type arguments), e.g. `C<String>`, instead of just an error marker. + val qualifiers = (value as? KaClassErrorType)?.qualifiers + if (!qualifiers.isNullOrEmpty()) { + qualifiers.forEachIndexed { index, qualifier -> + if (index > 0) output.append(".", KaTextAttribute.Punctuation) + val symbol = (qualifier as? KaResolvedClassTypeQualifier)?.symbol + if (symbol != null) { + identifier(qualifier.name, symbol) + } else { + output.append(qualifier.name.render(), KaTextAttribute.Identifier) + } + if (qualifier.typeArguments.isNotEmpty()) { + render(qualifier.typeArguments, KaPiece.TypeArgumentList) + } + } + return true + } + + output.append(value.presentableText ?: "ERROR", KaTextAttribute.Identifier) + return true + } +} + +// ------------------------------------------------------------------------------------------------- +// Type projections. +// ------------------------------------------------------------------------------------------------- + +private object TypeArgumentListRenderer : KaPieceRenderer<List<KaTypeProjection>>(KaPiece.TypeArgumentList) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: List<KaTypeProjection>, next: () -> Unit): Boolean { + output.append("<", KaTextAttribute.GroupStart) + value.forEachIndexed { index, projection -> + if (index > 0) output.append(", ", KaTextAttribute.Punctuation) + render(projection, KaPiece.TypeProjection) + } + output.append(">", KaTextAttribute.GroupEnd) + return true + } +} + +private object TypeProjectionRenderer : KaPieceRenderer<KaTypeProjection>(KaPiece.TypeProjection) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: KaTypeProjection, next: () -> Unit): Boolean { + when (value) { + is KaStarTypeProjection -> output.append("*", KaTextAttribute.Punctuation) + is KaTypeArgumentWithVariance -> { + if (value.variance != Variance.INVARIANT) keyword(value.variance.label) + render(value.type, KaPiece.Type) + } + } + return true + } +}
diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/symbolDeclarationRenderer/FirIdeDependentAnalysisSourceLikeModuleLegacyRenderingTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/symbolDeclarationRenderer/FirIdeDependentAnalysisSourceLikeModuleLegacyRenderingTestGenerated.java new file mode 100644 index 0000000..9141f4b --- /dev/null +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/symbolDeclarationRenderer/FirIdeDependentAnalysisSourceLikeModuleLegacyRenderingTestGenerated.java
@@ -0,0 +1,276 @@ +/* + * Copyright 2010-2024 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.test.cases.generated.cases.components.symbolDeclarationRenderer; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.analysis.api.fir.test.configurators.AnalysisApiFirTestConfiguratorFactory; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfiguratorFactoryData; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode; +import org.jetbrains.kotlin.analysis.test.framework.services.TargetPlatformEnum; +import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.symbolDeclarationRenderer.AbstractLegacyRenderingTest; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.analysis.api.fir.test.TestGeneratorKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration") +@TestDataPath("$PROJECT_ROOT") +public class FirIdeDependentAnalysisSourceLikeModuleLegacyRenderingTestGenerated extends AbstractLegacyRenderingTest { + @NotNull + @Override + public AnalysisApiTestConfigurator getConfigurator() { + return AnalysisApiFirTestConfiguratorFactory.INSTANCE.createConfigurator( + new AnalysisApiTestConfiguratorFactoryData( + TestModuleKind.SourceLike, + AnalysisSessionMode.Dependent, + AnalysisApiMode.Ide, + TargetPlatformEnum.JVM + ) + ); + } + + private void run(String fileName) { + runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/" + fileName); + } + + @Test + @TestMetadata("actualInheritance.kt") + public void testActualInheritance() { + run("actualInheritance.kt"); + } + + @Test + public void testAllFilesPresentInRenderDeclaration() { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration"), Pattern.compile("^(.+)\\.(kt|kts)$"), null, true); + } + + @Test + @TestMetadata("annotation.kt") + public void testAnnotation() { + run("annotation.kt"); + } + + @Test + @TestMetadata("complexTypes.kt") + public void testComplexTypes() { + run("complexTypes.kt"); + } + + @Test + @TestMetadata("constructorInObject.kt") + public void testConstructorInObject() { + run("constructorInObject.kt"); + } + + @Test + @TestMetadata("constructorOfAnonymousObject.kt") + public void testConstructorOfAnonymousObject() { + run("constructorOfAnonymousObject.kt"); + } + + @Test + @TestMetadata("contextParameter.kt") + public void testContextParameter() { + run("contextParameter.kt"); + } + + @Test + @TestMetadata("delegates.kt") + public void testDelegates() { + run("delegates.kt"); + } + + @Test + @TestMetadata("derivedClass.kt") + public void testDerivedClass() { + run("derivedClass.kt"); + } + + @Test + @TestMetadata("emptyAnonymousObject.kt") + public void testEmptyAnonymousObject() { + run("emptyAnonymousObject.kt"); + } + + @Test + @TestMetadata("enums.kt") + public void testEnums() { + run("enums.kt"); + } + + @Test + @TestMetadata("enums2.kt") + public void testEnums2() { + run("enums2.kt"); + } + + @Test + @TestMetadata("expectActual.kt") + public void testExpectActual() { + run("expectActual.kt"); + } + + @Test + @TestMetadata("F.kt") + public void testF() { + run("F.kt"); + } + + @Test + @TestMetadata("functionTypes.kt") + public void testFunctionTypes() { + run("functionTypes.kt"); + } + + @Test + @TestMetadata("functionalTypeAliases.kt") + public void testFunctionalTypeAliases() { + run("functionalTypeAliases.kt"); + } + + @Test + @TestMetadata("genericFunctions.kt") + public void testGenericFunctions() { + run("genericFunctions.kt"); + } + + @Test + @TestMetadata("genericProperty.kt") + public void testGenericProperty() { + run("genericProperty.kt"); + } + + @Test + @TestMetadata("intersectionType.kt") + public void testIntersectionType() { + run("intersectionType.kt"); + } + + @Test + @TestMetadata("nestedClass.kt") + public void testNestedClass() { + run("nestedClass.kt"); + } + + @Test + @TestMetadata("NestedOfAliasedType.kt") + public void testNestedOfAliasedType() { + run("NestedOfAliasedType.kt"); + } + + @Test + @TestMetadata("NestedSuperType.kt") + public void testNestedSuperType() { + run("NestedSuperType.kt"); + } + + @Test + @TestMetadata("noPrimaryConstructor.kt") + public void testNoPrimaryConstructor() { + run("noPrimaryConstructor.kt"); + } + + @Test + @TestMetadata("simpleClass.kt") + public void testSimpleClass() { + run("simpleClass.kt"); + } + + @Test + @TestMetadata("simpleFun.kt") + public void testSimpleFun() { + run("simpleFun.kt"); + } + + @Test + @TestMetadata("simpleTypeAlias.kt") + public void testSimpleTypeAlias() { + run("simpleTypeAlias.kt"); + } + + @Test + @TestMetadata("typeAliasWithGeneric.kt") + public void testTypeAliasWithGeneric() { + run("typeAliasWithGeneric.kt"); + } + + @Test + @TestMetadata("typeParameterVsNested.kt") + public void testTypeParameterVsNested() { + run("typeParameterVsNested.kt"); + } + + @Test + @TestMetadata("typeParameters.kt") + public void testTypeParameters() { + run("typeParameters.kt"); + } + + @Test + @TestMetadata("vararg.kt") + public void testVararg() { + run("vararg.kt"); + } + + @Test + @TestMetadata("where.kt") + public void testWhere() { + run("where.kt"); + } + + @Nested + @TestMetadata("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types") + @TestDataPath("$PROJECT_ROOT") + public class Types { + private void run(String fileName) { + runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/" + fileName); + } + + @Test + public void testAllFilesPresentInTypes() { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types"), Pattern.compile("^(.+)\\.(kt|kts)$"), null, true); + } + + @Test + @TestMetadata("annotationOnTypes.kt") + public void testAnnotationOnTypes() { + run("annotationOnTypes.kt"); + } + + @Test + @TestMetadata("annotationOnTypesWithComplexExpression.kt") + public void testAnnotationOnTypesWithComplexExpression() { + run("annotationOnTypesWithComplexExpression.kt"); + } + + @Test + @TestMetadata("approximatedCapturedType.kt") + public void testApproximatedCapturedType() { + run("approximatedCapturedType.kt"); + } + + @Test + @TestMetadata("contextFunctionTypes.kt") + public void testContextFunctionTypes() { + run("contextFunctionTypes.kt"); + } + + @Test + @TestMetadata("extraTypeArguments.kt") + public void testExtraTypeArguments() { + run("extraTypeArguments.kt"); + } + } +}
diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/symbolDeclarationRenderer/FirIdeNormalAnalysisSourceLikeModuleLegacyRenderingTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/symbolDeclarationRenderer/FirIdeNormalAnalysisSourceLikeModuleLegacyRenderingTestGenerated.java new file mode 100644 index 0000000..8eb1d8a --- /dev/null +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/symbolDeclarationRenderer/FirIdeNormalAnalysisSourceLikeModuleLegacyRenderingTestGenerated.java
@@ -0,0 +1,276 @@ +/* + * Copyright 2010-2024 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.test.cases.generated.cases.components.symbolDeclarationRenderer; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.analysis.api.fir.test.configurators.AnalysisApiFirTestConfiguratorFactory; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfiguratorFactoryData; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode; +import org.jetbrains.kotlin.analysis.test.framework.services.TargetPlatformEnum; +import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.symbolDeclarationRenderer.AbstractLegacyRenderingTest; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.analysis.api.fir.test.TestGeneratorKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration") +@TestDataPath("$PROJECT_ROOT") +public class FirIdeNormalAnalysisSourceLikeModuleLegacyRenderingTestGenerated extends AbstractLegacyRenderingTest { + @NotNull + @Override + public AnalysisApiTestConfigurator getConfigurator() { + return AnalysisApiFirTestConfiguratorFactory.INSTANCE.createConfigurator( + new AnalysisApiTestConfiguratorFactoryData( + TestModuleKind.SourceLike, + AnalysisSessionMode.Normal, + AnalysisApiMode.Ide, + TargetPlatformEnum.JVM + ) + ); + } + + private void run(String fileName) { + runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/" + fileName); + } + + @Test + @TestMetadata("actualInheritance.kt") + public void testActualInheritance() { + run("actualInheritance.kt"); + } + + @Test + public void testAllFilesPresentInRenderDeclaration() { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration"), Pattern.compile("^(.+)\\.(kt|kts)$"), null, true); + } + + @Test + @TestMetadata("annotation.kt") + public void testAnnotation() { + run("annotation.kt"); + } + + @Test + @TestMetadata("complexTypes.kt") + public void testComplexTypes() { + run("complexTypes.kt"); + } + + @Test + @TestMetadata("constructorInObject.kt") + public void testConstructorInObject() { + run("constructorInObject.kt"); + } + + @Test + @TestMetadata("constructorOfAnonymousObject.kt") + public void testConstructorOfAnonymousObject() { + run("constructorOfAnonymousObject.kt"); + } + + @Test + @TestMetadata("contextParameter.kt") + public void testContextParameter() { + run("contextParameter.kt"); + } + + @Test + @TestMetadata("delegates.kt") + public void testDelegates() { + run("delegates.kt"); + } + + @Test + @TestMetadata("derivedClass.kt") + public void testDerivedClass() { + run("derivedClass.kt"); + } + + @Test + @TestMetadata("emptyAnonymousObject.kt") + public void testEmptyAnonymousObject() { + run("emptyAnonymousObject.kt"); + } + + @Test + @TestMetadata("enums.kt") + public void testEnums() { + run("enums.kt"); + } + + @Test + @TestMetadata("enums2.kt") + public void testEnums2() { + run("enums2.kt"); + } + + @Test + @TestMetadata("expectActual.kt") + public void testExpectActual() { + run("expectActual.kt"); + } + + @Test + @TestMetadata("F.kt") + public void testF() { + run("F.kt"); + } + + @Test + @TestMetadata("functionTypes.kt") + public void testFunctionTypes() { + run("functionTypes.kt"); + } + + @Test + @TestMetadata("functionalTypeAliases.kt") + public void testFunctionalTypeAliases() { + run("functionalTypeAliases.kt"); + } + + @Test + @TestMetadata("genericFunctions.kt") + public void testGenericFunctions() { + run("genericFunctions.kt"); + } + + @Test + @TestMetadata("genericProperty.kt") + public void testGenericProperty() { + run("genericProperty.kt"); + } + + @Test + @TestMetadata("intersectionType.kt") + public void testIntersectionType() { + run("intersectionType.kt"); + } + + @Test + @TestMetadata("nestedClass.kt") + public void testNestedClass() { + run("nestedClass.kt"); + } + + @Test + @TestMetadata("NestedOfAliasedType.kt") + public void testNestedOfAliasedType() { + run("NestedOfAliasedType.kt"); + } + + @Test + @TestMetadata("NestedSuperType.kt") + public void testNestedSuperType() { + run("NestedSuperType.kt"); + } + + @Test + @TestMetadata("noPrimaryConstructor.kt") + public void testNoPrimaryConstructor() { + run("noPrimaryConstructor.kt"); + } + + @Test + @TestMetadata("simpleClass.kt") + public void testSimpleClass() { + run("simpleClass.kt"); + } + + @Test + @TestMetadata("simpleFun.kt") + public void testSimpleFun() { + run("simpleFun.kt"); + } + + @Test + @TestMetadata("simpleTypeAlias.kt") + public void testSimpleTypeAlias() { + run("simpleTypeAlias.kt"); + } + + @Test + @TestMetadata("typeAliasWithGeneric.kt") + public void testTypeAliasWithGeneric() { + run("typeAliasWithGeneric.kt"); + } + + @Test + @TestMetadata("typeParameterVsNested.kt") + public void testTypeParameterVsNested() { + run("typeParameterVsNested.kt"); + } + + @Test + @TestMetadata("typeParameters.kt") + public void testTypeParameters() { + run("typeParameters.kt"); + } + + @Test + @TestMetadata("vararg.kt") + public void testVararg() { + run("vararg.kt"); + } + + @Test + @TestMetadata("where.kt") + public void testWhere() { + run("where.kt"); + } + + @Nested + @TestMetadata("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types") + @TestDataPath("$PROJECT_ROOT") + public class Types { + private void run(String fileName) { + runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/" + fileName); + } + + @Test + public void testAllFilesPresentInTypes() { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types"), Pattern.compile("^(.+)\\.(kt|kts)$"), null, true); + } + + @Test + @TestMetadata("annotationOnTypes.kt") + public void testAnnotationOnTypes() { + run("annotationOnTypes.kt"); + } + + @Test + @TestMetadata("annotationOnTypesWithComplexExpression.kt") + public void testAnnotationOnTypesWithComplexExpression() { + run("annotationOnTypesWithComplexExpression.kt"); + } + + @Test + @TestMetadata("approximatedCapturedType.kt") + public void testApproximatedCapturedType() { + run("approximatedCapturedType.kt"); + } + + @Test + @TestMetadata("contextFunctionTypes.kt") + public void testContextFunctionTypes() { + run("contextFunctionTypes.kt"); + } + + @Test + @TestMetadata("extraTypeArguments.kt") + public void testExtraTypeArguments() { + run("extraTypeArguments.kt"); + } + } +}
diff --git a/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/TestGenerator.kt b/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/TestGenerator.kt index 5d44c42..97753bc 100644 --- a/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/TestGenerator.kt +++ b/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/TestGenerator.kt
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.substututorFactory.AbstractSubstitutorBuilderTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.symbolDeclarationOverridesProvider.AbstractIsSubclassOfTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.symbolDeclarationOverridesProvider.AbstractOverriddenDeclarationProviderTest +import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.symbolDeclarationRenderer.AbstractLegacyRenderingTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.symbolDeclarationRenderer.AbstractRendererTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.symbolDeclarationRenderer.AbstractSymbolRenderingByReferenceTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.symbolInfoProvider.* @@ -514,6 +515,10 @@ } component("symbolDeclarationRenderer") { + test<AbstractLegacyRenderingTest> { + model(it, "renderDeclaration") + } + test<AbstractRendererTest> { model(it, "renderDeclaration") }
diff --git a/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/symbolDeclarationRenderer/AbstractLegacyRenderingTest.kt b/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/symbolDeclarationRenderer/AbstractLegacyRenderingTest.kt new file mode 100644 index 0000000..be14e2c --- /dev/null +++ b/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/symbolDeclarationRenderer/AbstractLegacyRenderingTest.kt
@@ -0,0 +1,61 @@ +/* + * Copyright 2010-2026 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.impl.base.test.cases.components.symbolDeclarationRenderer + +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.projectStructure.KaDanglingFileResolutionMode +import org.jetbrains.kotlin.analysis.api.renderer.declarations.bodies.KaRendererBodyMemberScopeSorter +import org.jetbrains.kotlin.analysis.api.renderer.declarations.impl.KaDeclarationRendererForSource +import org.jetbrains.kotlin.analysis.api.renderer.declarations.renderers.KaClassifierBodyRenderer +import org.jetbrains.kotlin.analysis.api.renderer.render +import org.jetbrains.kotlin.analysis.api.symbols.KaDeclarationSymbol +import org.jetbrains.kotlin.analysis.api.symbols.markers.KaDeclarationContainerSymbol +import org.jetbrains.kotlin.analysis.api.symbols.symbol +import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest +import org.jetbrains.kotlin.analysis.test.framework.projectStructure.KtTestModule +import org.jetbrains.kotlin.analysis.test.framework.utils.executeOnPooledThreadInReadAction +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.services.TestServices +import org.jetbrains.kotlin.test.services.assertions + +abstract class AbstractLegacyRenderingTest : AbstractAnalysisApiBasedTest() { + override fun doTestByMainFile(mainFile: KtFile, mainModule: KtTestModule, testServices: TestServices) { + val renderer = KaDeclarationRendererForSource.WITH_SHORT_NAMES.with { + classifierBodyRenderer = KaClassifierBodyRenderer.BODY_WITH_MEMBERS + bodyMemberScopeSorter = object : KaRendererBodyMemberScopeSorter { + override fun sortMembers( + analysisSession: KaSession, + members: List<KaDeclarationSymbol>, + container: KaDeclarationContainerSymbol, + ): List<KaDeclarationSymbol> { + with(analysisSession) { + return KaRendererBodyMemberScopeSorter.ENUM_ENTRIES_AT_BEGINING + .sortMembers(analysisSession, members, container) + .sortedBy { it.render() } + } + } + } + } + + val actual = executeOnPooledThreadInReadAction { + buildString { + // Since we want to render the whole file, we shouldn't use `IGNORE_SELF` as it's only designed for local use cases. + copyAwareAnalyzeForTest( + mainFile, + danglingFileResolutionMode = KaDanglingFileResolutionMode.PREFER_SELF, + ) { contextFile -> + contextFile.declarations.forEach { declaration -> + append(declaration.symbol.render(renderer)) + appendLine() + appendLine() + } + } + } + } + + testServices.assertions.assertEqualsToTestOutputFile(actual, extension = ".rendered") + } +}
diff --git a/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/symbolDeclarationRenderer/AbstractRendererTest.kt b/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/symbolDeclarationRenderer/AbstractRendererTest.kt index ddbf7ca..3676742 100644 --- a/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/symbolDeclarationRenderer/AbstractRendererTest.kt +++ b/analysis/analysis-api-impl-base/testFixtures/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/symbolDeclarationRenderer/AbstractRendererTest.kt
@@ -5,15 +5,14 @@ package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.symbolDeclarationRenderer -import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.projectStructure.KaDanglingFileResolutionMode -import org.jetbrains.kotlin.analysis.api.renderer.declarations.bodies.KaRendererBodyMemberScopeSorter -import org.jetbrains.kotlin.analysis.api.renderer.declarations.impl.KaDeclarationRendererForSource -import org.jetbrains.kotlin.analysis.api.renderer.declarations.renderers.KaClassifierBodyRenderer -import org.jetbrains.kotlin.analysis.api.renderer.render -import org.jetbrains.kotlin.analysis.api.symbols.KaDeclarationSymbol -import org.jetbrains.kotlin.analysis.api.symbols.markers.KaDeclarationContainerSymbol +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderer +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderingOption +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderingOutput +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol import org.jetbrains.kotlin.analysis.api.symbols.symbol +import org.jetbrains.kotlin.analysis.test.data.manager.TestVariantChain +import org.jetbrains.kotlin.analysis.test.data.manager.withAdditionalVariant import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest import org.jetbrains.kotlin.analysis.test.framework.projectStructure.KtTestModule import org.jetbrains.kotlin.analysis.test.framework.utils.executeOnPooledThreadInReadAction @@ -22,24 +21,10 @@ import org.jetbrains.kotlin.test.services.assertions abstract class AbstractRendererTest : AbstractAnalysisApiBasedTest() { - override fun doTestByMainFile(mainFile: KtFile, mainModule: KtTestModule, testServices: TestServices) { - val renderer = KaDeclarationRendererForSource.WITH_SHORT_NAMES.with { - classifierBodyRenderer = KaClassifierBodyRenderer.BODY_WITH_MEMBERS - bodyMemberScopeSorter = object : KaRendererBodyMemberScopeSorter { - override fun sortMembers( - analysisSession: KaSession, - members: List<KaDeclarationSymbol>, - container: KaDeclarationContainerSymbol, - ): List<KaDeclarationSymbol> { - with(analysisSession) { - return KaRendererBodyMemberScopeSorter.ENUM_ENTRIES_AT_BEGINING - .sortMembers(analysisSession, members, container) - .sortedBy { it.render() } - } - } - } - } + override val variantChain: TestVariantChain + get() = super.variantChain.withAdditionalVariant("new") + override fun doTestByMainFile(mainFile: KtFile, mainModule: KtTestModule, testServices: TestServices) { val actual = executeOnPooledThreadInReadAction { buildString { // Since we want to render the whole file, we shouldn't use `IGNORE_SELF` as it's only designed for local use cases. @@ -47,8 +32,20 @@ mainFile, danglingFileResolutionMode = KaDanglingFileResolutionMode.PREFER_SELF, ) { contextFile -> + val renderer = KaRenderer.default.copy { + // Reproduce the legacy member ordering (see `AbstractLegacyRenderingTest`). + set(KaRenderingOption.ClassMemberOrdering) { first, second -> + fun renderToString(symbol: KaSymbol): String = + KaRenderingOutput.plainString().also { KaRenderer.default.render(symbol, it) }.toString() + + renderToString(first).compareTo(renderToString(second)) + } + } + contextFile.declarations.forEach { declaration -> - append(declaration.symbol.render(renderer)) + val output = KaRenderingOutput.plainString(indentationUnit = " ") + renderer.render(declaration.symbol, output) + append(output.toString()) appendLine() appendLine() }
diff --git a/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/symbolDeclarationRenderer/FirStandaloneNormalAnalysisSourceModuleLegacyRenderingTestGenerated.java b/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/symbolDeclarationRenderer/FirStandaloneNormalAnalysisSourceModuleLegacyRenderingTestGenerated.java new file mode 100644 index 0000000..1eab65f --- /dev/null +++ b/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/symbolDeclarationRenderer/FirStandaloneNormalAnalysisSourceModuleLegacyRenderingTestGenerated.java
@@ -0,0 +1,276 @@ +/* + * Copyright 2010-2024 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.standalone.fir.test.cases.generated.cases.components.symbolDeclarationRenderer; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.analysis.api.standalone.fir.test.configurators.AnalysisApiFirStandaloneModeTestConfiguratorFactory; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfiguratorFactoryData; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode; +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode; +import org.jetbrains.kotlin.analysis.test.framework.services.TargetPlatformEnum; +import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.symbolDeclarationRenderer.AbstractLegacyRenderingTest; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.analysis.api.standalone.fir.test.TestGeneratorKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration") +@TestDataPath("$PROJECT_ROOT") +public class FirStandaloneNormalAnalysisSourceModuleLegacyRenderingTestGenerated extends AbstractLegacyRenderingTest { + @NotNull + @Override + public AnalysisApiTestConfigurator getConfigurator() { + return AnalysisApiFirStandaloneModeTestConfiguratorFactory.INSTANCE.createConfigurator( + new AnalysisApiTestConfiguratorFactoryData( + TestModuleKind.Source, + AnalysisSessionMode.Normal, + AnalysisApiMode.Standalone, + TargetPlatformEnum.JVM + ) + ); + } + + private void run(String fileName) { + runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/" + fileName); + } + + @Test + @TestMetadata("actualInheritance.kt") + public void testActualInheritance() { + run("actualInheritance.kt"); + } + + @Test + public void testAllFilesPresentInRenderDeclaration() { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration"), Pattern.compile("^(.+)\\.(kt)$"), null, true); + } + + @Test + @TestMetadata("annotation.kt") + public void testAnnotation() { + run("annotation.kt"); + } + + @Test + @TestMetadata("complexTypes.kt") + public void testComplexTypes() { + run("complexTypes.kt"); + } + + @Test + @TestMetadata("constructorInObject.kt") + public void testConstructorInObject() { + run("constructorInObject.kt"); + } + + @Test + @TestMetadata("constructorOfAnonymousObject.kt") + public void testConstructorOfAnonymousObject() { + run("constructorOfAnonymousObject.kt"); + } + + @Test + @TestMetadata("contextParameter.kt") + public void testContextParameter() { + run("contextParameter.kt"); + } + + @Test + @TestMetadata("delegates.kt") + public void testDelegates() { + run("delegates.kt"); + } + + @Test + @TestMetadata("derivedClass.kt") + public void testDerivedClass() { + run("derivedClass.kt"); + } + + @Test + @TestMetadata("emptyAnonymousObject.kt") + public void testEmptyAnonymousObject() { + run("emptyAnonymousObject.kt"); + } + + @Test + @TestMetadata("enums.kt") + public void testEnums() { + run("enums.kt"); + } + + @Test + @TestMetadata("enums2.kt") + public void testEnums2() { + run("enums2.kt"); + } + + @Test + @TestMetadata("expectActual.kt") + public void testExpectActual() { + run("expectActual.kt"); + } + + @Test + @TestMetadata("F.kt") + public void testF() { + run("F.kt"); + } + + @Test + @TestMetadata("functionTypes.kt") + public void testFunctionTypes() { + run("functionTypes.kt"); + } + + @Test + @TestMetadata("functionalTypeAliases.kt") + public void testFunctionalTypeAliases() { + run("functionalTypeAliases.kt"); + } + + @Test + @TestMetadata("genericFunctions.kt") + public void testGenericFunctions() { + run("genericFunctions.kt"); + } + + @Test + @TestMetadata("genericProperty.kt") + public void testGenericProperty() { + run("genericProperty.kt"); + } + + @Test + @TestMetadata("intersectionType.kt") + public void testIntersectionType() { + run("intersectionType.kt"); + } + + @Test + @TestMetadata("nestedClass.kt") + public void testNestedClass() { + run("nestedClass.kt"); + } + + @Test + @TestMetadata("NestedOfAliasedType.kt") + public void testNestedOfAliasedType() { + run("NestedOfAliasedType.kt"); + } + + @Test + @TestMetadata("NestedSuperType.kt") + public void testNestedSuperType() { + run("NestedSuperType.kt"); + } + + @Test + @TestMetadata("noPrimaryConstructor.kt") + public void testNoPrimaryConstructor() { + run("noPrimaryConstructor.kt"); + } + + @Test + @TestMetadata("simpleClass.kt") + public void testSimpleClass() { + run("simpleClass.kt"); + } + + @Test + @TestMetadata("simpleFun.kt") + public void testSimpleFun() { + run("simpleFun.kt"); + } + + @Test + @TestMetadata("simpleTypeAlias.kt") + public void testSimpleTypeAlias() { + run("simpleTypeAlias.kt"); + } + + @Test + @TestMetadata("typeAliasWithGeneric.kt") + public void testTypeAliasWithGeneric() { + run("typeAliasWithGeneric.kt"); + } + + @Test + @TestMetadata("typeParameterVsNested.kt") + public void testTypeParameterVsNested() { + run("typeParameterVsNested.kt"); + } + + @Test + @TestMetadata("typeParameters.kt") + public void testTypeParameters() { + run("typeParameters.kt"); + } + + @Test + @TestMetadata("vararg.kt") + public void testVararg() { + run("vararg.kt"); + } + + @Test + @TestMetadata("where.kt") + public void testWhere() { + run("where.kt"); + } + + @Nested + @TestMetadata("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types") + @TestDataPath("$PROJECT_ROOT") + public class Types { + private void run(String fileName) { + runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/" + fileName); + } + + @Test + public void testAllFilesPresentInTypes() { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types"), Pattern.compile("^(.+)\\.(kt)$"), null, true); + } + + @Test + @TestMetadata("annotationOnTypes.kt") + public void testAnnotationOnTypes() { + run("annotationOnTypes.kt"); + } + + @Test + @TestMetadata("annotationOnTypesWithComplexExpression.kt") + public void testAnnotationOnTypesWithComplexExpression() { + run("annotationOnTypesWithComplexExpression.kt"); + } + + @Test + @TestMetadata("approximatedCapturedType.kt") + public void testApproximatedCapturedType() { + run("approximatedCapturedType.kt"); + } + + @Test + @TestMetadata("contextFunctionTypes.kt") + public void testContextFunctionTypes() { + run("contextFunctionTypes.kt"); + } + + @Test + @TestMetadata("extraTypeArguments.kt") + public void testExtraTypeArguments() { + run("extraTypeArguments.kt"); + } + } +}
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/internals/KaRendererProvider.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/internals/KaRendererProvider.kt new file mode 100644 index 0000000..7a5f445 --- /dev/null +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/internals/KaRendererProvider.kt
@@ -0,0 +1,14 @@ +/* + * Copyright 2010-2026 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.internals + +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi +import org.jetbrains.kotlin.analysis.api.rendering.KaRenderer + +@KaExperimentalApi +public interface KaRendererProvider { + public val defaultRenderer: KaRenderer +}
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaPieceRenderer.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaPieceRenderer.kt new file mode 100644 index 0000000..aa42a98 --- /dev/null +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaPieceRenderer.kt
@@ -0,0 +1,319 @@ +/* + * Copyright 2010-2026 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.rendering + +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotated +import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotation +import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotationValue +import org.jetbrains.kotlin.analysis.api.base.KaConstantValue +import org.jetbrains.kotlin.analysis.api.symbols.KaAnonymousObjectSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaBackingFieldSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaClassLikeSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaContextParameterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaDeclarationSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaEnumEntrySymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaJavaFieldSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaLocalVariableSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaNamedClassSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaNamedFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaPackageSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaParameterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaPropertyAccessorSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaPropertySymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaTypeAliasSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaTypeParameterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol +import org.jetbrains.kotlin.analysis.api.types.KaCapturedType +import org.jetbrains.kotlin.analysis.api.types.KaClassType +import org.jetbrains.kotlin.analysis.api.types.KaDefinitelyNotNullType +import org.jetbrains.kotlin.analysis.api.types.KaDynamicType +import org.jetbrains.kotlin.analysis.api.types.KaErrorType +import org.jetbrains.kotlin.analysis.api.types.KaFlexibleType +import org.jetbrains.kotlin.analysis.api.types.KaFunctionType +import org.jetbrains.kotlin.analysis.api.types.KaIntersectionType +import org.jetbrains.kotlin.analysis.api.types.KaType +import org.jetbrains.kotlin.analysis.api.types.KaTypeParameterType +import org.jetbrains.kotlin.analysis.api.types.KaTypeProjection + +/** + * Renders a single [KaPiece] into the contextual [KaRenderingOutput]. + * + * Renderers for a given [piece] form a stack within a [KaRenderer]; [KaRendererBuilder.push] adds one on top. A renderer may + * emit nested pieces via the top-level [render] function and fall back to the renderer beneath it by invoking its `next` callback. + * + * @param piece the piece this renderer is responsible for. + */ +@KaExperimentalApi +public abstract class KaPieceRenderer<T>(public val piece: KaPiece<T>) { + /** + * Renders [value] into the contextual [KaRenderingOutput]. Returns `true` if the value was handled, or `false` to fall back to + * the renderer beneath this one. Invoke [next] to render [value] with the renderer immediately beneath this one. + */ + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + public abstract fun render(value: T, next: () -> Unit): Boolean + + @KaExperimentalApi + public companion object { + /** + * The no-op renderer for the given [piece]. + */ + public fun <T> empty(piece: KaPiece<T>): KaPieceRenderer<T> { + return object : KaPieceRenderer<T>(piece) { + context(session: KaSession, context: KaRenderingContext, output: KaRenderingOutput) + override fun render(value: T, next: () -> Unit): Boolean = true + } + } + } +} + +/** The rendering state passed to every [KaPieceRenderer], used to render nested pieces and to read [KaRenderingOption] values. */ +@KaExperimentalApi +public interface KaRenderingContext { + /** Renders [value] as the given [piece], dispatching to the corresponding renderer stack. */ + context(session: KaSession) + public fun <T> render(value: T, piece: KaPiece<T>) + + /** Returns the effective value of [option] for this renderer (the overridden value, or its [KaRenderingOption.defaultValue]). */ + public fun <T> valueFor(option: KaRenderingOption<T>): T +} + +/** Renders [value] as the given [piece], dispatching to the corresponding renderer stack. */ +@KaExperimentalApi +context(session: KaSession, context: KaRenderingContext) +public fun <T> render(value: T, piece: KaPiece<T>) { + context.render(value, piece) +} + +/** + * A typed, enumerated key that identifies a specific piece of a symbol or a type to render. + * + * Each [KaPiece] is associated with a stack of [KaPieceRenderer]s in a [KaRenderer]. Renderers may render other pieces via + * [KaRenderingContext], which allows customizing individual parts of the output without reimplementing the whole rendering. + * + * The two top-level entry points are [Symbol] (renders any [KaSymbol]) and [Type] (renders any [KaType]). + */ +@KaExperimentalApi +public class KaPiece<T> private constructor() { + @KaExperimentalApi + public companion object { + /** The annotations of an annotated element, including the whitespace which separates them from what follows. */ + public val Annotations: KaPiece<KaAnnotated> = KaPiece() + + /** A single annotation entry, e.g. `@Foo(bar)`. */ + public val Annotation: KaPiece<KaAnnotation> = KaPiece() + + /** The parenthesized argument list of an annotation, e.g. `(message = "Use bar")`. */ + public val AnnotationValues: KaPiece<KaAnnotation> = KaPiece() + + /** A single annotation argument value (a constant, enum entry, class literal, nested annotation, or array). */ + public val AnnotationValue: KaPiece<KaAnnotationValue> = KaPiece() + + /** A constant value, e.g. `42`, `"text"`, or `null`. */ + public val ConstantValue: KaPiece<KaConstantValue> = KaPiece() + + /** Any [KaType]. Dispatches to a more specific type piece depending on the type kind. */ + public val Type: KaPiece<KaType> = KaPiece() + + /** The annotations attached to a [KaType]. */ + public val TypeAnnotations: KaPiece<KaType> = KaPiece() + + /** The nullability marker of a type, e.g. `?` in `String?`. Nothing is rendered for a non-nullable type. */ + public val TypeNullability: KaPiece<KaType> = KaPiece() + + /** A single type projection (an explicit type argument or a star projection). */ + public val TypeProjection: KaPiece<KaTypeProjection> = KaPiece() + + /** A comma-separated list of type arguments enclosed in angle brackets, e.g. `<Int, String>`. */ + public val TypeArgumentList: KaPiece<List<KaTypeProjection>> = KaPiece() + + /** A class type, e.g. `List<String>`. */ + public val ClassType: KaPiece<KaClassType> = KaPiece() + + /** A function type, e.g. `(Int) -> String`. */ + public val FunctionType: KaPiece<KaFunctionType> = KaPiece() + + /** A reference to a type parameter, e.g. `T`. */ + public val TypeParameterType: KaPiece<KaTypeParameterType> = KaPiece() + + /** A captured type produced by capturing a projection during type inference. */ + public val CapturedType: KaPiece<KaCapturedType> = KaPiece() + + /** A definitely non-nullable type, e.g. `T & Any`. */ + public val DefinitelyNotNullType: KaPiece<KaDefinitelyNotNullType> = KaPiece() + + /** A flexible type spanning a lower and an upper bound, such as a platform type. */ + public val FlexibleType: KaPiece<KaFlexibleType> = KaPiece() + + /** An intersection of several types, e.g. `A & B`. */ + public val IntersectionType: KaPiece<KaIntersectionType> = KaPiece() + + /** The Kotlin/JS `dynamic` type. */ + public val DynamicType: KaPiece<KaDynamicType> = KaPiece() + + /** A type that could not be resolved. */ + public val ErrorType: KaPiece<KaErrorType> = KaPiece() + + /** Any [KaSymbol]. Dispatches to a more specific symbol piece depending on the symbol kind. */ + public val Symbol: KaPiece<KaSymbol> = KaPiece() + + /** The name of a declaration, as rendered in the declaration itself, e.g. `foo` in `fun foo()`. */ + public val SymbolName: KaPiece<KaSymbol> = KaPiece() + + /** Common modifiers shared by all declarations (visibility, `expect`/`actual`, `external`). */ + public val SymbolModifiers: KaPiece<KaDeclarationSymbol> = KaPiece() + + /** The visibility modifier of a declaration, e.g. `private`. Nothing is rendered for a public or implicit visibility. */ + public val Visibility: KaPiece<KaDeclarationSymbol> = KaPiece() + + /** The modality modifier of a declaration, e.g. `abstract`. Nothing is rendered for a final, implicit, or redundant modality. */ + public val Modality: KaPiece<KaDeclarationSymbol> = KaPiece() + + /** A comma-separated list of type parameters enclosed in angle brackets, e.g. `<T : Number>`. */ + public val TypeParameterList: KaPiece<List<KaTypeParameterSymbol>> = KaPiece() + + /** The type parameter list of a callable. */ + public val CallableTypeParameterList: KaPiece<KaCallableSymbol> = KaPiece() + + /** The type parameter list of a class or a type alias. */ + public val ClassifierTypeParameterList: KaPiece<KaClassLikeSymbol> = KaPiece() + + /** A single type parameter, including its variance, `reified` modifier, and upper bound. */ + public val TypeParameter: KaPiece<KaTypeParameterSymbol> = KaPiece() + + /** The `where` clause listing the bounds of type parameters that have more than one upper bound. */ + public val WhereClause: KaPiece<List<KaTypeParameterSymbol>> = KaPiece() + + /** The `where` clause of a callable. */ + public val CallableWhereClause: KaPiece<KaCallableSymbol> = KaPiece() + + /** The `where` clause of a class or a type alias. */ + public val ClassifierWhereClause: KaPiece<KaClassLikeSymbol> = KaPiece() + + /** A `context(...)` clause. */ + public val ContextParameterList: KaPiece<List<KaContextParameterSymbol>> = KaPiece() + + /** The `context(...)` clause of a callable. */ + public val CallableContextParameterList: KaPiece<KaCallableSymbol> = KaPiece() + + /** A single context parameter. */ + public val ContextParameter: KaPiece<KaContextParameterSymbol> = KaPiece() + + /** The annotations of a context parameter. */ + public val ContextParameterAnnotations: KaPiece<KaContextParameterSymbol> = KaPiece() + + /** The type of context parameter. */ + public val ContextParameterType: KaPiece<KaContextParameterSymbol> = KaPiece() + + /** The parenthesized value parameter list, e.g. `(a: Int, b: String)`. */ + public val ValueParameterList: KaPiece<List<KaValueParameterSymbol>> = KaPiece() + + /** A single value parameter, including its modifiers, name, type, and default value. */ + public val ValueParameter: KaPiece<KaValueParameterSymbol> = KaPiece() + + /** The annotations of a value parameter. */ + public val ValueParameterAnnotations: KaPiece<KaValueParameterSymbol> = KaPiece() + + /** The type of value parameter. */ + public val ValueParameterType: KaPiece<KaValueParameterSymbol> = KaPiece() + + /** The default value of a value parameter, including the leading `=`, e.g. `= ...`. */ + public val ValueParameterDefaultValue: KaPiece<KaValueParameterSymbol> = KaPiece() + + /** Any [KaFunctionSymbol]; used for anonymous functions and SAM constructors. */ + public val Function: KaPiece<KaFunctionSymbol> = KaPiece() + + /** The modifiers of a function, e.g. `suspend`, `operator`, `inline`. */ + public val FunctionModifiers: KaPiece<KaFunctionSymbol> = KaPiece() + + /** The annotations of a function. */ + public val FunctionAnnotations: KaPiece<KaFunctionSymbol> = KaPiece() + + /** The receiver of an extension callable, including the trailing dot, e.g. `String.` in `fun String.foo()`. */ + public val FunctionReceiver: KaPiece<KaParameterSymbol> = KaPiece() + + /** The value parameter list of a function. */ + public val FunctionValueParameterList: KaPiece<KaFunctionSymbol> = KaPiece() + + /** The return type of a function, including the leading colon, e.g. `: String`. The type is always rendered. */ + public val FunctionReturnType: KaPiece<KaFunctionSymbol> = KaPiece() + + /** The return type of named function, including the leading colon, e.g. `: String`. */ + public val NamedFunctionReturnType: KaPiece<KaNamedFunctionSymbol> = KaPiece() + + /** A named function declaration. */ + public val NamedFunction: KaPiece<KaNamedFunctionSymbol> = KaPiece() + + /** A constructor declaration. */ + public val Constructor: KaPiece<KaConstructorSymbol> = KaPiece() + + /** The primary constructor of a class, as rendered in the class header, e.g. `(x: Int)` in `class Foo(x: Int)`. */ + public val PrimaryConstructor: KaPiece<KaConstructorSymbol> = KaPiece() + + /** The non-default accessors of a property, each on its own indented line below the property. */ + public val PropertyAccessors: KaPiece<KaPropertySymbol> = KaPiece() + + /** A property getter or setter. */ + public val PropertyAccessor: KaPiece<KaPropertyAccessorSymbol> = KaPiece() + + /** A property declaration (`val` or `var`). */ + public val Property: KaPiece<KaPropertySymbol> = KaPiece() + + /** The modifiers of a property, e.g. `const`, `lateinit`, `override`. */ + public val PropertyModifiers: KaPiece<KaPropertySymbol> = KaPiece() + + /** The annotations of a property. */ + public val PropertyAnnotations: KaPiece<KaPropertySymbol> = KaPiece() + + /** The type of property. */ + public val PropertyReturnType: KaPiece<KaPropertySymbol> = KaPiece() + + /** A local variable declaration. */ + public val LocalVariable: KaPiece<KaLocalVariableSymbol> = KaPiece() + + /** A field declared in Java. */ + public val JavaField: KaPiece<KaJavaFieldSymbol> = KaPiece() + + /** The backing field of a property. */ + public val BackingField: KaPiece<KaBackingFieldSymbol> = KaPiece() + + /** A single enum entry. */ + public val EnumEntry: KaPiece<KaEnumEntrySymbol> = KaPiece() + + /** A named class, interface, or object declaration. */ + public val NamedClass: KaPiece<KaNamedClassSymbol> = KaPiece() + + /** An anonymous object, e.g. `object : Runnable { ... }`. */ + public val AnonymousObject: KaPiece<KaAnonymousObjectSymbol> = KaPiece() + + /** A type alias declaration. */ + public val TypeAlias: KaPiece<KaTypeAliasSymbol> = KaPiece() + + /** The modifiers of a class, e.g. `data`, `sealed`, `enum`, `inner`. */ + public val ClassModifiers: KaPiece<KaNamedClassSymbol> = KaPiece() + + /** The annotations of a class. */ + public val ClassAnnotations: KaPiece<KaNamedClassSymbol> = KaPiece() + + /** The supertype list of a class, e.g. `: Base, Runnable`. */ + public val SupertypeList: KaPiece<KaClassSymbol> = KaPiece() + + /** A single supertype, including the constructor call parentheses of a class supertype, e.g. `Base()`. */ + public val Supertype: KaPiece<KaType> = KaPiece() + + /** The body of a class, containing its member declarations. */ + public val ClassBody: KaPiece<KaClassSymbol> = KaPiece() + + /** A package, e.g. `package kotlin.collections`. */ + public val Package: KaPiece<KaPackageSymbol> = KaPiece() + } +}
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaRenderer.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaRenderer.kt new file mode 100644 index 0000000..7ac0d36 --- /dev/null +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaRenderer.kt
@@ -0,0 +1,93 @@ +/* + * Copyright 2010-2026 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.rendering + +import com.intellij.openapi.components.service +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.base.KaConstantValue +import org.jetbrains.kotlin.analysis.api.internals.KaRendererProvider +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.analysis.api.types.KaType +import org.jetbrains.kotlin.analysis.api.types.KaTypeProjection + +/** + * Renders [KaSymbol]s, [KaType]s, [KaTypeProjection]s, and [KaConstantValue]s into a [KaRenderingOutput] as human-readable, Kotlin-like + * text. + * + * A renderer is an immutable bundle of [KaPieceRenderer]s (a stack per [KaPiece]) together with [KaRenderingOption] values. Obtain the + * standard renderer via [default] and derive customized variants with [copy]. + */ +@KaExperimentalApi +public interface KaRenderer { + /** Renders the declaration of [symbol] — its signature and modifiers, and, for a class, its body — into [output]. */ + context(session: KaSession) + public fun render(symbol: KaSymbol, output: KaRenderingOutput) + + /** Renders [type] into [output]. */ + context(session: KaSession) + public fun render(type: KaType, output: KaRenderingOutput) + + /** Renders [constantValue] into [output]. */ + context(session: KaSession) + public fun render(constantValue: KaConstantValue, output: KaRenderingOutput) + + /** + * Returns a copy of this renderer with the customizations applied by [block] (overriding [KaRenderingOption]s or pushing + * additional [KaPieceRenderer]s). This renderer is left unchanged. + */ + public fun copy(block: KaRendererBuilder.() -> Unit): KaRenderer + + @KaExperimentalApi + public companion object { + /** The standard [KaRenderer], which renders declarations and types close to their Kotlin source form. */ + public val default: KaRenderer + get() { + @OptIn(KaExperimentalApi::class) + return service<KaRendererProvider>().defaultRenderer + } + } +} + +/** Renders the [symbol] into a string. */ +@KaExperimentalApi +context(session: KaSession) +public fun KaRenderer.renderToString(symbol: KaSymbol): String { + val output = KaRenderingOutput.plainString() + render(symbol, output) + return output.toString() +} + +/** Renders the [type] into a string. */ +@KaExperimentalApi +context(session: KaSession) +public fun KaRenderer.renderToString(type: KaType): String { + val output = KaRenderingOutput.plainString() + render(type, output) + return output.toString() +} + +/** Renders the [constantValue] into a string. */ +@KaExperimentalApi +context(session: KaSession) +public fun KaRenderer.renderToString(constantValue: KaConstantValue): String { + val output = KaRenderingOutput.plainString() + render(constantValue, output) + return output.toString() +} + +/** A mutable builder for customizing a [KaRenderer], available within [KaRenderer.copy]. */ +@KaExperimentalApi +public interface KaRendererBuilder { + /** Pushes [renderer] on top of the stack for its [KaPieceRenderer.piece], so it takes precedence over the renderers below it. */ + public fun <T> push(renderer: KaPieceRenderer<T>) + + /** Overrides the value of [option]. */ + public fun <T> set(option: KaRenderingOption<T>, value: T) + + /** Resets [option] back to its [KaRenderingOption.defaultValue]. */ + public fun <T> unset(option: KaRenderingOption<T>) +}
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaRenderingOption.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaRenderingOption.kt new file mode 100644 index 0000000..e9e1de0 --- /dev/null +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaRenderingOption.kt
@@ -0,0 +1,155 @@ +/* + * Copyright 2010-2026 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.rendering + +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotated +import org.jetbrains.kotlin.analysis.api.scopes.declaredMemberScope +import org.jetbrains.kotlin.analysis.api.scopes.staticDeclaredMemberScope +import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaEnumEntrySymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaParameterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolOrigin +import org.jetbrains.kotlin.analysis.api.symbols.KaTypeParameterSymbol +import org.jetbrains.kotlin.analysis.api.types.KaType +import org.jetbrains.kotlin.analysis.api.types.augmentedByWarningLevelAnnotations + +/** + * A typed configuration flag that influences rendering. Its [defaultValue] is used unless overridden via [KaRendererBuilder.set]. + * + * The available options are defined as properties of the companion object. + */ +@KaExperimentalApi +public class KaRenderingOption<T> internal constructor(public val defaultValue: T) { + @KaExperimentalApi + public companion object { + /** How qualified class type names are rendered (with the package, with outer classifiers, or as a simple name). */ + public val ClassTypeQualification: KaRenderingOption<KaClassTypeQualification> = + KaRenderingOption(KaClassTypeQualification.WITH_OUTER_CLASSIFIERS) + + /** + * A transformation applied to every rendered [KaType] before it is printed. + * + * The transformation is applied to each type as it is rendered, so it also affects nested types, such as type arguments, upper + * bounds, and the components of flexible and intersection types. It runs before [TypeApproximation]. + * + * By default, warning-level nullability annotations are treated as strict ones (see [augmentedByWarningLevelAnnotations]), so + * `@RecentlyNullable X!` is rendered as `X?`. + */ + public val TypeTransformation: KaRenderingOption<context(KaSession, KaRenderingContext) (KaType) -> KaType> = + KaRenderingOption { type -> type.augmentedByWarningLevelAnnotations } + + /** + * Whether every rendered [KaType] is approximated to a type that can be written in Kotlin source code, and in which direction. + * + * The approximation is applied to each type as it is rendered, so it also affects nested types. It runs after + * [TypeTransformation]. + */ + public val TypeApproximation: KaRenderingOption<KaTypeApproximation> = + KaRenderingOption(KaTypeApproximation.NONE) + + /** Whether the primary constructor is rendered in the class header (e.g. `class Foo(x: Int)`) rather than as a body member. */ + public val PrimaryConstructorInClassHeader: KaRenderingOption<Boolean> = + KaRenderingOption(true) + + /** Whether the `context(...)` receivers of the given element are placed on their own line rather than inline. */ + public val ContextReceiversOnNewLine: KaRenderingOption<context(KaSession, KaRenderingContext) (Any) -> Boolean> = + KaRenderingOption { _ -> true } + + /** Whether the annotations of the given element are placed on their own line rather than inline before it. */ + public val AnnotationsOnNewLine: KaRenderingOption<context(KaSession, KaRenderingContext) (KaAnnotated) -> Boolean> = + KaRenderingOption { value -> + when (value) { + is KaType, is KaTypeParameterSymbol, is KaParameterSymbol, is KaEnumEntrySymbol -> false + is KaConstructorSymbol -> !value.isPrimary + else -> true + } + } + + /** Whether a blank line separates consecutive members within a class body. */ + public val ExtraLineBetweenMembers: KaRenderingOption<Boolean> = + KaRenderingOption(true) + + /** The symbol origins whose members are excluded from a rendered class body (e.g. inherited, generated, or delegated members). */ + public val ClassMemberOrigins: KaRenderingOption<Set<KaSymbolOrigin>> = + KaRenderingOption( + setOf( + KaSymbolOrigin.DELEGATED, + KaSymbolOrigin.SOURCE_MEMBER_GENERATED, + KaSymbolOrigin.SUBSTITUTION_OVERRIDE, + KaSymbolOrigin.INTERSECTION_OVERRIDE, + ) + ) + + /** Computes the members to render for a class, in the order they are gathered. */ + public val ClassMembers: KaRenderingOption<context(KaSession, KaRenderingContext) (KaClassSymbol) -> List<KaSymbol>> = + KaRenderingOption { classSymbol -> + val context = contextOf<KaRenderingContext>() + val excludePrimaryConstructor = context.valueFor(PrimaryConstructorInClassHeader) + val allowedOrigins = context.valueFor(ClassMemberOrigins) + + buildList { + val declaredScope = classSymbol.declaredMemberScope + + if (!classSymbol.classKind.isObject) { + addAll(declaredScope.constructors.filter { !excludePrimaryConstructor || !it.isPrimary }.toList()) + } + + addAll(declaredScope.callables.toList()) + + val staticDeclaredScope = classSymbol.staticDeclaredMemberScope + addAll(staticDeclaredScope.callables.toList()) + addAll(staticDeclaredScope.classifiers.toList()) + }.filterNot { it.origin in allowedOrigins } + } + + /** A comparator establishing the order in which class members are rendered. The default (returning `0`) keeps the [ClassMembers] order. */ + public val ClassMemberOrdering: KaRenderingOption<context(KaSession, KaRenderingContext) (KaSymbol, KaSymbol) -> Int> = + KaRenderingOption { _, _ -> 0 } + } +} + +/** Controls how qualified the name of a class type is rendered. */ +@KaExperimentalApi +public enum class KaClassTypeQualification { + /** The fully qualified name, including the package, e.g. `org.example.Foo.Nested`. */ + FULLY_QUALIFIED, + + /** The name prefixed with its outer classifiers, but without the package, e.g. `Foo.Nested`. */ + WITH_OUTER_CLASSIFIERS, + + /** The simple name only, e.g. `Nested`. */ + SIMPLE, +} + +/** + * Controls whether rendered types are approximated to *denotable* types (types which can be written in Kotlin source code), and in which + * direction. + * + * Approximation matters for types that cannot be written down by a user, such as captured types, intersection types, or types with + * flexible bounds. Such a type usually has both a denotable subtype and a denotable supertype, and the correct choice depends on the + * position in which the type is used: an output position (e.g. a return type) requires a supertype, while an input position (e.g. a value + * parameter type) requires a subtype. + * + * @see KaRenderingOption.TypeApproximation + */ +@KaExperimentalApi +public enum class KaTypeApproximation { + /** Types are rendered as they are, even if the result cannot be written in Kotlin source code. */ + NONE, + + /** Every type is approximated to its closest denotable subtype, which is correct for input positions, such as parameter types. */ + TO_DENOTABLE_SUBTYPE, + + /** + * Every type is approximated to its closest denotable supertype, which is correct for output positions, such as return types. + * Locally declared types are approximated to local supertypes. + */ + TO_DENOTABLE_SUPERTYPE, +}
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaRenderingOutput.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaRenderingOutput.kt new file mode 100644 index 0000000..780ea30 --- /dev/null +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaRenderingOutput.kt
@@ -0,0 +1,106 @@ +/* + * Copyright 2010-2026 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.rendering + +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi +import org.jetbrains.kotlin.analysis.api.lifetime.KaLifetimeOwner +import org.jetbrains.kotlin.analysis.api.lifetime.KaLifetimeToken +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol + +/** + * A destination for rendered output. A [KaRenderer] writes text fragments, each tagged with [KaTextAttribute]s that describe + * its semantic role, so a output may add syntax highlighting, produce plain text, or ignore the attributes entirely. + * + * All methods return `this` to allow chaining. + */ +@KaExperimentalApi +public interface KaRenderingOutput { + /** Appends [text] tagged with the given [attributes]. */ + public fun append(text: String, attributes: Set<KaTextAttribute>): KaRenderingOutput + + /** Appends [text] tagged with a single [attribute]. */ + public fun append(text: String, attribute: KaTextAttribute): KaRenderingOutput { + return append(text, setOf(attribute)) + } + + /** Increases the indentation level applied at the start of subsequent lines. */ + public fun indent(): KaRenderingOutput + + /** Decreases the indentation level. Must be balanced with a preceding [indent]. */ + public fun unindent(): KaRenderingOutput + + /** Starts a new line at the current indentation level. */ + public fun newLine(): KaRenderingOutput + + /** Appends a single space. */ + public fun space(): KaRenderingOutput + + @KaExperimentalApi + public companion object { + /** + * A simple [KaRenderingOutput] that accumulates rendered text into a plain [String], ignoring all [KaTextAttribute]s. + * Uses four spaces for member indentation. + * + * The rendered text is available via [toString]. + */ + public fun plainString(): KaRenderingOutput { + return KaStringRenderingOutput(indentationUnit = " ") + } + + /** + * A simple [KaRenderingOutput] that accumulates rendered text into a plain [String], ignoring all [KaTextAttribute]s. + * Uses the [indentationUnit] for member indentation. + * + * The rendered text is available via [toString]. + */ + public fun plainString(indentationUnit: String): KaRenderingOutput { + return KaStringRenderingOutput(indentationUnit) + } + } +} + +/** Describes the semantic role of a fragment of rendered text, e.g. for syntax highlighting. */ +@KaExperimentalApi +public sealed interface KaTextAttribute { + /** A hard keyword or soft/modifier keyword, such as `fun`, `val`, or `suspend`. */ + @KaExperimentalApi + public object Keyword : KaTextAttribute + + /** Punctuation, such as `:`, `,`, or `?`. */ + @KaExperimentalApi + public object Punctuation : KaTextAttribute + + /** An opening bracket of a group, such as `(`, `<`, or `{`. */ + @KaExperimentalApi + public object GroupStart : KaTextAttribute + + /** A closing bracket of a group, such as `)`, `>`, or `}`. */ + @KaExperimentalApi + public object GroupEnd : KaTextAttribute + + /** An identifier, such as a declaration or type name. See [Symbol] when the referenced symbol is known. */ + @KaExperimentalApi + public object Identifier : KaTextAttribute + + /** Whitespace that is not a line break. */ + @KaExperimentalApi + public object Whitespace : KaTextAttribute + + /** A string or character literal, such as `"text"` or `'c'`. */ + @KaExperimentalApi + public object StringLiteral : KaTextAttribute + + /** A numeric literal, such as `42`, `2f`, or `3u`. */ + @KaExperimentalApi + public object NumberLiteral : KaTextAttribute + + /** An identifier that references a known [symbol]. */ + @KaExperimentalApi + public class Symbol(public val symbol: KaSymbol) : KaTextAttribute, KaLifetimeOwner { + override val token: KaLifetimeToken + get() = symbol.token + } +}
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaStringRenderingOutput.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaStringRenderingOutput.kt new file mode 100644 index 0000000..5ce8d0a --- /dev/null +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/rendering/KaStringRenderingOutput.kt
@@ -0,0 +1,58 @@ +/* + * Copyright 2010-2026 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.rendering + +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi + +@KaExperimentalApi +internal class KaStringRenderingOutput(private val indentationUnit: String) : KaRenderingOutput { + private val builder: StringBuilder = StringBuilder() + private var indentLevel: Int = 0 + private var atLineStart: Boolean = true + + override fun append(text: String, attributes: Set<KaTextAttribute>): KaRenderingOutput { + if (text.isEmpty()) return this + indentIfNeeded() + builder.append(text) + return this + } + + override fun indent(): KaRenderingOutput { + indentLevel++ + return this + } + + override fun unindent(): KaRenderingOutput { + require(indentLevel > 0) { "Unbalanced indent()/unindent() calls." } + indentLevel-- + return this + } + + override fun newLine(): KaRenderingOutput { + // Avoid trailing whitespace at the end of the line. + while (builder.isNotEmpty() && builder.last() == ' ') { + builder.deleteCharAt(builder.length - 1) + } + builder.append('\n') + atLineStart = true + return this + } + + override fun space(): KaRenderingOutput { + indentIfNeeded() + builder.append(' ') + return this + } + + private fun indentIfNeeded() { + if (atLineStart) { + repeat(indentLevel) { builder.append(indentationUnit) } + atLineStart = false + } + } + + override fun toString(): String = builder.toString().trim() +}
diff --git a/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/annotation.new.rendered b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/annotation.new.rendered new file mode 100644 index 0000000..714c8a9 --- /dev/null +++ b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/annotation.new.rendered
@@ -0,0 +1,26 @@ +@Target(allowedTargets = [AnnotationTarget.ANNOTATION_CLASS]) +annotation class base + +@base +annotation class derived + +@base +class correct(x: Int) { + @base + constructor() + + @base + val x: Int +} + +@base +enum class My { + @base FIRST, + @base SECOND +} + +@base +fun foo(@base y: @base Int): Int + +@base +val z: Int
diff --git a/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/contextParameter.new.rendered b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/contextParameter.new.rendered new file mode 100644 index 0000000..6a6ddc6 --- /dev/null +++ b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/contextParameter.new.rendered
@@ -0,0 +1,15 @@ +interface Context2<A> { + fun getContextElement(): A +} + +class Context2Impl<A>(value: A) : Context2<A> { + override fun getContextElement(): A + + val value: A +} + +context(b: B, `fun`: A) +fun foo() + +context(`<unused var>`: Context2<C<String>>) +fun bar()
diff --git a/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/simpleClass.new.rendered b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/simpleClass.new.rendered new file mode 100644 index 0000000..0a18b80 --- /dev/null +++ b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/simpleClass.new.rendered
@@ -0,0 +1,19 @@ +interface SomeInterface { + fun foo(x: Int, y: String): String + + val bar: Boolean +} + +class SomeClass : SomeInterface { + lateinit var fau: Double + + override fun foo(x: Int, y: String): String + + override var bar: Boolean + get() + set(value: Boolean) + + private val baz: Int +} + +value class InlineClass
diff --git a/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/typeParameters.new.rendered b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/typeParameters.new.rendered new file mode 100644 index 0000000..d2375df --- /dev/null +++ b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/typeParameters.new.rendered
@@ -0,0 +1,20 @@ +@Target(allowedTargets = [AnnotationTarget.TYPE_PARAMETER]) +annotation class A + +interface List<@A out T : Any> { + infix fun concat(other: List<T>): List<T> + + operator fun get(index: Int): T +} + +typealias StringList = List<out String> + +typealias AnyList = List<*> + +abstract class AbstractList<out T : Any> : List<T> + +class SomeList : AbstractList<Int>() { + override infix fun concat(other: List<Int>): List<Int> + + override operator fun get(index: Int): Int +}
diff --git a/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/annotationOnTypes.new.rendered b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/annotationOnTypes.new.rendered new file mode 100644 index 0000000..770023c --- /dev/null +++ b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/annotationOnTypes.new.rendered
@@ -0,0 +1,9 @@ +@Target(allowedTargets = [AnnotationTarget.TYPE]) +annotation class A1 + +@Target(allowedTargets = [AnnotationTarget.TYPE]) +annotation class A2(value: String) { + val value: String +} + +fun x(): @A1 @A2(value = "LIST") List<@A2(value = "INT") Int>
diff --git a/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/annotationOnTypesWithComplexExpression.new.rendered b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/annotationOnTypesWithComplexExpression.new.rendered new file mode 100644 index 0000000..1433689 --- /dev/null +++ b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/annotationOnTypesWithComplexExpression.new.rendered
@@ -0,0 +1,6 @@ +@Target(allowedTargets = [AnnotationTarget.TYPE]) +annotation class A(value: Int) { + val value: Int +} + +fun x(): @A(value = 3) Int
diff --git a/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/approximatedCapturedType.new.rendered b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/approximatedCapturedType.new.rendered new file mode 100644 index 0000000..a37de78 --- /dev/null +++ b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/types/approximatedCapturedType.new.rendered
@@ -0,0 +1,5 @@ +class SimpleClass + +val classloader: ClassLoader! + +val obj: KClass<out Any!>?
diff --git a/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/vararg.new.rendered b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/vararg.new.rendered new file mode 100644 index 0000000..963737f --- /dev/null +++ b/analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/vararg.new.rendered
@@ -0,0 +1,6 @@ +@Target(allowedTargets = [AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE]) +annotation class base + +fun foo1(vararg ints: Int) + +fun foo2(@base vararg ints: Int)