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