All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.analysis.api.fir.KaSymbolByFirBuilder.kt Maven / Gradle / Ivy

/*
 * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.analysis.api.fir

import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.api.fir.signatures.KaFirFunctionSubstitutorBasedSignature
import org.jetbrains.kotlin.analysis.api.fir.signatures.KaFirVariableSubstitutorBasedSignature
import org.jetbrains.kotlin.analysis.api.fir.symbols.*
import org.jetbrains.kotlin.analysis.api.fir.types.*
import org.jetbrains.kotlin.analysis.api.fir.utils.firSymbol
import org.jetbrains.kotlin.analysis.api.fir.utils.toVariance
import org.jetbrains.kotlin.analysis.api.impl.base.types.KaBaseStarTypeProjection
import org.jetbrains.kotlin.analysis.api.impl.base.types.KaBaseTypeArgumentWithVariance
import org.jetbrains.kotlin.analysis.api.lifetime.KaLifetimeToken
import org.jetbrains.kotlin.analysis.api.platform.packages.KotlinPackageProvider
import org.jetbrains.kotlin.analysis.api.signatures.KaCallableSignature
import org.jetbrains.kotlin.analysis.api.signatures.KaFunctionSignature
import org.jetbrains.kotlin.analysis.api.signatures.KaVariableSignature
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.types.KaSubstitutor
import org.jetbrains.kotlin.analysis.api.types.KaType
import org.jetbrains.kotlin.analysis.api.types.KaTypeProjection
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.errorWithFirSpecificEntries
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.impl.FirFieldImpl
import org.jetbrains.kotlin.fir.diagnostics.ConeCannotInferTypeParameterType
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.originalIfFakeOverride
import org.jetbrains.kotlin.fir.renderer.FirRenderer
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnmatchedTypeArgumentsError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedSymbolError
import org.jetbrains.kotlin.fir.resolve.getContainingClass
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectOrStaticData
import org.jetbrains.kotlin.fir.scopes.impl.originalConstructorIfTypeAlias
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.utils.exceptions.withConeTypeEntry
import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry
import org.jetbrains.kotlin.fir.utils.exceptions.withFirSymbolEntry
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
import org.jetbrains.kotlin.utils.exceptions.requireWithAttachment
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract

/**
 * Maps FirElement to KaSymbol & ConeType to KaType, thread safe
 */
internal class KaSymbolByFirBuilder(
    private val project: Project,
    val analysisSession: KaFirSession,
    val token: KaLifetimeToken,
) {
    private val firResolveSession: LLFirResolveSession get() = analysisSession.firResolveSession
    private val firProvider: FirSymbolProvider get() = rootSession.symbolProvider
    val rootSession: LLFirSession get() = firResolveSession.useSiteFirSession

    val classifierBuilder = ClassifierSymbolBuilder()
    val functionBuilder = FunctionSymbolBuilder()
    val variableBuilder = VariableSymbolBuilder()
    val callableBuilder = CallableSymbolBuilder()
    val typeBuilder = TypeBuilder()

    fun buildSymbol(fir: FirDeclaration): KaSymbol = buildSymbol(fir.symbol)
    fun buildSymbol(firSymbol: FirBasedSymbol<*>): KaSymbol = when (firSymbol) {
        is FirClassLikeSymbol<*> -> classifierBuilder.buildClassLikeSymbol(firSymbol)
        is FirTypeParameterSymbol -> classifierBuilder.buildTypeParameterSymbol(firSymbol)
        is FirCallableSymbol<*> -> callableBuilder.buildCallableSymbol(firSymbol)
        is FirFileSymbol -> buildFileSymbol(firSymbol)
        is FirScriptSymbol -> buildScriptSymbol(firSymbol)
        else -> throwUnexpectedElementError(firSymbol)
    }

    fun buildEnumEntrySymbol(firSymbol: FirEnumEntrySymbol): KaEnumEntrySymbol = KaFirEnumEntrySymbol(firSymbol, analysisSession)

    fun buildFileSymbol(firSymbol: FirFileSymbol): KaFileSymbol = KaFirFileSymbol(firSymbol, analysisSession)

    fun buildScriptSymbol(firSymbol: FirScriptSymbol): KaScriptSymbol = KaFirScriptSymbol(firSymbol, analysisSession)

    private val packageProvider: KotlinPackageProvider get() = analysisSession.useSitePackageProvider

    fun createPackageSymbolIfOneExists(packageFqName: FqName): KaPackageSymbol? {
        val exists = packageProvider.doesPackageExist(packageFqName, analysisSession.targetPlatform)
        if (!exists) {
            return null
        }

        return createPackageSymbol(packageFqName)
    }

    fun createPackageSymbol(packageFqName: FqName): KaPackageSymbol {
        return KaFirPackageSymbol(packageFqName, project, token)
    }

    inner class ClassifierSymbolBuilder {
        fun buildClassifierSymbol(firSymbol: FirClassifierSymbol<*>): KaClassifierSymbol {
            return when (firSymbol) {
                is FirClassLikeSymbol<*> -> classifierBuilder.buildClassLikeSymbol(firSymbol)
                is FirTypeParameterSymbol -> buildTypeParameterSymbol(firSymbol)
            }
        }


        fun buildClassLikeSymbol(firSymbol: FirClassLikeSymbol<*>): KaClassLikeSymbol = when (firSymbol) {
            is FirAnonymousObjectSymbol -> buildAnonymousObjectSymbol(firSymbol)
            is FirRegularClassSymbol -> buildNamedClassOrObjectSymbol(firSymbol)
            is FirTypeAliasSymbol -> buildTypeAliasSymbol(firSymbol)
        }

        fun buildNamedClassOrObjectSymbol(symbol: FirRegularClassSymbol): KaNamedClassSymbol {
            return KaFirNamedClassSymbol(symbol, analysisSession)
        }

        fun buildAnonymousObjectSymbol(symbol: FirAnonymousObjectSymbol): KaAnonymousObjectSymbol = when (symbol.classKind) {
            ClassKind.ENUM_ENTRY -> KaFirEnumEntryInitializerSymbol(symbol, analysisSession)
            else -> KaFirAnonymousObjectSymbol(symbol, analysisSession)
        }

        fun buildTypeAliasSymbol(symbol: FirTypeAliasSymbol): KaTypeAliasSymbol {
            return KaFirTypeAliasSymbol(symbol, analysisSession)
        }

        fun buildTypeParameterSymbol(firSymbol: FirTypeParameterSymbol): KaTypeParameterSymbol {
            val callableSymbol = firSymbol.containingDeclarationSymbol as? FirCallableSymbol<*>
            callableSymbol?.fir?.unwrapSubstitutionOverrideIfNeeded()?.let { unwrappedCallable ->
                val originalIndex = callableSymbol.typeParameterSymbols.indexOf(firSymbol)
                if (originalIndex == -1) {
                    errorWithAttachment("Containing callable doesn't have the corresponding parameter") {
                        withFirSymbolEntry("typeParameter", firSymbol)
                        withFirSymbolEntry("containingCallable", callableSymbol)
                    }
                }

                val unwrappedParameter = unwrappedCallable.symbol.typeParameterSymbols[originalIndex]
                return buildTypeParameterSymbol(unwrappedParameter)
            }

            return KaFirTypeParameterSymbol(firSymbol, analysisSession)
        }

        fun buildClassLikeSymbolByClassId(classId: ClassId): KaClassLikeSymbol? {
            val firClassLikeSymbol = firProvider.getClassLikeSymbolByClassId(classId) ?: return null
            return buildClassLikeSymbol(firClassLikeSymbol)
        }

        fun buildClassLikeSymbolByLookupTag(lookupTag: ConeClassLikeLookupTag): KaClassLikeSymbol? {
            val firClassLikeSymbol = lookupTag.toSymbol(analysisSession.firSession) ?: return null
            return buildClassLikeSymbol(firClassLikeSymbol)
        }
    }

    inner class FunctionSymbolBuilder {
        fun buildFunctionSymbol(firSymbol: FirFunctionSymbol<*>): KaFunctionSymbol = when (firSymbol) {
            is FirNamedFunctionSymbol -> {
                if (firSymbol.origin == FirDeclarationOrigin.SamConstructor) {
                    buildSamConstructorSymbol(firSymbol)
                } else {
                    buildNamedFunctionSymbol(firSymbol)
                }
            }
            is FirConstructorSymbol -> buildConstructorSymbol(firSymbol)
            is FirAnonymousFunctionSymbol -> buildAnonymousFunctionSymbol(firSymbol)
            is FirPropertyAccessorSymbol -> buildPropertyAccessorSymbol(firSymbol)
            else -> throwUnexpectedElementError(firSymbol)
        }

        fun buildFunctionSignature(fir: FirFunctionSymbol<*>): KaFunctionSignature {
            if (fir is FirNamedFunctionSymbol && fir.origin != FirDeclarationOrigin.SamConstructor)
                return buildNamedFunctionSignature(fir)

            return with(analysisSession) { buildFunctionSymbol(fir).asSignature() }
        }

        fun buildNamedFunctionSymbol(firSymbol: FirNamedFunctionSymbol): KaNamedFunctionSymbol {
            firSymbol.fir.unwrapSubstitutionOverrideIfNeeded()?.let {
                return buildNamedFunctionSymbol(it.symbol)
            }

            if (firSymbol.dispatchReceiverType?.contains { it is ConeStubType } == true) {
                return buildNamedFunctionSymbol(
                    firSymbol.originalIfFakeOverride()
                        ?: errorWithFirSpecificEntries("Stub type in real declaration", fir = firSymbol.fir)
                )
            }

            firSymbol.unwrapImportedFromObjectOrStatic(::buildNamedFunctionSymbol)?.let { return it }

            check(firSymbol.origin != FirDeclarationOrigin.SamConstructor)
            return KaFirNamedFunctionSymbol(firSymbol, analysisSession)
        }

        fun buildNamedFunctionSignature(firSymbol: FirNamedFunctionSymbol): KaFunctionSignature {
            return KaFirFunctionSubstitutorBasedSignature(analysisSession.token, firSymbol, analysisSession.firSymbolBuilder)
        }

        fun buildAnonymousFunctionSymbol(firSymbol: FirAnonymousFunctionSymbol): KaAnonymousFunctionSymbol {
            return KaFirAnonymousFunctionSymbol(firSymbol, analysisSession)
        }

        fun buildConstructorSymbol(firSymbol: FirConstructorSymbol): KaConstructorSymbol {
            val originalFirSymbol = firSymbol.fir.originalConstructorIfTypeAlias?.symbol ?: firSymbol
            val unwrapped = originalFirSymbol.fir.unwrapSubstitutionOverrideIfNeeded()?.symbol ?: originalFirSymbol
            return KaFirConstructorSymbol(unwrapped, analysisSession)
        }

        fun buildSamConstructorSymbol(firSymbol: FirNamedFunctionSymbol): KaSamConstructorSymbol {
            check(firSymbol.origin == FirDeclarationOrigin.SamConstructor)
            return KaFirSamConstructorSymbol(firSymbol, analysisSession)
        }

        fun buildPropertyAccessorSymbol(firSymbol: FirPropertyAccessorSymbol): KaPropertyAccessorSymbol {
            val propertySymbol = variableBuilder.buildVariableSymbol(firSymbol.propertySymbol)
            requireWithAttachment(
                propertySymbol is KaPropertySymbol,
                { "Unexpected property symbol type: ${propertySymbol::class.simpleName}" },
            ) {
                withFirSymbolEntry("propertySymbol", firSymbol.propertySymbol)
            }

            val accessorSymbol = if (firSymbol.isGetter) propertySymbol.getter else propertySymbol.setter
            requireWithAttachment(
                accessorSymbol != null,
                { "Inconsistent state: property accessor is null while property symbol is not null" },
            ) {
                withFirSymbolEntry("propertySymbol", firSymbol.propertySymbol)
            }

            return accessorSymbol
        }
    }

    inner class VariableSymbolBuilder {
        fun buildVariableSymbol(firSymbol: FirVariableSymbol<*>): KaVariableSymbol = when (firSymbol) {
            is FirPropertySymbol -> when {
                firSymbol.isLocal -> buildLocalVariableSymbol(firSymbol)
                firSymbol is FirSyntheticPropertySymbol -> buildSyntheticJavaPropertySymbol(firSymbol)
                else -> buildPropertySymbol(firSymbol)
            }
            is FirValueParameterSymbol -> buildValueParameterSymbol(firSymbol)
            is FirFieldSymbol -> buildFieldSymbol(firSymbol)
            is FirEnumEntrySymbol -> buildEnumEntrySymbol(firSymbol) // TODO enum entry should not be callable
            is FirBackingFieldSymbol -> buildBackingFieldSymbol(firSymbol)
            is FirErrorPropertySymbol -> buildErrorVariableSymbol(firSymbol)

            is FirDelegateFieldSymbol -> throwUnexpectedElementError(firSymbol)
        }

        fun buildVariableLikeSignature(firSymbol: FirVariableSymbol<*>): KaVariableSignature {
            if (firSymbol is FirPropertySymbol && !firSymbol.isLocal && firSymbol !is FirSyntheticPropertySymbol) {
                return buildPropertySignature(firSymbol)
            }
            return with(analysisSession) { buildVariableSymbol(firSymbol).asSignature() }
        }

        fun buildPropertySymbol(firSymbol: FirPropertySymbol): KaVariableSymbol {
            checkRequirementForBuildingSymbol(firSymbol, !firSymbol.isLocal)
            checkRequirementForBuildingSymbol(firSymbol, firSymbol !is FirSyntheticPropertySymbol)

            firSymbol.fir.unwrapSubstitutionOverrideIfNeeded()?.let {
                return buildVariableSymbol(it.symbol)
            }

            firSymbol.unwrapImportedFromObjectOrStatic(::buildPropertySymbol)?.let { return it }

            return KaFirKotlinPropertySymbol.create(firSymbol, analysisSession)
        }

        fun buildPropertySignature(firSymbol: FirPropertySymbol): KaVariableSignature {
            firSymbol.lazyResolveToPhase(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE)
            return KaFirVariableSubstitutorBasedSignature(analysisSession.token, firSymbol, analysisSession.firSymbolBuilder)
        }

        fun buildLocalVariableSymbol(firSymbol: FirPropertySymbol): KaLocalVariableSymbol {
            return KaFirLocalVariableSymbol(firSymbol, analysisSession)
        }

        fun buildErrorVariableSymbol(firSymbol: FirErrorPropertySymbol): KaLocalVariableSymbol {
            return KaFirErrorVariableSymbol(firSymbol, analysisSession)
        }

        fun buildSyntheticJavaPropertySymbol(firSymbol: FirSyntheticPropertySymbol): KaSyntheticJavaPropertySymbol {
            return KaFirSyntheticJavaPropertySymbol(firSymbol, analysisSession)
        }

        fun buildValueParameterSymbol(firSymbol: FirValueParameterSymbol): KaValueParameterSymbol {
            val functionSymbol = firSymbol.containingFunctionSymbol
            functionSymbol.fir.unwrapSubstitutionOverrideIfNeeded()?.let { unwrappedFunction ->
                val originalIndex = functionSymbol.valueParameterSymbols.indexOf(firSymbol)
                if (originalIndex == -1) {
                    errorWithAttachment("Containing function doesn't have the corresponding parameter") {
                        withFirSymbolEntry("valueParameter", firSymbol)
                        withFirSymbolEntry("function", functionSymbol)
                    }
                }

                val unwrappedParameter = unwrappedFunction.symbol.valueParameterSymbols[originalIndex]
                return buildValueParameterSymbol(unwrappedParameter)
            }

            return if (functionSymbol is FirPropertyAccessorSymbol && functionSymbol.fir is FirDefaultPropertyAccessor) {
                val owner = functionBuilder.buildPropertyAccessorSymbol(functionSymbol)
                requireWithAttachment(
                    owner is KaFirDefaultPropertySetterSymbol,
                    { "Unexpected owner type: ${owner::class.simpleName}" }
                ) {
                    withFirSymbolEntry("function", functionSymbol)
                }

                KaFirDefaultSetterValueParameter(owner)
            } else
                KaFirValueParameterSymbol(firSymbol, analysisSession)
        }


        fun buildFieldSymbol(firSymbol: FirFieldSymbol): KaJavaFieldSymbol {
            firSymbol.unwrapImportedFromObjectOrStatic(::buildFieldSymbol)?.let { return it }
            firSymbol.fir.unwrapSubstitutionOverrideIfNeeded()?.let { return buildFieldSymbol(it.symbol) }

            checkRequirementForBuildingSymbol(firSymbol, firSymbol.fir.isJavaFieldOrSubstitutionOverrideOfJavaField())
            return KaFirJavaFieldSymbol(firSymbol, analysisSession)
        }

        fun buildBackingFieldSymbol(firSymbol: FirBackingFieldSymbol): KaBackingFieldSymbol {
            val propertySymbol = buildPropertySymbol(firSymbol.propertySymbol)
            requireWithAttachment(
                propertySymbol is KaPropertySymbol,
                { "Inconsistent state: property symbol is not a ${KaPropertySymbol::class.simpleName}" },
            ) {
                withFirSymbolEntry("property", propertySymbol.firSymbol)
                withFirSymbolEntry("backingField", firSymbol)
            }

            val backingFieldSymbol = propertySymbol.backingFieldSymbol
            requireWithAttachment(
                backingFieldSymbol != null,
                { "Inconsistent state: backing field symbol is null" },
            ) {
                withFirSymbolEntry("property", propertySymbol.firSymbol)
                withFirSymbolEntry("backingField", firSymbol)
            }

            return backingFieldSymbol
        }

        private fun FirField.isJavaFieldOrSubstitutionOverrideOfJavaField(): Boolean = when (this) {
            is FirJavaField -> true
            is FirFieldImpl -> (this as FirField).originalForSubstitutionOverride?.isJavaFieldOrSubstitutionOverrideOfJavaField() == true
            else -> throwUnexpectedElementError(this)
        }
    }

    inner class CallableSymbolBuilder {
        fun buildCallableSymbol(firSymbol: FirCallableSymbol<*>): KaCallableSymbol = when (firSymbol) {
            is FirPropertyAccessorSymbol -> functionBuilder.buildPropertyAccessorSymbol(firSymbol)
            is FirFunctionSymbol<*> -> functionBuilder.buildFunctionSymbol(firSymbol)
            is FirVariableSymbol<*> -> variableBuilder.buildVariableSymbol(firSymbol)
            else -> throwUnexpectedElementError(firSymbol)
        }

        fun buildCallableSignature(firSymbol: FirCallableSymbol<*>): KaCallableSignature = when (firSymbol) {
            is FirPropertyAccessorSymbol -> with(analysisSession) { functionBuilder.buildPropertyAccessorSymbol(firSymbol).asSignature() }
            is FirFunctionSymbol<*> -> functionBuilder.buildFunctionSignature(firSymbol)
            is FirVariableSymbol<*> -> variableBuilder.buildVariableLikeSignature(firSymbol)
            else -> throwUnexpectedElementError(firSymbol)
        }

        fun buildExtensionReceiverSymbol(firSymbol: FirCallableSymbol<*>): KaReceiverParameterSymbol? {
            if (firSymbol.fir.receiverParameter == null) return null
            return buildCallableSymbol(firSymbol).receiverParameter
        }
    }

    inner class TypeBuilder {
        fun buildKtType(coneType: ConeKotlinType): KaType = when (coneType) {
            is ConeClassLikeTypeImpl -> {
                when {
                    coneType.lookupTag.toSymbol(rootSession) == null -> {
                        KaFirClassErrorType(
                            coneType = coneType,
                            coneDiagnostic = ConeUnresolvedSymbolError(coneType.lookupTag.classId),
                            builder = this@KaSymbolByFirBuilder
                        )
                    }
                    hasFunctionalClassId(coneType) -> KaFirFunctionType(coneType, this@KaSymbolByFirBuilder)
                    else -> KaFirUsualClassType(coneType, this@KaSymbolByFirBuilder)
                }
            }
            is ConeTypeParameterType -> KaFirTypeParameterType(coneType, this@KaSymbolByFirBuilder)
            is ConeErrorType -> when (val diagnostic = coneType.diagnostic) {
                is ConeUnresolvedError, is ConeUnmatchedTypeArgumentsError -> {
                    KaFirClassErrorType(coneType, diagnostic, this@KaSymbolByFirBuilder)
                }
                else -> {
                    KaFirErrorType(coneType, this@KaSymbolByFirBuilder)
                }
            }
            is ConeDynamicType -> KaFirDynamicType(coneType, this@KaSymbolByFirBuilder)
            is ConeFlexibleType -> KaFirFlexibleType(coneType, this@KaSymbolByFirBuilder)
            is ConeIntersectionType -> KaFirIntersectionType(coneType, this@KaSymbolByFirBuilder)
            is ConeDefinitelyNotNullType -> KaFirDefinitelyNotNullType(coneType, this@KaSymbolByFirBuilder)
            is ConeCapturedType -> KaFirCapturedType(coneType, this@KaSymbolByFirBuilder)
            is ConeIntegerLiteralType -> buildKtType(coneType.getApproximatedType())

            is ConeTypeVariableType -> {
                val diagnostic = when (val typeParameter = coneType.typeConstructor.originalTypeParameter) {
                    null -> ConeSimpleDiagnostic("Cannot infer parameter type for ${coneType.typeConstructor.debugName}")
                    else -> ConeCannotInferTypeParameterType((typeParameter as ConeTypeParameterLookupTag).typeParameterSymbol)
                }
                buildKtType(ConeErrorType(diagnostic, isUninferredParameter = true, attributes = coneType.attributes))
            }
            else -> throwUnexpectedElementError(coneType)
        }

        private fun hasFunctionalClassId(coneType: ConeClassLikeTypeImpl): Boolean {
            // Avoid expansion of `coneType` when checking if it is a function type. Otherwise, a type alias pointing to a function type
            // will be treated as a function type itself. Then, `TypeBuilder` will build a `KaFirFunctionType` instead of a
            // `KaFirUsualClassType` to represent the type alias.
            //
            // If we have such a type alias pointing to a function type, it is most likely the abbreviation of an expanded function type. An
            // abbreviation shouldn't be expanded, and so there shouldn't be any implicit expansion here.
            return coneType.functionTypeKind(analysisSession.firResolveSession.useSiteFirSession, expandTypeAliases = false) != null
        }

        fun buildKtType(coneType: FirTypeRef): KaType {
            return buildKtType(coneType.coneType)
        }

        fun buildTypeProjection(coneType: ConeTypeProjection): KaTypeProjection = when (coneType) {
            is ConeStarProjection -> KaBaseStarTypeProjection(token)
            is ConeKotlinTypeProjection -> KaBaseTypeArgumentWithVariance(
                buildKtType(coneType.type),
                coneType.kind.toVariance(),
                token,
            )
        }

        fun buildSubstitutor(substitutor: ConeSubstitutor): KaSubstitutor {
            if (substitutor == ConeSubstitutor.Empty) return KaSubstitutor.Empty(token)
            return when (substitutor) {
                is ConeSubstitutorByMap -> KaFirMapBackedSubstitutor(substitutor, this@KaSymbolByFirBuilder)
                is ChainedSubstitutor -> KaFirChainedSubstitutor(substitutor, this@KaSymbolByFirBuilder)
                else -> KaFirGenericSubstitutor(substitutor, this@KaSymbolByFirBuilder)
            }
        }
    }

    /**
     * We shouldn't expose imported callables as they may have different [org.jetbrains.kotlin.name.CallableId]s
     * than the original callables.
     * Resolved FIR has explicitly declared original objects receivers instead of such synthetic callables.
     */
    private inline fun , R> T.unwrapImportedFromObjectOrStatic(builder: (T) -> R): R? {
        return if (origin == FirDeclarationOrigin.ImportedFromObjectOrStatic) {
            val originalSymbol = fir.importedFromObjectOrStaticData!!.original.symbol
            // The symbol has to be the same type as it is just a copy with possibly different `CallableId`
            builder(originalSymbol as T)
        } else {
            null
        }
    }

    /**
     * N.B. This functions lifts only a single layer of SUBSTITUTION_OVERRIDE at a time.
     */
    private inline fun  T.unwrapSubstitutionOverrideIfNeeded(): T? {
        unwrapUseSiteSubstitutionOverride()?.let { return it }

        unwrapInheritanceSubstitutionOverrideIfNeeded()?.let { return it }

        return null
    }

    /**
     * Use-site substitution override happens in situations like this:
     *
     * ```
     * interface List { fun get(i: Int): A }
     *
     * fun take(list: List) {
     *   list.get(10) // this call
     * }
     * ```
     *
     * In FIR, `List::get` symbol in the example will be a substitution override with a `String` instead of `A`.
     * We want to lift such substitution overrides.
     *
     * @receiver A declaration that needs to be unwrapped.
     * @return An unsubstituted declaration ([originalForSubstitutionOverride]]) if [this] is a use-site substitution override.
     */
    private inline fun  T.unwrapUseSiteSubstitutionOverride(): T? {
        val originalDeclaration = originalForSubstitutionOverride ?: return null
        return originalDeclaration.takeIf { this.origin is FirDeclarationOrigin.SubstitutionOverride.CallSite }
    }

    /**
     * We want to unwrap a SUBSTITUTION_OVERRIDE wrapper if it doesn't affect the declaration's signature in any way. If the signature
     * is somehow changed, then we want to keep the wrapper.
     *
     * Such substitute overrides happen because of inheritance.
     *
     * If the declaration references only its own type parameters, or parameters from the outer declarations, then
     * we consider that it's signature will not be changed by the SUBSTITUTION_OVERRIDE, so the wrapper can be unwrapped.
     *
     * This have a few caveats when it comes to the inner classes. TODO Provide a reference to some more in-detail description of that.
     *
     * @receiver A declaration that needs to be unwrapped.
     * @return An unsubstituted declaration ([originalForSubstitutionOverride]]) if it exists and if it does not have any change
     * in signature; `null` otherwise.
     */
    private inline fun  T.unwrapInheritanceSubstitutionOverrideIfNeeded(): T? {
        val containingClass = getContainingClass() ?: return null
        val originalDeclaration = originalForSubstitutionOverride ?: return null

        val allowedTypeParameters = buildSet {
            // declaration's own parameters
            originalDeclaration.typeParameters.mapTo(this) { it.symbol.toLookupTag() }

            // captured outer parameters
            containingClass.typeParameters.mapNotNullTo(this) {
                (it as? FirOuterClassTypeParameterRef)?.symbol?.toLookupTag()
            }
        }

        val usedTypeParameters = collectReferencedTypeParameters(originalDeclaration)

        return if (allowedTypeParameters.containsAll(usedTypeParameters)) {
            originalDeclaration
        } else {
            null
        }
    }

    companion object {
        private fun throwUnexpectedElementError(element: FirBasedSymbol<*>): Nothing {
            errorWithAttachment("Unexpected ${element::class.simpleName}") {
                withFirSymbolEntry("firSymbol", element)
            }
        }

        private fun throwUnexpectedElementError(element: FirElement): Nothing {
            errorWithAttachment("Unexpected ${element::class.simpleName}") {
                withFirEntry("firElement", element)
            }
        }

        private fun throwUnexpectedElementError(element: ConeKotlinType): Nothing {
            errorWithAttachment("Unexpected ${element::class.simpleName}") {
                withConeTypeEntry("coneType", element)
            }
        }

        @OptIn(ExperimentalContracts::class)
        private inline fun  checkRequirementForBuildingSymbol(
            firSymbol: FirBasedSymbol<*>,
            requirement: Boolean,
        ) {
            contract {
                returns() implies requirement
            }
            require(requirement) {
                val renderedSymbol = FirRenderer.withResolvePhase().renderElementWithTypeAsString(firSymbol.fir)
                "Cannot build ${S::class.simpleName} for $renderedSymbol}"
            }
        }
    }
}

internal fun FirElement.buildSymbol(builder: KaSymbolByFirBuilder): KaSymbol? = (this as? FirDeclaration)?.symbol?.let(builder::buildSymbol)
internal fun FirDeclaration.buildSymbol(builder: KaSymbolByFirBuilder): KaSymbol = builder.buildSymbol(symbol)
internal fun FirBasedSymbol<*>.buildSymbol(builder: KaSymbolByFirBuilder): KaSymbol = builder.buildSymbol(this)

private fun collectReferencedTypeParameters(declaration: FirCallableDeclaration): Set {
    val allUsedTypeParameters = mutableSetOf()

    declaration.accept(object : FirVisitorVoid() {
        override fun visitElement(element: FirElement) {
            element.acceptChildren(this)
        }

        override fun visitSimpleFunction(simpleFunction: FirSimpleFunction) {
            simpleFunction.typeParameters.forEach { it.accept(this) }

            simpleFunction.receiverParameter?.accept(this)
            simpleFunction.valueParameters.forEach { it.returnTypeRef.accept(this) }
            simpleFunction.returnTypeRef.accept(this)
        }

        override fun visitProperty(property: FirProperty) {
            property.typeParameters.forEach { it.accept(this) }

            property.receiverParameter?.accept(this)
            property.returnTypeRef.accept(this)
        }

        override fun visitReceiverParameter(receiverParameter: FirReceiverParameter) {
            receiverParameter.typeRef.accept(this)
        }

        override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef) {
            super.visitResolvedTypeRef(resolvedTypeRef)

            handleTypeRef(resolvedTypeRef)
        }

        private fun handleTypeRef(resolvedTypeRef: FirResolvedTypeRef) {
            val resolvedType = resolvedTypeRef.coneType

            resolvedType.forEachType {
                if (it is ConeTypeParameterType) {
                    allUsedTypeParameters.add(it.lookupTag)
                }
            }
        }
    })

    return allUsedTypeParameters
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy