diff --git a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt index 8a256313e2d..971dee70f61 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt @@ -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")) diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 95f59a672c3..df9706e2fb7 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -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) { testClass { model("components/expressionType") } + + testClass { + model("components/declarationRenderer") + } } testGroup("idea/idea-frontend-fir/idea-fir-low-level-api/tests", "idea/testData") { diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt index c3251bd500b..e89d27ebe0a 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt @@ -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 diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtSymbolDeclarationOverridesProvider.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtSymbolDeclarationOverridesProvider.kt index f619768a90d..53b5e0e6e21 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtSymbolDeclarationOverridesProvider.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtSymbolDeclarationOverridesProvider.kt @@ -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]) * diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtSymbolDeclarationRendererProvider.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtSymbolDeclarationRendererProvider.kt new file mode 100644 index 00000000000..e1934debb6d --- /dev/null +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtSymbolDeclarationRendererProvider.kt @@ -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 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.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) +} \ No newline at end of file diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtTypeRenderer.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtTypeRenderer.kt deleted file mode 100644 index d971779474b..00000000000 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtTypeRenderer.kt +++ /dev/null @@ -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, 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) - } -} \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/designationCollector.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/designationCollector.kt new file mode 100644 index 00000000000..9d43da34278 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/designationCollector.kt @@ -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> { + val designation = mutableListOf>() + 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 +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/util/declarationUtils.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/util/declarationUtils.kt index 8be63c33373..cb507b3dddf 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/util/declarationUtils.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/util/declarationUtils.kt @@ -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 { - require(this is FirCallableDeclaration<*>) - val designation = mutableListOf() - 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> { + return containingClass() + ?.toFirRegularClass(session) + ?.collectDesignation() + ?: return emptyList() } + diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt index 47cd4ee4ad7..af3724fdfea 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt @@ -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 } } - diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirSymbolDeclarationRendererProvider.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirSymbolDeclarationRendererProvider.kt new file mode 100644 index 00000000000..e7782d8489f --- /dev/null +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirSymbolDeclarationRendererProvider.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/renderer/ConeTypeIdeRenderer.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/renderer/ConeTypeIdeRenderer.kt new file mode 100644 index 00000000000..9ce3dead008 --- /dev/null +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/renderer/ConeTypeIdeRenderer.kt @@ -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?) { + 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? = 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 -> (Mutable)List! + val simpleCollection = replacePrefixes( + lowerRendered, + kotlinCollectionsPrefix + mutablePrefix, + upperRendered, + kotlinCollectionsPrefix, + "$kotlinCollectionsPrefix($mutablePrefix)" + ) + if (simpleCollection != null) return simpleCollection + // java.util.Map.Entry -> (Mutable)Map.(Mutable)Entry! + 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, 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().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) + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/renderer/FirAnnotationsIdeRenderer.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/renderer/FirAnnotationsIdeRenderer.kt new file mode 100644 index 00000000000..c8142d3a64a --- /dev/null +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/renderer/FirAnnotationsIdeRenderer.kt @@ -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, + 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 { + 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" + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/renderer/FirIdeRenderer.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/renderer/FirIdeRenderer.kt new file mode 100644 index 00000000000..1c3302de947 --- /dev/null +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/renderer/FirIdeRenderer.kt @@ -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() { + + 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? = 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("") + 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() + 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, withSpace: Boolean) = + renderTypeParameters(typeParameters.map { it.symbol.fir }, withSpace) + + private fun StringBuilder.renderTypeParameters(typeParameters: List, withSpace: Boolean) { + if (typeParameters.isNotEmpty()) { + append("<") + renderTypeParameterList(typeParameters) + append(">") + if (withSpace) { + append(" ") + } + } + } + + private fun StringBuilder.renderTypeParameterList(typeParameters: List) { + val iterator = typeParameters.iterator() + while (iterator.hasNext()) { + val firTypeParameter = iterator.next() + renderTypeParameter(firTypeParameter, false) + if (iterator.hasNext()) { + append(", ") + } + } + } + + private fun StringBuilder.renderReceiver(firCallableDeclaration: FirCallableDeclaration<*>) { + 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) { + + val upperBoundStrings = ArrayList(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) { + 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(' ') + } +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/F.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/F.kt new file mode 100644 index 00000000000..3b2748ad007 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/F.kt @@ -0,0 +1,4 @@ +open class A + + +class B : A \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/F.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/F.kt.rendered new file mode 100644 index 00000000000..ca3b9842885 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/F.kt.rendered @@ -0,0 +1,2 @@ +open class A +class B : A \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedOfAliasedType.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedOfAliasedType.kt new file mode 100644 index 00000000000..2af76b328d1 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedOfAliasedType.kt @@ -0,0 +1,9 @@ +abstract class A { + abstract class Nested +} + +typealias TA = A + +class B : TA() { + class NestedInB : Nested() +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedOfAliasedType.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedOfAliasedType.kt.rendered new file mode 100644 index 00000000000..9655f77c71f --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedOfAliasedType.kt.rendered @@ -0,0 +1,7 @@ +abstract class A { + abstract class Nested +} +typealias TA = A +class B : TA { + class NestedInB : A.Nested +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedSuperType.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedSuperType.kt new file mode 100644 index 00000000000..ca28a0dc3dd --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedSuperType.kt @@ -0,0 +1,13 @@ +package p + +abstract class My { + abstract class NestedOne : My() { + abstract class NestedTwo : NestedOne() { + + } + } +} + +class Your : My() { + class NestedThree : NestedOne() +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedSuperType.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedSuperType.kt.rendered new file mode 100644 index 00000000000..ece7a0815a7 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/NestedSuperType.kt.rendered @@ -0,0 +1,8 @@ +abstract class My { + abstract class NestedOne : My { + abstract class NestedTwo : My.NestedOne + } +} +class Your : My { + class NestedThree : My.NestedOne +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/annotation.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/annotation.kt new file mode 100644 index 00000000000..31b8e95d177 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/annotation.kt @@ -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 diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/annotation.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/annotation.kt.rendered new file mode 100644 index 00000000000..e8c7bc76a38 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/annotation.kt.rendered @@ -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 diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/complexTypes.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/complexTypes.kt new file mode 100644 index 00000000000..4ff9238e2a4 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/complexTypes.kt @@ -0,0 +1,11 @@ +package a.b + +class C { + inner class D { + + } +} + +interface Test { + val x: a.b.C.D, *> +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/complexTypes.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/complexTypes.kt.rendered new file mode 100644 index 00000000000..48dcf68a7ed --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/complexTypes.kt.rendered @@ -0,0 +1,6 @@ +class C { + inner class D +} +interface Test { + val x: C.D, *> +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorInObject.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorInObject.kt new file mode 100644 index 00000000000..98fcf0c9e1f --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorInObject.kt @@ -0,0 +1,20 @@ +object A { + constructor() + init {} +} + +enum class B { + X() { + constructor() + } +} + +class C { + companion object { + constructor() + } +} + +val anonObject = object { + constructor() +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorInObject.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorInObject.kt.rendered new file mode 100644 index 00000000000..e352dc4b389 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorInObject.kt.rendered @@ -0,0 +1,12 @@ +object A { + constructor() +} +enum class B { + X, +} +class C { + companion object { + constructor() + } +} +val anonObject: Any diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorOfAnonymousObject.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorOfAnonymousObject.kt new file mode 100644 index 00000000000..1f41062b2b0 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorOfAnonymousObject.kt @@ -0,0 +1,10 @@ +interface IFace { + fun getStatus(arg: T): Boolean +} +class Some + +private fun resolve(): IFace { + return object : IFace { + override fun getStatus(arg: Some) = true + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorOfAnonymousObject.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorOfAnonymousObject.kt.rendered new file mode 100644 index 00000000000..4c363521f90 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/constructorOfAnonymousObject.kt.rendered @@ -0,0 +1,10 @@ +interface IFace { + fun getStatus(arg: T): Boolean +} +class Some +private fun resolve(): IFace { + object : IFace { + constructor() + override fun getStatus(arg: Some): Boolean + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/delegates.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/delegates.kt new file mode 100644 index 00000000000..772710ce076 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/delegates.kt @@ -0,0 +1,19 @@ +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KCallable +import kotlin.reflect.KProperty + +public interface MyRwProperty { + 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 { + 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 diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/delegates.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/delegates.kt.rendered new file mode 100644 index 00000000000..5a336399b93 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/delegates.kt.rendered @@ -0,0 +1,12 @@ +interface MyRwProperty { + operator fun setValue(thisRef: T, property: Any, value: V) + operator fun getValue(thisRef: T, property: Any): V +} +val x: Int + get() +val delegate: MyRwProperty +val value: Int + get() +var variable: Int + get() + set(value: Int) diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/derivedClass.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/derivedClass.kt new file mode 100644 index 00000000000..90379bd28e7 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/derivedClass.kt @@ -0,0 +1,5 @@ +open class Base(val x: T) + +class Derived(x: T) : Base(x) + +fun create(x: T): Derived = Derived(x) \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/derivedClass.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/derivedClass.kt.rendered new file mode 100644 index 00000000000..885cec1f906 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/derivedClass.kt.rendered @@ -0,0 +1,8 @@ +open class Base { + constructor(x: T) + val x: T +} +class Derived : Base { + constructor(x: T) +} +fun create(x: T): Derived diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/emptyAnonymousObject.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/emptyAnonymousObject.kt new file mode 100644 index 00000000000..ea846bc72fa --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/emptyAnonymousObject.kt @@ -0,0 +1,4 @@ + +fun test() { + val x = object {} +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/emptyAnonymousObject.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/emptyAnonymousObject.kt.rendered new file mode 100644 index 00000000000..4666484bff2 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/emptyAnonymousObject.kt.rendered @@ -0,0 +1,3 @@ +fun test() { + val x: Any +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/enums.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/enums.kt new file mode 100644 index 00000000000..e0ab031a924 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/enums.kt @@ -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() {} +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/enums.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/enums.kt.rendered new file mode 100644 index 00000000000..65e7f5857ad --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/enums.kt.rendered @@ -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() +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/enums2.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/enums2.kt new file mode 100644 index 00000000000..8729baeccde --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/enums2.kt @@ -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 +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/enums2.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/enums2.kt.rendered new file mode 100644 index 00000000000..240a05f6b57 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/enums2.kt.rendered @@ -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 +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/expectActual.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/expectActual.kt new file mode 100644 index 00000000000..7163676cf55 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/expectActual.kt @@ -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 \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/expectActual.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/expectActual.kt.rendered new file mode 100644 index 00000000000..aba5be8f6b9 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/expectActual.kt.rendered @@ -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 diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/functionTypes.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/functionTypes.kt new file mode 100644 index 00000000000..60a3246170d --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/functionTypes.kt @@ -0,0 +1,8 @@ +fun simpleRun(f: (T) -> Unit): Unit = f() + +fun List.simpleMap(f: (T) -> R): R { + +} + +fun simpleWith(t: T, f: T.() -> Unit): Unit = t.f() + diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/functionTypes.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/functionTypes.kt.rendered new file mode 100644 index 00000000000..08eecdb5b50 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/functionTypes.kt.rendered @@ -0,0 +1,3 @@ +fun simpleRun(f: (T) -> Unit) +fun List.simpleMap(f: (T) -> R): R +fun simpleWith(t: T, f: T.() -> Unit) diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/genericFunctions.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/genericFunctions.kt new file mode 100644 index 00000000000..a88e9c41a82 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/genericFunctions.kt @@ -0,0 +1,7 @@ +interface Any + +inline fun Any.safeAs(): T? = this as? T + +abstract class Summator { + abstract fun plus(first: T, second: T): T +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/genericFunctions.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/genericFunctions.kt.rendered new file mode 100644 index 00000000000..f84326fff37 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/genericFunctions.kt.rendered @@ -0,0 +1,5 @@ +interface Any +inline fun Any.safeAs(): T? +abstract class Summator { + abstract fun plus(first: T, second: T): T +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/genericProperty.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/genericProperty.kt new file mode 100644 index 00000000000..79e406eb4d0 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/genericProperty.kt @@ -0,0 +1,3 @@ +fun genericFoo(): T = TODO() + +val T.generic: T get() = genericFoo() \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/genericProperty.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/genericProperty.kt.rendered new file mode 100644 index 00000000000..3b6ba1e40d7 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/genericProperty.kt.rendered @@ -0,0 +1,3 @@ +fun genericFoo(): T +val T.generic: T + get() diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/intersectionType.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/intersectionType.kt new file mode 100644 index 00000000000..4ef9bbea516 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/intersectionType.kt @@ -0,0 +1,2 @@ + +private fun foo(x: Any) = if (x is String && x is Int) x else null \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/intersectionType.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/intersectionType.kt.rendered new file mode 100644 index 00000000000..856e7e055f0 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/intersectionType.kt.rendered @@ -0,0 +1 @@ +private fun foo(x: Any): Any? diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/nestedClass.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/nestedClass.kt new file mode 100644 index 00000000000..a85711506cb --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/nestedClass.kt @@ -0,0 +1,7 @@ +abstract class Base(val s: String) + +class Outer { + class Derived(s: String) : Base(s) + + object Obj : Base("") +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/nestedClass.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/nestedClass.kt.rendered new file mode 100644 index 00000000000..6f553414d99 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/nestedClass.kt.rendered @@ -0,0 +1,10 @@ +abstract class Base { + constructor(s: String) + val s: String +} +class Outer { + class Derived : Base { + constructor(s: String) + } + object Obj : Base +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/noPrimaryConstructor.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/noPrimaryConstructor.kt new file mode 100644 index 00000000000..fbd6b4e236e --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/noPrimaryConstructor.kt @@ -0,0 +1,9 @@ +class NoPrimary { + val x: String + + constructor(x: String) { + this.x = x + } + + constructor(): this("") +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/noPrimaryConstructor.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/noPrimaryConstructor.kt.rendered new file mode 100644 index 00000000000..f9420c5657b --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/noPrimaryConstructor.kt.rendered @@ -0,0 +1,5 @@ +class NoPrimary { + val x: String + constructor(x: String) + constructor() +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleClass.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleClass.kt new file mode 100644 index 00000000000..d7f9078fd3b --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleClass.kt @@ -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 \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleClass.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleClass.kt.rendered new file mode 100644 index 00000000000..4a2510ca7c7 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleClass.kt.rendered @@ -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 \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleFun.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleFun.kt new file mode 100644 index 00000000000..1416a08e3a7 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleFun.kt @@ -0,0 +1,3 @@ +fun foo() {} + +suspend fun bar() {} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleFun.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleFun.kt.rendered new file mode 100644 index 00000000000..4d00c1b7dd8 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleFun.kt.rendered @@ -0,0 +1,2 @@ +fun foo() +suspend fun bar() diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleTypeAlias.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleTypeAlias.kt new file mode 100644 index 00000000000..00b88d1bd87 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleTypeAlias.kt @@ -0,0 +1,5 @@ +interface B + +typealias C = B + +class D : C \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleTypeAlias.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleTypeAlias.kt.rendered new file mode 100644 index 00000000000..c1901faf12c --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/simpleTypeAlias.kt.rendered @@ -0,0 +1,3 @@ +interface B +typealias C = B +class D : C \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/typeAliasWithGeneric.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeAliasWithGeneric.kt new file mode 100644 index 00000000000..c9c9eadadb0 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeAliasWithGeneric.kt @@ -0,0 +1,7 @@ +open class A + +interface B + +typealias C = B + +class D : C diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/typeAliasWithGeneric.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeAliasWithGeneric.kt.rendered new file mode 100644 index 00000000000..9e3abb500e7 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeAliasWithGeneric.kt.rendered @@ -0,0 +1,4 @@ +open class A +interface B +typealias C = B +class D : C diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameterVsNested.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameterVsNested.kt new file mode 100644 index 00000000000..70fbd215fd8 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameterVsNested.kt @@ -0,0 +1,17 @@ +package test + +interface Some + +abstract class My { + open inner class T + + abstract val x: T + + abstract fun foo(arg: T) + + abstract val y: My.T + + abstract val z: test.My.T + + abstract class Some : My.T() +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameterVsNested.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameterVsNested.kt.rendered new file mode 100644 index 00000000000..16eb79a5e07 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameterVsNested.kt.rendered @@ -0,0 +1,9 @@ +interface Some +abstract class My { + open inner class T + abstract val x: My.T + abstract fun foo(arg: My.T) + abstract val y: My.T + abstract val z: My.T + abstract class Some : My.T +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameters.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameters.kt new file mode 100644 index 00000000000..e267b1ada73 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameters.kt @@ -0,0 +1,16 @@ +interface List { + operator fun get(index: Int): T + + infix fun concat(other: List): List +} + +typealias StringList = List +typealias AnyList = List<*> + +abstract class AbstractList : List + +class SomeList : AbstractList() { + override fun get(index: Int): Int = 42 + + override fun concat(other: List): List = this +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameters.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameters.kt.rendered new file mode 100644 index 00000000000..41b8bc6eab3 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameters.kt.rendered @@ -0,0 +1,11 @@ +interface List { + operator fun get(index: Int): T + infix fun concat(other: List): List +} +typealias StringList = List +typealias AnyList = List<*> +abstract class AbstractList : List +class SomeList : AbstractList { + override operator fun get(index: Int): Int + override infix fun concat(other: List): List +} diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/where.kt b/idea/idea-frontend-fir/testData/components/declarationRenderer/where.kt new file mode 100644 index 00000000000..ae57050bc5d --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/where.kt @@ -0,0 +1,6 @@ +interface A +interface B + +class C where T : A, T : B { + +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/declarationRenderer/where.kt.rendered b/idea/idea-frontend-fir/testData/components/declarationRenderer/where.kt.rendered new file mode 100644 index 00000000000..f455894ee76 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/declarationRenderer/where.kt.rendered @@ -0,0 +1,3 @@ +interface A +interface B +class C where T : B diff --git a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/components/AbstractRendererTest.kt b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/components/AbstractRendererTest.kt new file mode 100644 index 00000000000..741ccec94b3 --- /dev/null +++ b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/components/AbstractRendererTest.kt @@ -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) + } +} diff --git a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/components/RendererTestGenerated.java b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/components/RendererTestGenerated.java new file mode 100644 index 00000000000..b68fbd18d5d --- /dev/null +++ b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/components/RendererTestGenerated.java @@ -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"); + } +} diff --git a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRendererTest.kt b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRendererTest.kt index d4f9891257e..6ecd59ff67b 100644 --- a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRendererTest.kt +++ b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRendererTest.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!", rendererOptions = KtTypeRendererOptions.SHORT_NAMES ) } @@ -137,7 +139,7 @@ class KtTypeRendererTest : KotlinLightCodeInsightFixtureTestCase() { doTestByTypeText( type = "Int.(String, Long) -> Char", expected = "Function3", - rendererOptions = KtTypeRendererOptions.SHORT_NAMES.copy(renderFunctionTypes = false) + rendererOptions = KtTypeRendererOptions.SHORT_NAMES.copy(renderFunctionType = false) ) }