[FIR IDE] Implement fir renderer for IDE

This commit is contained in:
Igor Yakovlev
2021-03-03 16:04:49 +01:00
parent f2e02c2d50
commit 7d3be9eafa
68 changed files with 1976 additions and 222 deletions
@@ -104,6 +104,8 @@ object StandardClassIds {
val MapEntry = Map.createNestedClassId(Name.identifier("Entry"))
val MutableMapEntry = MutableMap.createNestedClassId(Name.identifier("MutableEntry"))
val extensionFunctionType = "ExtensionFunctionType".baseId()
val Suppress = "Suppress".baseId()
val FlexibleNullability = ClassId(FqName("kotlin.internal.ir"), Name.identifier("FlexibleNullability"))
@@ -95,6 +95,7 @@ import org.jetbrains.kotlin.idea.frontend.api.components.AbstractExpectedExpress
import org.jetbrains.kotlin.idea.frontend.api.components.AbstractHLExpressionTypeTest
import org.jetbrains.kotlin.idea.frontend.api.components.AbstractOverriddenDeclarationProviderTest
import org.jetbrains.kotlin.idea.frontend.api.components.AbstractReturnExpressionTargetTest
import org.jetbrains.kotlin.idea.frontend.api.components.AbstractRendererTest
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
import org.jetbrains.kotlin.idea.frontend.api.scopes.AbstractFileScopeTest
import org.jetbrains.kotlin.idea.frontend.api.scopes.AbstractMemberScopeByFqNameTest
@@ -1042,6 +1043,10 @@ fun main(args: Array<String>) {
testClass<AbstractHLExpressionTypeTest> {
model("components/expressionType")
}
testClass<AbstractRendererTest> {
model("components/declarationRenderer")
}
}
testGroup("idea/idea-frontend-fir/idea-fir-low-level-api/tests", "idea/testData") {
@@ -29,7 +29,6 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
KtDiagnosticProviderMixIn,
KtScopeProviderMixIn,
KtCompletionCandidateCheckerMixIn,
KtTypeRendererMixIn,
KtSymbolDeclarationOverridesProviderMixIn,
KtExpressionTypeProviderMixIn,
KtTypeProviderMixIn,
@@ -40,7 +39,8 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
KtExpressionInfoProviderMixIn,
KtSymbolsMixIn,
KtReferenceResolveMixIn,
KtReferenceShortenerMixIn {
KtReferenceShortenerMixIn,
KtSymbolDeclarationRendererMixIn {
override val analysisSession: KtAnalysisSession get() = this
@@ -73,10 +73,8 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
internal val referenceShortener: KtReferenceShortener get() = referenceShortenerImpl
protected abstract val referenceShortenerImpl: KtReferenceShortener
@Suppress("LeakingThis")
protected open val typeRendererImpl: KtTypeRenderer = KtDefaultTypeRenderer(this, token)
internal val typeRenderer: KtTypeRenderer get() = typeRendererImpl
internal val symbolDeclarationRendererProvider: KtSymbolDeclarationRendererProvider get() = symbolDeclarationRendererProviderImpl
protected abstract val symbolDeclarationRendererProviderImpl: KtSymbolDeclarationRendererProvider
internal val expressionTypeProvider: KtExpressionTypeProvider get() = expressionTypeProviderImpl
protected abstract val expressionTypeProviderImpl: KtExpressionTypeProvider
@@ -32,7 +32,7 @@ interface KtSymbolDeclarationOverridesProviderMixIn : KtAnalysisSessionMixIn {
/**
* Return a list of symbols which are **directly** overridden by symbol
**
* E.g, if we have `A.foo` overrides `B.foo` overrides `C.foo`, only declarations directly overriden `B.foo` will be returned
* E.g, if we have `A.foo` overrides `B.foo` overrides `C.foo`, only declarations directly overridden `B.foo` will be returned
*
* Unwraps substituted overridden symbols (see [org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbolOrigin.INTERSECTION_OVERRIDE])
*
@@ -0,0 +1,113 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api.components
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
/**
* KtType to string renderer options
* @see KtType
* @see KtSymbolDeclarationRendererProvider.render
*/
data class KtTypeRendererOptions(
/**
* Render type name without package name for not local types
*/
val shortQualifiedNames: Boolean = false,
/**
* Render function types FunctionN using Kotlin function type syntax
* @see Function
* @sample Function0<Int> returns () -> Int
*/
val renderFunctionType: Boolean = true,
) {
companion object {
val DEFAULT = KtTypeRendererOptions()
val SHORT_NAMES = DEFAULT.copy(shortQualifiedNames = true)
}
}
/**
* KtSymbol to string renderer options
* @see KtSymbol
* @see KtSymbolDeclarationRendererProvider.render
*/
data class KtDeclarationRendererOptions(
/**
* Set of modifiers that needed to be rendered
* @see RendererModifier
*/
val modifiers: Set<RendererModifier> = RendererModifier.ALL,
/**
* Type render options @see KtTypeRendererOptions
* @see KtTypeRendererOptions
*/
val typeRendererOptions: KtTypeRendererOptions = KtTypeRendererOptions.DEFAULT,
/**
* Render Unit return type for functions
*/
val renderUnitReturnType: Boolean = false,
/**
* Normalize java-specific visibilities for java declaration
*/
val normalizedVisibilities: Boolean = false,
/**
* Render containing declarations
*/
val renderContainingDeclarations: Boolean = false,
/**
* Approximate Kotlin not-denotable types into denotable for declarations return type
*/
val approximateTypes: Boolean = false,
) {
companion object {
val DEFAULT = KtDeclarationRendererOptions()
}
}
enum class RendererModifier(val includeByDefault: Boolean) {
VISIBILITY(true),
MODALITY(true),
OVERRIDE(true),
ANNOTATIONS(false),
INNER(true),
DATA(true),
INLINE(true),
EXPECT(true),
ACTUAL(true),
CONST(true),
LATEINIT(true),
FUN(true),
VALUE(true)
;
companion object {
val ALL = values().toSet()
}
}
abstract class KtSymbolDeclarationRendererProvider : KtAnalysisSessionComponent() {
abstract fun render(symbol: KtSymbol, options: KtDeclarationRendererOptions): String
abstract fun render(type: KtType, options: KtTypeRendererOptions): String
}
/**
* Provides services for rendering Symbols and Types into the Kotlin strings
*/
interface KtSymbolDeclarationRendererMixIn : KtAnalysisSessionMixIn {
/**
* Render symbol into the representable Kotlin string
*/
fun KtSymbol.render(options: KtDeclarationRendererOptions = KtDeclarationRendererOptions.DEFAULT): String =
analysisSession.symbolDeclarationRendererProvider.render(this, options)
/**
* Render kotlin type into the representable Kotlin type string
*/
fun KtType.render(options: KtTypeRendererOptions = KtTypeRendererOptions.DEFAULT): String =
analysisSession.symbolDeclarationRendererProvider.render(this, options)
}
@@ -1,193 +0,0 @@
/*
* Copyright 2010-2020 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.idea.frontend.api.components
import org.jetbrains.kotlin.idea.frontend.api.*
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.types.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.types.Variance
abstract class KtTypeRenderer : KtAnalysisSessionComponent() {
abstract fun render(type: KtType, options: KtTypeRendererOptions): String
}
interface KtTypeRendererMixIn : KtAnalysisSessionMixIn {
fun KtType.render(options: KtTypeRendererOptions = KtTypeRendererOptions.DEFAULT): String =
analysisSession.typeRenderer.render(this, options)
}
data class KtTypeRendererOptions(
val renderFqNames: Boolean,
val renderFunctionTypes: Boolean
) {
companion object {
val DEFAULT = KtTypeRendererOptions(
renderFqNames = true,
renderFunctionTypes = true,
)
val SHORT_NAMES = KtTypeRendererOptions(
renderFqNames = false,
renderFunctionTypes = true,
)
}
}
class KtDefaultTypeRenderer(override val analysisSession: KtAnalysisSession, override val token: ValidityToken) : KtTypeRenderer() {
override fun render(type: KtType, options: KtTypeRendererOptions): String = type.withValidityAssertion {
buildString { render(type, options) }
}
private fun StringBuilder.render(type: KtType, options: KtTypeRendererOptions) {
when (type) {
is KtDenotableType -> when (type) {
is KtClassType -> {
renderClassType(type, options)
}
is KtTypeParameterType -> {
append(type.name.asString())
renderNullability(type.nullability)
}
}
is KtNonDenotableType -> when (type) {
is KtFlexibleType -> inParens {
render(type.lowerBound, options)
append("..")
render(type.upperBound, options)
}
is KtIntersectionType -> inParens {
type.conjuncts.forEachIndexed { index, conjunct ->
render(conjunct, options)
if (index != type.conjuncts.lastIndex) {
append("&")
}
}
}
}
is KtErrorType -> {
append(type.error)
}
else -> error("Unsupported type ${type::class}")
}
}
private fun StringBuilder.renderClassType(
type: KtClassType,
options: KtTypeRendererOptions
) {
when {
type is KtUsualClassType || !options.renderFunctionTypes -> {
renderClassTypeAsNonFunctional(type, options)
}
type is KtFunctionalType -> {
renderFunctionalType(type, options)
}
}
}
private fun StringBuilder.renderClassTypeAsNonFunctional(
type: KtClassType,
options: KtTypeRendererOptions
) {
render(type.classId, options)
renderTypeArgumentsIfNotEmpty(type.typeArguments, options)
renderNullability(type.nullability)
}
private fun StringBuilder.renderFunctionalType(
type: KtFunctionalType,
options: KtTypeRendererOptions
) {
wrapIf(type.nullability == KtTypeNullability.NULLABLE, left = "(", right = ")?") {
if (type.isSuspend) append("suspend ")
renderReceiverTypeOfFunctionalType(type, options)
renderParametersOfFunctionalType(type, options)
append(" -> ")
render(type.typeArguments.last(), options)
}
}
private fun StringBuilder.renderReceiverTypeOfFunctionalType(
type: KtFunctionalType,
options: KtTypeRendererOptions
) {
type.receiverType?.let { receiverType ->
render(receiverType, options)
append(".")
}
}
private fun StringBuilder.renderParametersOfFunctionalType(
type: KtFunctionalType,
options: KtTypeRendererOptions
) {
append("(")
val parameterTypes = type.parameterTypes
type.parameterTypes.forEachIndexed { index, parameterType ->
render(parameterType, options)
if (index != parameterTypes.lastIndex) {
append(", ")
}
}
append(")")
}
private fun StringBuilder.renderTypeArgumentsIfNotEmpty(typeArguments: List<KtTypeArgument>, options: KtTypeRendererOptions) {
if (typeArguments.isNotEmpty()) {
append("<")
typeArguments.forEachIndexed { index, typeArgument ->
render(typeArgument, options)
if (index != typeArguments.lastIndex) {
append(", ")
}
}
append(">")
}
}
private fun StringBuilder.render(typeArgument: KtTypeArgument, options: KtTypeRendererOptions) {
when (typeArgument) {
KtStarProjectionTypeArgument -> {
append("*")
}
is KtTypeArgumentWithVariance -> {
val varianceWithSpace = when (typeArgument.variance) {
Variance.OUT_VARIANCE -> "out "
Variance.IN_VARIANCE -> "in "
Variance.INVARIANT -> ""
}
append(varianceWithSpace)
render(typeArgument.type, options)
}
}
}
private fun StringBuilder.renderNullability(nullability: KtTypeNullability) {
if (nullability == KtTypeNullability.NULLABLE) {
append("?")
}
}
private fun StringBuilder.render(classId: ClassId, options: KtTypeRendererOptions) {
if (options.renderFqNames) {
append(classId.asString().replace('/', '.'))
} else {
append(classId.shortClassName.asString())
}
}
private inline fun StringBuilder.inParens(render: StringBuilder.() -> Unit) {
append("(")
render()
append(")")
}
private inline fun StringBuilder.wrapIf(condition: Boolean, left: String, right: String, render: StringBuilder.() -> Unit) {
if (condition) append(left)
render()
if (condition) append(right)
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.fir.low.level.api.api
import org.jetbrains.kotlin.fir.containingClassForLocal
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.classId
import org.jetbrains.kotlin.fir.declarations.isLocal
import org.jetbrains.kotlin.fir.resolve.firProvider
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
fun FirRegularClass.collectDesignation(): List<FirClassLikeDeclaration<*>> {
val designation = mutableListOf<FirClassLikeDeclaration<*>>()
designation.add(this)
fun FirRegularClass.collectForNonLocal() {
require(!isLocal)
val firProvider = session.firProvider
var containingClassId = classId.outerClassId
while (containingClassId != null) {
val currentClass = firProvider.getFirClassifierByFqName(containingClassId) ?: break
designation.add(currentClass)
containingClassId = containingClassId.outerClassId
}
}
fun FirRegularClass.collectForLocal() {
require(isLocal)
var containingClassLookUp = containingClassForLocal()
while (containingClassLookUp != null && containingClassLookUp.classId.isLocal) {
val currentClass = containingClassLookUp.toFirRegularClass(session) ?: break
designation.add(currentClass)
containingClassLookUp = currentClass.containingClassForLocal()
}
}
if (isLocal) collectForLocal() else collectForNonLocal()
designation.reverse()
return designation
}
@@ -5,12 +5,14 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.util
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.realPsi
import org.jetbrains.kotlin.fir.resolve.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.idea.fir.low.level.api.api.InvalidFirElementTypeException
import org.jetbrains.kotlin.idea.fir.low.level.api.api.collectDesignation
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
@@ -135,18 +137,10 @@ private fun KtTypeAlias.findFir(firSymbolProvider: FirSymbolProvider): FirTypeAl
val FirDeclaration.isGeneratedDeclaration
get() = realPsi == null
internal fun FirDeclaration.collectDesignation(): List<FirDeclaration> {
require(this is FirCallableDeclaration<*>)
val designation = mutableListOf<FirDeclaration>()
val firProvider = session.firIdeProvider
var containingClassId = containingClass()?.classId
while (containingClassId != null) {
val klass = firProvider.getFirClassifierByFqName(containingClassId)
if (klass != null) {
designation.add(klass)
}
containingClassId = containingClassId.outerClassId
}
designation.reverse()
return designation
internal fun FirCallableDeclaration<*>.collectDesignation(): List<FirClassLikeDeclaration<*>> {
return containingClass()
?.toFirRegularClass(session)
?.collectDesignation()
?: return emptyList()
}
@@ -13,11 +13,10 @@ import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForCompletion
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getFirFile
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getResolveState
import org.jetbrains.kotlin.idea.frontend.api.InvalidWayOfUsingAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.tokens.ReadActionConfinementValidityToken
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.components.KtSymbolDeclarationRendererProvider
import org.jetbrains.kotlin.idea.frontend.api.fir.components.*
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbolProvider
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.EnclosingDeclarationContext
@@ -57,6 +56,9 @@ private constructor(
override val referenceShortenerImpl = KtFirReferenceShortener(this, token, firResolveState)
override val symbolDeclarationRendererProviderImpl: KtSymbolDeclarationRendererProvider =
KtFirSymbolDeclarationRendererProvider(this, token)
override val expressionInfoProviderImpl = KtFirExpressionInfoProvider(this, token)
override val typeProviderImpl = KtFirTypeProvider(this, token)
@@ -127,4 +129,3 @@ internal sealed class KtFirAnalysisSessionContext {
val fakeKtFile = fakeContextElement.containingKtFile
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api.fir.components
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.idea.frontend.api.components.KtDeclarationRendererOptions
import org.jetbrains.kotlin.idea.frontend.api.components.KtSymbolDeclarationRendererProvider
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.fir.renderer.ConeTypeIdeRenderer
import org.jetbrains.kotlin.idea.frontend.api.fir.renderer.FirIdeRenderer
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol
import org.jetbrains.kotlin.idea.frontend.api.fir.types.KtFirType
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
internal class KtFirSymbolDeclarationRendererProvider(
override val analysisSession: KtFirAnalysisSession,
override val token: ValidityToken,
) : KtSymbolDeclarationRendererProvider() {
override fun render(type: KtType, options: KtTypeRendererOptions): String {
require(type is KtFirType)
return ConeTypeIdeRenderer(analysisSession.firResolveState.rootModuleSession, options).renderType(type.coneType)
}
override fun render(symbol: KtSymbol, options: KtDeclarationRendererOptions): String {
require(symbol is KtFirSymbol<*>)
val containingSymbol = with(analysisSession) {
(symbol as? KtSymbolWithKind)?.getContainingSymbol()
}
check(containingSymbol is KtFirSymbol<*>?)
val phaseNeeded =
if (options.renderContainingDeclarations) FirResolvePhase.BODY_RESOLVE else FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE
return (containingSymbol ?: symbol).firRef.withFir(phaseNeeded) { fir ->
val containingFir = containingSymbol?.firRef?.withFirUnsafe { it }
FirIdeRenderer.render(fir, containingFir, options, fir.session)
}
}
}
@@ -0,0 +1,306 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api.fir.renderer
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
import org.jetbrains.kotlin.fir.declarations.isInner
import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.resolve.inference.*
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.idea.asJava.applyIf
import org.jetbrains.kotlin.idea.fir.low.level.api.api.collectDesignation
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
import org.jetbrains.kotlin.name.StandardClassIds
internal class ConeTypeIdeRenderer(
private val session: FirSession,
private val options: KtTypeRendererOptions,
) {
companion object {
const val ERROR_TYPE_TEXT = "ERROR_TYPE"
}
private fun StringBuilder.appendError(message: String? = null) {
append(ERROR_TYPE_TEXT)
if (message != null) append(" $message")
}
private var filterExtensionFunctionType: Boolean = false
private fun StringBuilder.renderAnnotationList(annotations: List<FirAnnotationCall>?) {
if (annotations != null) {
val filteredExtensionIfNeeded = annotations.applyIf(filterExtensionFunctionType) {
annotations.filterNot { it.toAnnotationClassId() == StandardClassIds.extensionFunctionType }
}
renderAnnotations(this@ConeTypeIdeRenderer, filteredExtensionIfNeeded, session)
}
}
fun renderType(type: ConeTypeProjection, annotations: List<FirAnnotationCall>? = null): String = buildString {
when (type) {
is ConeKotlinErrorType -> {
appendError()
}
//is Dynamic??? -> append("dynamic")
is ConeClassLikeType -> {
if (options.renderFunctionType && shouldRenderAsPrettyFunctionType(type)) {
val oldFilterExtensionFunctionType = filterExtensionFunctionType
filterExtensionFunctionType = true
renderAnnotationList(annotations)
renderFunctionType(type)
filterExtensionFunctionType = oldFilterExtensionFunctionType
} else {
renderAnnotationList(annotations)
renderTypeConstructorAndArguments(type)
}
}
is ConeTypeParameterType -> {
renderAnnotationList(annotations)
append(type.lookupTag.name.asString())
renderNullability(type.type)
}
is ConeIntersectionType -> {
renderAnnotationList(annotations)
type.intersectedTypes.joinTo(this, "&", prefix = "(", postfix = ")") {
renderType(it)
}
renderNullability(type.type)
}
is ConeFlexibleType -> {
renderAnnotationList(annotations)
append(renderFlexibleType(renderType(type.lowerBound), renderType(type.upperBound)))
}
else -> appendError("Unexpected cone type ${type::class.qualifiedName}")
}
}
private fun StringBuilder.renderNullability(type: ConeKotlinType) {
if (type.nullability == ConeNullability.NULLABLE) {
append("?")
}
}
fun shouldRenderAsPrettyFunctionType(type: ConeKotlinType): Boolean {
return type.type.isBuiltinFunctionalType(session) && type.typeArguments.none { it.kind == ProjectionKind.STAR }
}
private fun differsOnlyInNullability(lower: String, upper: String) =
lower == upper.replace("?", "") || upper.endsWith("?") && ("$lower?") == upper || "($lower)?" == upper
private fun renderFlexibleType(lowerRendered: String, upperRendered: String): String {
if (differsOnlyInNullability(lowerRendered, upperRendered)) {
if (upperRendered.startsWith("(")) {
// the case of complex type, e.g. (() -> Unit)?
return "($lowerRendered)!"
}
return "$lowerRendered!"
}
val kotlinCollectionsPrefix = "kotlin.collections."
val mutablePrefix = "Mutable"
// java.util.List<Foo> -> (Mutable)List<Foo!>!
val simpleCollection = replacePrefixes(
lowerRendered,
kotlinCollectionsPrefix + mutablePrefix,
upperRendered,
kotlinCollectionsPrefix,
"$kotlinCollectionsPrefix($mutablePrefix)"
)
if (simpleCollection != null) return simpleCollection
// java.util.Map.Entry<Foo, Bar> -> (Mutable)Map.(Mutable)Entry<Foo!, Bar!>!
val mutableEntry = replacePrefixes(
lowerRendered,
kotlinCollectionsPrefix + "MutableMap.MutableEntry",
upperRendered,
kotlinCollectionsPrefix + "Map.Entry",
"$kotlinCollectionsPrefix(Mutable)Map.(Mutable)Entry"
)
if (mutableEntry != null) return mutableEntry
val kotlinPrefix = "kotlin."
// Foo[] -> Array<(out) Foo!>!
val array = replacePrefixes(
lowerRendered,
kotlinPrefix + "Array<",
upperRendered,
kotlinPrefix + "Array<out ",
kotlinPrefix + "Array<(out) "
)
if (array != null) return array
return "($lowerRendered..$upperRendered)"
}
private fun replacePrefixes(
lowerRendered: String,
lowerPrefix: String,
upperRendered: String,
upperPrefix: String,
foldedPrefix: String
): String? {
if (lowerRendered.startsWith(lowerPrefix) && upperRendered.startsWith(upperPrefix)) {
val lowerWithoutPrefix = lowerRendered.substring(lowerPrefix.length)
val upperWithoutPrefix = upperRendered.substring(upperPrefix.length)
val flexibleCollectionName = foldedPrefix + lowerWithoutPrefix
if (lowerWithoutPrefix == upperWithoutPrefix) return flexibleCollectionName
if (differsOnlyInNullability(lowerWithoutPrefix, upperWithoutPrefix)) {
return "$flexibleCollectionName!"
}
}
return null
}
private fun StringBuilder.renderTypeConstructorAndArguments(type: ConeClassLikeType) {
fun renderTypeArguments(typeArguments: Array<out ConeTypeProjection>, range: IntRange) {
if (range.any()) {
typeArguments.slice(range).joinTo(this, ", ", prefix = "<", postfix = ">") {
renderTypeProjection(it)
}
}
}
val classSymbolToRender = type.lookupTag.toSymbol(session)
if (classSymbolToRender == null) {
appendError("Unresolved type")
return
}
if (!options.shortQualifiedNames && !classSymbolToRender.classId.isLocal) {
val packageName = classSymbolToRender.classId.packageFqName.asString()
if (packageName.isNotEmpty()) {
append(packageName).append(".")
}
}
if (classSymbolToRender !is FirRegularClassSymbol) {
append(classSymbolToRender.classId.shortClassName)
if (type.typeArguments.any()) {
type.typeArguments.joinTo(this, ", ", prefix = "<", postfix = ">") {
renderTypeProjection(it)
}
}
return
}
val classToRender = classSymbolToRender.fir
val designation = classToRender.collectDesignation()
var typeParametersLeft = type.typeArguments.count()
fun needToRenderTypeParameters(index: Int): Boolean {
if (typeParametersLeft <= 0) return false
return index == designation.lastIndex ||
(designation[index] as? FirRegularClass)?.isInner == true ||
(designation[index + 1] as? FirRegularClass)?.isInner == true
}
designation.filterIsInstance<FirRegularClass>().forEachIndexed { index, currentClass ->
if (index != 0) append(".")
append(currentClass.name)
if (needToRenderTypeParameters(index)) {
val typeParametersCount = currentClass.typeParameters.count { it is FirTypeParameter }
val begin = typeParametersLeft - typeParametersCount
val end = typeParametersLeft
check(begin >= 0)
typeParametersLeft -= typeParametersCount
renderTypeArguments(type.typeArguments, begin until end)
}
}
renderNullability(type)
}
private fun renderTypeProjection(typeProjection: ConeTypeProjection): String {
val type = typeProjection.type?.let(::renderType) ?: "???"
return when (typeProjection.kind) {
ProjectionKind.STAR -> "*"
ProjectionKind.IN -> "in $type"
ProjectionKind.OUT -> "out $type"
ProjectionKind.INVARIANT -> type
}
}
private fun StringBuilder.renderFunctionType(type: ConeClassLikeType) {
val lengthBefore = length
val hasAnnotations = length != lengthBefore
val isSuspend = type.isSuspendFunctionType(session)
val isNullable = type.isMarkedNullable
val receiverType = type.receiverType(session)
val needParenthesis = isNullable || (hasAnnotations && receiverType != null)
if (needParenthesis) {
if (isSuspend) {
insert(lengthBefore, '(')
} else {
if (hasAnnotations) {
check(last() == ' ')
if (get(lastIndex - 1) != ')') {
// last annotation rendered without parenthesis - need to add them otherwise parsing will be incorrect
insert(lastIndex, "()")
}
}
append("(")
}
}
if (isSuspend) {
append("suspend")
append(" ")
}
if (receiverType != null) {
val surroundReceiver = shouldRenderAsPrettyFunctionType(receiverType) &&
!receiverType.isMarkedNullable ||
receiverType.isSuspendFunctionType(session)
if (surroundReceiver) {
append("(")
}
append(renderType(receiverType))
if (surroundReceiver) {
append(")")
}
append(".")
}
append("(")
val notNullParametersType = type
.valueParameterTypesIncludingReceiver(session)
.filterNotNull()
.applyIf(receiverType != null) { drop(1) }
notNullParametersType.forEachIndexed { index, typeProjection ->
if (index != 0) append(", ")
append(renderTypeProjection(typeProjection))
}
append(") -> ")
val returnType = type.returnType(session)
if (returnType != null) {
append(renderType(returnType))
} else {
appendError()
}
if (needParenthesis) append(")")
renderNullability(type)
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api.fir.renderer
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.mapAnnotationParameters
internal fun StringBuilder.renderAnnotations(
coneTypeIdeRenderer: ConeTypeIdeRenderer,
annotations: List<FirAnnotationCall>,
session: FirSession
) {
for (annotation in annotations) {
if (!annotation.isParameterName()) {
append(renderAnnotation(annotation, coneTypeIdeRenderer, session))
append(" ")
}
}
}
private fun FirAnnotationCall.isParameterName(): Boolean {
return toAnnotationClassId().asSingleFqName() == StandardNames.FqNames.parameterName
}
private fun renderAnnotation(annotation: FirAnnotationCall, coneTypeIdeRenderer: ConeTypeIdeRenderer, session: FirSession): String {
return buildString {
append('@')
val resolvedTypeRef = annotation.typeRef as? FirResolvedTypeRef
check(resolvedTypeRef != null)
append(coneTypeIdeRenderer.renderType(resolvedTypeRef.type))
val arguments = renderAndSortAnnotationArguments(annotation, session)
if (arguments.isNotEmpty()) {
arguments.joinTo(this, ", ", "(", ")")
}
}
}
private fun renderAndSortAnnotationArguments(descriptor: FirAnnotationCall, session: FirSession): List<String> {
val argumentList = mapAnnotationParameters(descriptor, session).entries.map { (name, value) ->
"$name = ${renderConstant(value)}"
}
return argumentList.sorted()
}
private fun renderConstant(value: FirExpression): String {
return when (value) {
is FirConstExpression<*> -> value.toString()
else -> "NOT_CONST_EXPRESSION"
}
}
@@ -0,0 +1,676 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api.fir.renderer
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirBlock
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.idea.asJava.applyIf
import org.jetbrains.kotlin.idea.frontend.api.components.KtDeclarationRendererOptions
import org.jetbrains.kotlin.idea.frontend.api.components.RendererModifier
import org.jetbrains.kotlin.idea.frontend.api.fir.types.PublicTypeApproximator
import org.jetbrains.kotlin.idea.util.ifTrue
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
internal class FirIdeRenderer private constructor(
private var containingDeclaration: FirDeclaration?,
private val options: KtDeclarationRendererOptions,
private val session: FirSession
) : FirVisitor<Unit, StringBuilder>() {
private val typeIdeRenderer: ConeTypeIdeRenderer = ConeTypeIdeRenderer(session, options.typeRendererOptions)
private fun StringBuilder.renderAnnotations(annotated: FirAnnotatedDeclaration) {
if (RendererModifier.ANNOTATIONS in options.modifiers) {
renderAnnotations(typeIdeRenderer, annotated.annotations, session)
}
}
private fun renderType(type: ConeTypeProjection, annotations: List<FirAnnotationCall>? = null): String =
typeIdeRenderer.renderType(type, annotations)
private fun renderType(firRef: FirTypeRef, approximate: Boolean = false): String {
require(firRef is FirResolvedTypeRef)
val approximatedIfNeeded = approximate.ifTrue {
PublicTypeApproximator.approximateTypeToPublicDenotable(firRef.coneType, session)
} ?: firRef.coneType
return renderType(approximatedIfNeeded, firRef.annotations)
}
private fun StringBuilder.renderName(declaration: FirDeclaration) {
if (declaration is FirAnonymousObject) {
append("<no name provided>")
return
}
val name = when (declaration) {
is FirRegularClass -> declaration.name
is FirSimpleFunction -> declaration.name
is FirProperty -> declaration.name
is FirValueParameter -> declaration.name
is FirTypeParameter -> declaration.name
is FirTypeAlias -> declaration.name
is FirEnumEntry -> declaration.name
else -> TODO("Unexpected declaration ${declaration::class.qualifiedName}")
}
append(name.render())
}
private fun StringBuilder.renderCompanionObjectName(firClass: FirRegularClass) {
if (firClass.name != SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) {
tabRightBySpace()
append(firClass.name.render())
}
}
private fun StringBuilder.renderVisibility(visibility: Visibility) {
if (RendererModifier.VISIBILITY !in options.modifiers) return
val currentVisibility = when (visibility) {
Visibilities.Local -> Visibilities.Public
Visibilities.PrivateToThis -> Visibilities.Public
Visibilities.InvisibleFake -> Visibilities.Public
Visibilities.Inherited -> Visibilities.Public
Visibilities.Unknown -> Visibilities.Public
else -> visibility
}.applyIf(options.normalizedVisibilities) {
normalize()
}
if (currentVisibility == Visibilities.DEFAULT_VISIBILITY) return
append(currentVisibility.internalDisplayName)
append(" ")
}
private fun StringBuilder.renderModality(modality: Modality, defaultModality: Modality) {
if (modality == defaultModality) return
renderModifier(RendererModifier.MODALITY in options.modifiers, modality.name.toLowerCaseAsciiOnly())
}
private fun FirMemberDeclaration.implicitModalityWithoutExtensions(containingDeclaration: FirDeclaration?): Modality {
if (this is FirRegularClass) {
return if (classKind == ClassKind.INTERFACE) Modality.ABSTRACT else Modality.FINAL
}
val containingFirClass = containingDeclaration as? FirRegularClass ?: return Modality.FINAL
if (this !is FirCallableMemberDeclaration<*>) return Modality.FINAL
if (isOverride) {
if (containingFirClass.modality != Modality.FINAL) return Modality.OPEN
}
return if (containingFirClass.classKind == ClassKind.INTERFACE && this.visibility != Visibilities.Private) {
if (this.modality == Modality.ABSTRACT) Modality.ABSTRACT else Modality.OPEN
} else
Modality.FINAL
}
private fun StringBuilder.renderModalityForCallable(
callable: FirCallableMemberDeclaration<*>,
containingDeclaration: FirDeclaration?
) {
val modality = callable.modality ?: return
val isTopLevel = containingDeclaration == null
if (!isTopLevel || modality != Modality.FINAL) {
if (callable.isOverride) return
renderModality(modality, callable.implicitModalityWithoutExtensions(containingDeclaration))
}
}
private fun StringBuilder.renderOverride(callableMember: FirCallableMemberDeclaration<*>) {
if (RendererModifier.OVERRIDE !in options.modifiers) return
renderModifier(callableMember.isOverride, "override")
}
private fun StringBuilder.renderModifier(value: Boolean, modifier: String) {
if (value) {
append(modifier)
append(" ")
}
}
private fun StringBuilder.renderMemberModifiers(declaration: FirMemberDeclaration) {
renderModifier(declaration.isExternal, "external")
renderModifier(RendererModifier.EXPECT in options.modifiers && declaration.isExpect, "expect")
renderModifier(RendererModifier.ACTUAL in options.modifiers && declaration.isActual, "actual")
}
private fun StringBuilder.renderAdditionalModifiers(firMember: FirMemberDeclaration) {
val isOperator =
firMember.isOperator//TODO make similar to functionDescriptor.overriddenDescriptors.none { it.isOperator }
val isInfix =
firMember.isInfix//TODO make similar to functionDescriptor.overriddenDescriptors.none { it.isInfix }
renderModifier(firMember.isTailRec, "tailrec")
renderSuspendModifier(firMember)
renderModifier(firMember.isInline, "inline")
renderModifier(isInfix, "infix")
renderModifier(isOperator, "operator")
}
private fun StringBuilder.renderSuspendModifier(functionDescriptor: FirMemberDeclaration) {
renderModifier(functionDescriptor.isSuspend, "suspend")
}
override fun visitValueParameter(valueParameter: FirValueParameter, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
append("value-parameter").append(" ")
renderValueParameter(valueParameter)
}
}
override fun visitProperty(property: FirProperty, data: StringBuilder) = with(data) {
appendLine()
appendTabs()
renderAnnotations(property)
renderVisibility(property.visibility)
renderModifier(RendererModifier.CONST in options.modifiers && property.isConst, "const")
renderMemberModifiers(property)
renderModalityForCallable(property, containingDeclaration)
renderOverride(property)
renderModifier(RendererModifier.LATEINIT in options.modifiers && property.isLateInit, "lateinit")
renderValVarPrefix(property)
renderTypeParameters(property.typeParameters, true)
renderReceiver(property)
renderName(property)
append(": ").append(renderType(property.returnTypeRef, approximate = options.approximateTypes))
renderWhereSuffix(property.typeParameters)
fun FirPropertyAccessor?.needToRender() = this != null && (hasBody || visibility != property.visibility)
val needToRenderAccessors = options.renderContainingDeclarations &&
(property.getter.needToRender() || (property.isVar && property.setter.needToRender()))
fun FirPropertyAccessor?.render(isGetterByDefault: Boolean) {
if (this == null) {
appendLine()
appendTabs()
append(if (isGetterByDefault) "get" else "set")
} else {
visitPropertyAccessor(this, data)
}
}
if (needToRenderAccessors) {
underBlockDeclaration(property, withBrackets = false) {
property.getter.render(isGetterByDefault = true)
if (property.isVar) property.setter.render(isGetterByDefault = false)
}
}
}
override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: StringBuilder) {
require(containingDeclaration is FirProperty) { "Invalid containing declaration" }
with(data) {
appendLine()
appendTabs()
renderAnnotations(propertyAccessor)
renderVisibility(propertyAccessor.visibility)
renderModalityForCallable(propertyAccessor, containingDeclaration)
renderMemberModifiers(propertyAccessor)
renderAdditionalModifiers(propertyAccessor)
append(if (propertyAccessor.isGetter) "get" else "set")
if (propertyAccessor.isSetter) {
append("(value: ")
val renderedType = propertyAccessor.valueParameters.singleOrNull()?.returnTypeRef?.let { renderType(it) }
if (renderedType != null) append(renderedType) else append(ConeTypeIdeRenderer.ERROR_TYPE_TEXT)
append(")")
} else {
append("()")
}
}
if (options.renderContainingDeclarations) {
propertyAccessor.body?.let {
underBlockDeclaration(propertyAccessor, it, data)
}
}
}
override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
renderAnnotations(simpleFunction)
renderVisibility(simpleFunction.visibility)
renderModalityForCallable(simpleFunction, containingDeclaration)
renderMemberModifiers(simpleFunction)
renderOverride(simpleFunction)
renderAdditionalModifiers(simpleFunction)
append("fun ")
renderTypeParameters(simpleFunction.typeParameters, true)
renderReceiver(simpleFunction)
renderName(simpleFunction)
renderValueParameters(simpleFunction.valueParameters)
val returnType = simpleFunction.returnTypeRef
if (options.renderUnitReturnType || (!returnType.isUnit)) {
append(": ")
append(renderType(returnType, approximate = options.approximateTypes))
}
renderWhereSuffix(simpleFunction.typeParameters)
}
if (options.renderContainingDeclarations) {
simpleFunction.body?.let {
underBlockDeclaration(simpleFunction, it, data)
}
}
}
override fun visitAnonymousObject(anonymousObject: FirAnonymousObject, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
renderAnnotations(anonymousObject)
append(getClassifierKindPrefix(anonymousObject))
renderSuperTypes(anonymousObject)
}
if (options.renderContainingDeclarations) {
data.underBlockDeclaration(anonymousObject) {
anonymousObject.declarations.forEach {
it.accept(this, data)
}
}
}
}
override fun visitConstructor(constructor: FirConstructor, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
val containingClass = containingDeclaration
check(containingClass is FirDeclaration && (containingClass is FirClass<*> || containingClass is FirEnumEntry)) {
"Invalid renderer containing declaration for constructor"
}
renderAnnotations(constructor)
append("constructor")
renderValueParameters(constructor.valueParameters)
}
if (options.renderContainingDeclarations) {
constructor.body?.let {
underBlockDeclaration(constructor, it, data)
}
}
}
override fun visitTypeParameter(typeParameter: FirTypeParameter, data: StringBuilder) {
data.renderTypeParameter(typeParameter, true)
}
override fun visitRegularClass(regularClass: FirRegularClass, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
renderAnnotations(regularClass)
if (regularClass.classKind != ClassKind.ENUM_ENTRY) {
renderVisibility(regularClass.visibility)
}
val haveNotModality = regularClass.classKind == ClassKind.INTERFACE && regularClass.modality == Modality.ABSTRACT ||
regularClass.classKind.isSingleton && regularClass.modality == Modality.FINAL
if (!haveNotModality) {
regularClass.modality?.let {
renderModality(it, regularClass.implicitModalityWithoutExtensions(containingDeclaration))
}
}
renderMemberModifiers(regularClass)
renderModifier(RendererModifier.INNER in options.modifiers && regularClass.isInner, "inner")
renderModifier(RendererModifier.DATA in options.modifiers && regularClass.isData, "data")
renderModifier(RendererModifier.INLINE in options.modifiers && regularClass.isInline, "inline")
//TODO renderModifier(data, RendererModifier.VALUE in modifiers && regularClass.isValue, "value")
renderModifier(RendererModifier.FUN in options.modifiers && regularClass.isFun, "fun")
append(getClassifierKindPrefix(regularClass))
if (!regularClass.isCompanion) {
tabRightBySpace()
renderName(regularClass)
} else {
renderCompanionObjectName(regularClass)
}
if (regularClass.classKind == ClassKind.ENUM_ENTRY) return
val typeParameters = regularClass.typeParameters.filterIsInstance<FirTypeParameter>()
renderTypeParameterRefs(typeParameters, false)
renderSuperTypes(regularClass)
renderWhereSuffix(typeParameters)
}
fun FirDeclaration.isDefaultPrimaryConstructor() =
this is FirConstructor &&
isPrimary &&
valueParameters.isEmpty() &&
!hasBody &&
(visibility == Visibilities.DEFAULT_VISIBILITY || regularClass.classKind == ClassKind.OBJECT)
fun FirDeclaration.skipDeclarationForEnumClass(): Boolean {
if (this is FirConstructor) return isPrimary && valueParameters.isEmpty()
if (this !is FirSimpleFunction) return false
if (name.asString() == "values" && valueParameters.isEmpty()) return true
if (name.asString() == "valueOf") {
return valueParameters.count() == 1 && (valueParameters[0].returnTypeRef.coneType).classId == StandardClassIds.String
}
return false
}
fun renderDeclarationForEnumClass() {
check(regularClass.isEnumClass)
val partitioned = regularClass.declarations.partition { it is FirEnumEntry }
partitioned.first.forEach { enumEntry ->
check(enumEntry is FirEnumEntry)
visitEnumEntry(enumEntry, data)
data.append(",")
}
partitioned.second.forEach {
if (!it.skipDeclarationForEnumClass()) {
it.accept(this, data)
}
}
}
fun renderDeclarationForNotEnumClass() {
check(!regularClass.isEnumClass)
regularClass.declarations.forEach {
if (!it.isDefaultPrimaryConstructor()) {
it.accept(this, data)
}
}
}
if (options.renderContainingDeclarations) {
data.underBlockDeclaration(regularClass) {
if (regularClass.isEnumClass) {
renderDeclarationForEnumClass()
} else {
renderDeclarationForNotEnumClass()
}
}
}
}
private var tabbedString = ""
private inline fun underTabbedBlock(body: () -> Unit) {
val oldTabbedString = tabbedString
tabbedString = " ".repeat(tabbedString.length + 4)
body()
tabbedString = oldTabbedString
}
private inline fun underContainingDeclaration(firDeclaration: FirDeclaration, body: () -> Unit) {
val oldContainingDeclaration = containingDeclaration
containingDeclaration = firDeclaration
body()
containingDeclaration = oldContainingDeclaration
}
private inline fun StringBuilder.underBlockDeclaration(firDeclaration: FirDeclaration, withBrackets: Boolean = true, body: () -> Unit) {
val oldLength = length
if (withBrackets) append(" {")
val unchangedLength = length
underContainingDeclaration(firDeclaration) {
underTabbedBlock(body)
}
if (unchangedLength != length) {
if (withBrackets) {
appendLine()
appendTabs()
append("}")
}
} else {
delete(oldLength, unchangedLength)
}
}
private fun StringBuilder.appendTabs() = append(tabbedString)
private fun underBlockDeclaration(firDeclaration: FirDeclaration, firBlock: FirBlock, data: StringBuilder) {
data.underBlockDeclaration(firDeclaration) {
firBlock.accept(this, data)
}
}
override fun visitTypeAlias(typeAlias: FirTypeAlias, data: StringBuilder) = with(data) {
renderAnnotations(typeAlias)
renderVisibility(typeAlias.visibility)
renderMemberModifiers(typeAlias)
append("typealias").append(" ")
renderName(typeAlias)
renderTypeParameters(typeAlias.typeParameters, false)
append(" = ").append(renderType(typeAlias.expandedTypeRef))
Unit
}
override fun visitEnumEntry(enumEntry: FirEnumEntry, data: StringBuilder) {
with(data) {
appendLine()
appendTabs()
renderName(enumEntry)
}
}
override fun visitElement(element: FirElement, data: StringBuilder) {
element.acceptChildren(this, data)
}
companion object {
fun render(
firDeclaration: FirDeclaration,
containingDeclaration: FirDeclaration?,
options: KtDeclarationRendererOptions,
session: FirSession
): String {
val renderer = FirIdeRenderer(
containingDeclaration,
options,
session,
)
return buildString {
firDeclaration.accept(renderer, this)
}.trim(' ', '\n', '\t')
}
}
/* TYPE PARAMETERS */
private fun StringBuilder.renderTypeParameter(typeParameter: FirTypeParameter, topLevel: Boolean) {
if (topLevel) {
append("<")
}
renderModifier(typeParameter.isReified, "reified")
val variance = typeParameter.variance.label
renderModifier(variance.isNotEmpty(), variance)
renderAnnotations(typeParameter)
renderName(typeParameter)
val upperBoundsCount = typeParameter.bounds.size
if ((upperBoundsCount > 1 && !topLevel) || upperBoundsCount == 1) {
val upperBound = typeParameter.bounds.first()
if (!upperBound.isNullableAny) {
append(" : ").append(renderType(upperBound))
}
} else if (topLevel) {
typeParameter.bounds.filterNot { it.isNullableAny }.forEachIndexed { index, upperBound ->
val separator = if (index == 0) " : " else " & "
append(separator)
append(renderType(upperBound))
}
} else {
// rendered with "where"
}
if (topLevel) {
append(">")
}
}
private fun StringBuilder.renderTypeParameterRefs(typeParameters: List<FirTypeParameterRef>, withSpace: Boolean) =
renderTypeParameters(typeParameters.map { it.symbol.fir }, withSpace)
private fun StringBuilder.renderTypeParameters(typeParameters: List<FirTypeParameter>, withSpace: Boolean) {
if (typeParameters.isNotEmpty()) {
append("<")
renderTypeParameterList(typeParameters)
append(">")
if (withSpace) {
append(" ")
}
}
}
private fun StringBuilder.renderTypeParameterList(typeParameters: List<FirTypeParameter>) {
val iterator = typeParameters.iterator()
while (iterator.hasNext()) {
val firTypeParameter = iterator.next()
renderTypeParameter(firTypeParameter, false)
if (iterator.hasNext()) {
append(", ")
}
}
}
private fun StringBuilder.renderReceiver(firCallableDeclaration: FirCallableDeclaration<*>) {
val receiverType = firCallableDeclaration.receiverTypeRef
if (receiverType != null) {
renderAnnotations(firCallableDeclaration)
val needBrackets =
typeIdeRenderer.shouldRenderAsPrettyFunctionType(receiverType.coneType) && receiverType.isMarkedNullable == true
val result = renderType(receiverType).applyIf(needBrackets) { "($this)" }
append(result)
append(".")
}
}
private fun StringBuilder.renderWhereSuffix(typeParameters: List<FirTypeParameterRef>) {
val upperBoundStrings = ArrayList<String>(0)
for (typeParameter in typeParameters) {
val typeParameterFir = typeParameter.symbol.fir
typeParameterFir.bounds
.drop(1) // first parameter is rendered by renderTypeParameter
.mapTo(upperBoundStrings) { typeParameterFir.name.render() + " : " + renderType(it) }
}
if (upperBoundStrings.isNotEmpty()) {
append(" where ")
upperBoundStrings.joinTo(this, ", ")
}
}
/* VARIABLES */
private fun StringBuilder.renderValueParameters(valueParameters: List<FirValueParameter>) {
append("(")
valueParameters.forEachIndexed { index, valueParameter ->
if (index != 0) append(", ")
renderValueParameter(valueParameter)
}
append(")")
}
private fun StringBuilder.renderValueParameter(valueParameter: FirValueParameter) {
renderAnnotations(valueParameter)
renderModifier(valueParameter.isCrossinline, "crossinline")
renderModifier(valueParameter.isNoinline, "noinline")
renderVariable(valueParameter)
val withDefaultValue = valueParameter.defaultValue != null //TODO check if default value is inherited
if (withDefaultValue) {
append(" = ...")
}
}
private fun StringBuilder.renderValVarPrefix(variable: FirVariable<*>, isInPrimaryConstructor: Boolean = false) {
if (!isInPrimaryConstructor || variable !is FirValueParameter) {
append(if (variable.isVar) "var" else "val").append(" ")
}
}
private fun StringBuilder.renderVariable(variable: FirVariable<*>) {
val typeToRender = variable.returnTypeRef
val isVarArg = (variable as? FirValueParameter)?.isVararg ?: false
renderModifier(isVarArg, "vararg")
renderName(variable)
append(": ")
append(renderType(typeToRender))
}
private fun StringBuilder.renderSuperTypes(klass: FirClass<*>) {
if (klass.defaultType().isNothing) return
val supertypes = klass.superTypeRefs.applyIf(klass.classKind == ClassKind.ENUM_CLASS) {
filterNot {
(it as? FirResolvedTypeRef)?.coneType?.classId == StandardClassIds.Enum
}
}.applyIf(klass.classKind == ClassKind.ANNOTATION_CLASS) {
filterNot {
(it as? FirResolvedTypeRef)?.coneType?.classId == StandardClassIds.Annotation
}
}
if (supertypes.isEmpty() || klass.superTypeRefs.singleOrNull()?.let { it.isAny || it.isNullableAny } == true) return
tabRightBySpace()
append(": ")
supertypes.joinTo(this, ", ") { renderType(it) }
}
private fun getClassifierKindPrefix(classifier: FirDeclaration): String = when (classifier) {
is FirTypeAlias -> "typealias"
is FirRegularClass ->
if (classifier.isCompanion) {
"companion object"
} else {
when (classifier.classKind) {
ClassKind.CLASS -> "class"
ClassKind.INTERFACE -> "interface"
ClassKind.ENUM_CLASS -> "enum class"
ClassKind.OBJECT -> "object"
ClassKind.ANNOTATION_CLASS -> "annotation class"
ClassKind.ENUM_ENTRY -> "enum entry"
}
}
is FirAnonymousObject -> "object"
is FirEnumEntry -> "enum entry"
else ->
throw AssertionError("Unexpected classifier: $classifier")
}
private fun StringBuilder.tabRightBySpace() {
if (length == 0 || last() != ' ') append(' ')
}
}
@@ -0,0 +1,4 @@
open class A
class B : A
@@ -0,0 +1,2 @@
open class A
class B : A
@@ -0,0 +1,9 @@
abstract class A {
abstract class Nested
}
typealias TA = A
class B : TA() {
class NestedInB : Nested()
}
@@ -0,0 +1,7 @@
abstract class A {
abstract class Nested
}
typealias TA = A
class B : TA {
class NestedInB : A.Nested
}
@@ -0,0 +1,13 @@
package p
abstract class My {
abstract class NestedOne : My() {
abstract class NestedTwo : NestedOne() {
}
}
}
class Your : My() {
class NestedThree : NestedOne()
}
@@ -0,0 +1,8 @@
abstract class My {
abstract class NestedOne : My {
abstract class NestedTwo : My.NestedOne
}
}
class Your : My {
class NestedThree : My.NestedOne
}
@@ -0,0 +1,20 @@
@Target(AnnotationTarget.ANNOTATION_CLASS) annotation class base
@base annotation class derived
@base class correct(@base val x: Int) {
@base constructor(): this(0)
}
@base enum class My {
@base FIRST,
@base SECOND
}
@base fun foo(@base y: @base Int): Int {
@base fun bar(@base z: @base Int) = z + 1
@base val local = bar(y)
return local
}
@base val z = 0
@@ -0,0 +1,16 @@
@Target(allowedTargets = NOT_CONST_EXPRESSION) annotation class base
@base annotation class derived
@base class correct {
constructor(@base x: Int)
@base val x: Int
@base constructor()
}
@base enum class My {
FIRST,
SECOND,
}
@base fun foo(@base y: @base Int): Int {
@base fun bar(@base z: @base Int): Int
@base val local: Int
}
@base val z: Int
@@ -0,0 +1,11 @@
package a.b
class C<T, out S> {
inner class D<R, in P> {
}
}
interface Test {
val x: a.b.C<out CharSequence, *>.D<in List<*>, *>
}
@@ -0,0 +1,6 @@
class C<T, out S> {
inner class D<R, in P>
}
interface Test {
val x: C<out CharSequence, *>.D<in List<*>, *>
}
@@ -0,0 +1,20 @@
object A {
constructor()
init {}
}
enum class B {
X() {
constructor()
}
}
class C {
companion object {
constructor()
}
}
val anonObject = object {
constructor()
}
@@ -0,0 +1,12 @@
object A {
constructor()
}
enum class B {
X,
}
class C {
companion object {
constructor()
}
}
val anonObject: Any
@@ -0,0 +1,10 @@
interface IFace<T> {
fun getStatus(arg: T): Boolean
}
class Some
private fun resolve(): IFace<Some> {
return object : IFace<Some> {
override fun getStatus(arg: Some) = true
}
}
@@ -0,0 +1,10 @@
interface IFace<T> {
fun getStatus(arg: T): Boolean
}
class Some
private fun resolve(): IFace<Some> {
object : IFace<Some> {
constructor()
override fun getStatus(arg: Some): Boolean
}
}
@@ -0,0 +1,19 @@
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KCallable
import kotlin.reflect.KProperty
public interface MyRwProperty<in T, V> {
public operator fun setValue(thisRef: T, property: Any, value: V)
public operator fun getValue(thisRef: T, property: Any): V
}
val x: Int by lazy { 1 + 2 }
val delegate = object: MyRwProperty<Any?, Int> {
override fun getValue(thisRef: Any?, property: Any): Int = 1
override fun setValue(thisRef: Any?, property: Any, value: Int) {}
}
val value by delegate
var variable by delegate
@@ -0,0 +1,12 @@
interface MyRwProperty<in T, V> {
operator fun setValue(thisRef: T, property: Any, value: V)
operator fun getValue(thisRef: T, property: Any): V
}
val x: Int
get()
val delegate: MyRwProperty<Any?, Int>
val value: Int
get()
var variable: Int
get()
set(value: Int)
@@ -0,0 +1,5 @@
open class Base<T>(val x: T)
class Derived<T : Any>(x: T) : Base<T>(x)
fun <T : Any> create(x: T): Derived<T> = Derived(x)
@@ -0,0 +1,8 @@
open class Base<T> {
constructor(x: T)
val x: T
}
class Derived<T : Any> : Base<T> {
constructor(x: T)
}
fun <T : Any> create(x: T): Derived<T>
@@ -0,0 +1,4 @@
fun test() {
val x = object {}
}
@@ -0,0 +1,3 @@
fun test() {
val x: Any
}
@@ -0,0 +1,46 @@
import my.println
enum class Order {
FIRST,
SECOND,
THIRD
}
enum class Planet(val m: Double, internal val r: Double) {
MERCURY(1.0, 2.0) {
override fun sayHello() {
println("Hello!!!")
}
},
VENERA(3.0, 4.0) {
override fun sayHello() {
println("Ola!!!")
}
},
EARTH(5.0, 6.0) {
override fun sayHello() {
println("Privet!!!")
}
};
val g: Double = G * m / (r * r)
abstract fun sayHello()
companion object {
const val G = 6.67e-11
}
}
enum class PseudoInsn(val signature: String = "()V") {
FIX_STACK_BEFORE_JUMP,
FAKE_ALWAYS_TRUE_IFEQ("()I"),
FAKE_ALWAYS_FALSE_IFEQ("()I"),
SAVE_STACK_BEFORE_TRY,
RESTORE_STACK_IN_TRY_CATCH,
STORE_NOT_NULL,
AS_NOT_NULL("(Ljava/lang/Object;)Ljava/lang/Object;")
;
fun emit() {}
}
@@ -0,0 +1,30 @@
enum class Order {
FIRST,
SECOND,
THIRD,
}
enum class Planet {
MERCURY,
VENERA,
EARTH,
constructor(m: Double, r: Double)
val m: Double
internal val r: Double
val g: Double
abstract fun sayHello()
companion object {
const val G: Double
}
}
enum class PseudoInsn {
FIX_STACK_BEFORE_JUMP,
FAKE_ALWAYS_TRUE_IFEQ,
FAKE_ALWAYS_FALSE_IFEQ,
SAVE_STACK_BEFORE_TRY,
RESTORE_STACK_IN_TRY_CATCH,
STORE_NOT_NULL,
AS_NOT_NULL,
constructor(signature: String = ...)
val signature: String
fun emit()
}
@@ -0,0 +1,16 @@
interface Some
object O1 : Some
object O2 : Some
enum class SomeEnum(val x: Some) {
FIRST(O1) {
override fun check(y: Some): Boolean = true
},
SECOND(O2) {
override fun check(y: Some): Boolean = y == O2
};
abstract fun check(y: Some): Boolean
}
@@ -0,0 +1,10 @@
interface Some
object O1 : Some
object O2 : Some
enum class SomeEnum {
FIRST,
SECOND,
constructor(x: Some)
val x: Some
abstract fun check(y: Some): Boolean
}
@@ -0,0 +1,11 @@
expect class MyClass
expect fun foo(): String
expect val x: Int
actual class MyClass
actual fun foo() = "Hello"
actual val x = 42
@@ -0,0 +1,6 @@
expect class MyClass
expect fun foo(): String
expect val x: Int
actual class MyClass
actual fun foo(): String
actual val x: Int
@@ -0,0 +1,8 @@
fun <T> simpleRun(f: (T) -> Unit): Unit = f()
fun <T, R> List<T>.simpleMap(f: (T) -> R): R {
}
fun <T> simpleWith(t: T, f: T.() -> Unit): Unit = t.f()
@@ -0,0 +1,3 @@
fun <T> simpleRun(f: (T) -> Unit)
fun <T, R> List<T>.simpleMap(f: (T) -> R): R
fun <T> simpleWith(t: T, f: T.() -> Unit)
@@ -0,0 +1,7 @@
interface Any
inline fun <reified T : Any> Any.safeAs(): T? = this as? T
abstract class Summator {
abstract fun <T> plus(first: T, second: T): T
}
@@ -0,0 +1,5 @@
interface Any
inline fun <reified T : Any> Any.safeAs(): T?
abstract class Summator {
abstract fun <T> plus(first: T, second: T): T
}
@@ -0,0 +1,3 @@
fun <T> genericFoo(): T = TODO()
val <T> T.generic: T get() = genericFoo()
@@ -0,0 +1,3 @@
fun <T> genericFoo(): T
val <T> T.generic: T
get()
@@ -0,0 +1,2 @@
private fun foo(x: Any) = if (x is String && x is Int) x else null
@@ -0,0 +1 @@
private fun foo(x: Any): Any?
@@ -0,0 +1,7 @@
abstract class Base(val s: String)
class Outer {
class Derived(s: String) : Base(s)
object Obj : Base("")
}
@@ -0,0 +1,10 @@
abstract class Base {
constructor(s: String)
val s: String
}
class Outer {
class Derived : Base {
constructor(s: String)
}
object Obj : Base
}
@@ -0,0 +1,9 @@
class NoPrimary {
val x: String
constructor(x: String) {
this.x = x
}
constructor(): this("")
}
@@ -0,0 +1,5 @@
class NoPrimary {
val x: String
constructor(x: String)
constructor()
}
@@ -0,0 +1,15 @@
interface SomeInterface {
fun foo(x: Int, y: String): String
val bar: Boolean
}
class SomeClass : SomeInterface {
private val baz = 42
override fun foo(x: Int, y: String): String {
return y + x + baz
}
override var bar: Boolean
get() = true
set(value) {}
lateinit var fau: Double
}
inline class InlineClass
@@ -0,0 +1,13 @@
interface SomeInterface {
fun foo(x: Int, y: String): String
val bar: Boolean
}
class SomeClass : SomeInterface {
private val baz: Int
override fun foo(x: Int, y: String): String
override var bar: Boolean
get()
set(value: Boolean)
lateinit var fau: Double
}
inline class InlineClass
@@ -0,0 +1,3 @@
fun foo() {}
suspend fun bar() {}
@@ -0,0 +1,2 @@
fun foo()
suspend fun bar()
@@ -0,0 +1,5 @@
interface B
typealias C = B
class D : C
@@ -0,0 +1,3 @@
interface B
typealias C = B
class D : C
@@ -0,0 +1,7 @@
open class A
interface B<S, T : A>
typealias C<T> = B<T, A>
class D : C<A>
@@ -0,0 +1,4 @@
open class A
interface B<S, T : A>
typealias C<T> = B<T, A>
class D : C<A>
@@ -0,0 +1,17 @@
package test
interface Some
abstract class My<T : Some> {
open inner class T
abstract val x: T
abstract fun foo(arg: T)
abstract val y: My<test.Some>.T
abstract val z: test.My<test.Some>.T
abstract class Some : My<test.Some>.T()
}
@@ -0,0 +1,9 @@
interface Some
abstract class My<T : Some> {
open inner class T
abstract val x: My<T>.T
abstract fun foo(arg: My<T>.T)
abstract val y: My<Some>.T
abstract val z: My<Some>.T
abstract class Some : My<Some>.T
}
@@ -0,0 +1,16 @@
interface List<out T : Any> {
operator fun get(index: Int): T
infix fun concat(other: List<T>): List<T>
}
typealias StringList = List<out String>
typealias AnyList = List<*>
abstract class AbstractList<out T : Any> : List<T>
class SomeList : AbstractList<Int>() {
override fun get(index: Int): Int = 42
override fun concat(other: List<Int>): List<Int> = this
}
@@ -0,0 +1,11 @@
interface List<out T : Any> {
operator fun get(index: Int): T
infix fun concat(other: List<T>): List<T>
}
typealias StringList = List<out String>
typealias AnyList = List<*>
abstract class AbstractList<out T : Any> : List<T>
class SomeList : AbstractList<Int> {
override operator fun get(index: Int): Int
override infix fun concat(other: List<Int>): List<Int>
}
@@ -0,0 +1,6 @@
interface A
interface B
class C<T> where T : A, T : B {
}
@@ -0,0 +1,3 @@
interface A
interface B
class C<T : A> where T : B
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api.components
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.idea.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.idea.frontend.api.analyse
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractRendererTest : KotlinLightCodeInsightFixtureTestCase() {
override fun isFirPlugin() = true
protected fun doTest(path: String) {
val testDataFile = File(path)
val ktFile = myFixture.configureByText(testDataFile.name, FileUtil.loadFile(testDataFile)) as KtFile
val options = KtDeclarationRendererOptions.DEFAULT.copy(
approximateTypes = true,
renderContainingDeclarations = true,
typeRendererOptions = KtTypeRendererOptions.SHORT_NAMES
)
val actual = executeOnPooledThreadInReadAction {
buildString {
ktFile.declarations.forEach {
analyse(it) {
append(it.getSymbol().render(options))
appendLine()
}
}
}
}
KotlinTestUtils.assertEqualsToFile(File(path + ".rendered"), actual)
}
}
@@ -0,0 +1,161 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api.components;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/idea-frontend-fir/testData/components/declarationRenderer")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class RendererTestGenerated extends AbstractRendererTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInDeclarationRenderer() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/components/declarationRenderer"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("annotation.kt")
public void testAnnotation() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/annotation.kt");
}
@TestMetadata("complexTypes.kt")
public void testComplexTypes() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/complexTypes.kt");
}
@TestMetadata("constructorInObject.kt")
public void testConstructorInObject() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/constructorInObject.kt");
}
@TestMetadata("constructorOfAnonymousObject.kt")
public void testConstructorOfAnonymousObject() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/constructorOfAnonymousObject.kt");
}
@TestMetadata("delegates.kt")
public void testDelegates() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/delegates.kt");
}
@TestMetadata("derivedClass.kt")
public void testDerivedClass() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/derivedClass.kt");
}
@TestMetadata("emptyAnonymousObject.kt")
public void testEmptyAnonymousObject() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/emptyAnonymousObject.kt");
}
@TestMetadata("enums.kt")
public void testEnums() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/enums.kt");
}
@TestMetadata("enums2.kt")
public void testEnums2() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/enums2.kt");
}
@TestMetadata("expectActual.kt")
public void testExpectActual() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/expectActual.kt");
}
@TestMetadata("F.kt")
public void testF() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/F.kt");
}
@TestMetadata("functionTypes.kt")
public void testFunctionTypes() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/functionTypes.kt");
}
@TestMetadata("genericFunctions.kt")
public void testGenericFunctions() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/genericFunctions.kt");
}
@TestMetadata("genericProperty.kt")
public void testGenericProperty() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/genericProperty.kt");
}
@TestMetadata("intersectionType.kt")
public void testIntersectionType() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/intersectionType.kt");
}
@TestMetadata("nestedClass.kt")
public void testNestedClass() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/nestedClass.kt");
}
@TestMetadata("NestedOfAliasedType.kt")
public void testNestedOfAliasedType() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/NestedOfAliasedType.kt");
}
@TestMetadata("NestedSuperType.kt")
public void testNestedSuperType() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/NestedSuperType.kt");
}
@TestMetadata("noPrimaryConstructor.kt")
public void testNoPrimaryConstructor() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/noPrimaryConstructor.kt");
}
@TestMetadata("simpleClass.kt")
public void testSimpleClass() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/simpleClass.kt");
}
@TestMetadata("simpleFun.kt")
public void testSimpleFun() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/simpleFun.kt");
}
@TestMetadata("simpleTypeAlias.kt")
public void testSimpleTypeAlias() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/simpleTypeAlias.kt");
}
@TestMetadata("typeAliasWithGeneric.kt")
public void testTypeAliasWithGeneric() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/typeAliasWithGeneric.kt");
}
@TestMetadata("typeParameterVsNested.kt")
public void testTypeParameterVsNested() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameterVsNested.kt");
}
@TestMetadata("typeParameters.kt")
public void testTypeParameters() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameters.kt");
}
@TestMetadata("where.kt")
public void testWhere() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/where.kt");
}
}
@@ -16,6 +16,8 @@ import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtProperty
class KtTypeRendererTest : KotlinLightCodeInsightFixtureTestCase() {
override fun isFirPlugin() = true
private fun doTestByTypeText(
type: String,
expected: String,
@@ -88,7 +90,7 @@ class KtTypeRendererTest : KotlinLightCodeInsightFixtureTestCase() {
fun testFlexibleType() {
doTestByExpression(
expression = "java.lang.String.CASE_INSENSITIVE_ORDER",
expected = "(Comparator<(String..String?)>..Comparator<(String..String?)>?)",
expected = "Comparator<String!>!",
rendererOptions = KtTypeRendererOptions.SHORT_NAMES
)
}
@@ -137,7 +139,7 @@ class KtTypeRendererTest : KotlinLightCodeInsightFixtureTestCase() {
doTestByTypeText(
type = "Int.(String, Long) -> Char",
expected = "Function3<Int, String, Long, Char>",
rendererOptions = KtTypeRendererOptions.SHORT_NAMES.copy(renderFunctionTypes = false)
rendererOptions = KtTypeRendererOptions.SHORT_NAMES.copy(renderFunctionType = false)
)
}