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

org.jetbrains.kotlin.fir.builder.PsiRawFirBuilder.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * 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.fir.builder

import com.intellij.psi.PsiElement
import com.intellij.psi.tree.IElementType
import com.intellij.util.AstLoadingFilter
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.StandardNames.BACKING_FIELD
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.isCallTheFirstStatement
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.builder.buildRawContractDescription
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.declarations.impl.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.diagnostics.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.*
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.extensions.extensionService
import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.references.builder.*
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.FirImplicitTypeRefImplWithoutSource
import org.jetbrains.kotlin.fir.types.impl.FirQualifierPartImpl
import org.jetbrains.kotlin.fir.types.impl.FirTypeArgumentListImpl
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
import org.jetbrains.kotlin.utils.exceptions.requireWithAttachment
import org.jetbrains.kotlin.utils.exceptions.withPsiEntry

open class PsiRawFirBuilder(
    session: FirSession,
    val baseScopeProvider: FirScopeProvider,
    bodyBuildingMode: BodyBuildingMode = BodyBuildingMode.NORMAL,
) : AbstractRawFirBuilder(session) {
    protected open fun bindFunctionTarget(target: FirFunctionTarget, function: FirFunction) {
        target.bind(function)
    }

    var mode: BodyBuildingMode = bodyBuildingMode
        private set

    private inline fun  runOnStubs(crossinline body: () -> T): T {
        return when (mode) {
            BodyBuildingMode.NORMAL -> body()
            BodyBuildingMode.LAZY_BODIES -> {
                AstLoadingFilter.disallowTreeLoading { body() }
            }
        }
    }

    fun buildFirFile(file: KtFile): FirFile {
        return runOnStubs { file.accept(Visitor(), null) as FirFile }
    }

    fun buildAnnotationCall(annotation: KtAnnotationEntry, containerSymbol: FirBasedSymbol<*>): FirAnnotationCall {
        return withContainerSymbol(containerSymbol) {
            Visitor().visitAnnotationEntry(annotation, null) as FirAnnotationCall
        }
    }

    fun buildTypeReference(reference: KtTypeReference): FirTypeRef {
        return reference.accept(Visitor(), null) as FirTypeRef
    }

    override fun PsiElement.toFirSourceElement(kind: KtFakeSourceElementKind?): KtPsiSourceElement {
        val actualKind = kind ?: KtRealSourceElementKind
        return this.toKtPsiSourceElement(actualKind)
    }

    override val PsiElement.elementType: IElementType
        get() = node.elementType

    override val PsiElement.asText: String
        get() = text

    override val PsiElement.unescapedValue: String
        get() = (this as KtEscapeStringTemplateEntry).unescapedValue

    override fun PsiElement.getChildNodeByType(type: IElementType): PsiElement? {
        return children.firstOrNull { it.node.elementType == type }
    }

    override fun PsiElement.getReferencedNameAsName(): Name {
        return (this as KtSimpleNameExpression).getReferencedNameAsName()
    }

    override fun PsiElement.getLabelName(): String? {
        return when (this) {
            is KtExpressionWithLabel -> getLabelName()
            is KtNamedFunction -> parent.getLabelName()
            else -> null
        }
    }

    override fun PsiElement.getExpressionInParentheses(): PsiElement? {
        return (this as KtParenthesizedExpression).expression
    }

    override fun PsiElement.getAnnotatedExpression(): PsiElement? {
        return (this as KtAnnotatedExpression).baseExpression
    }

    override fun PsiElement.getLabeledExpression(): PsiElement? {
        return (this as KtLabeledExpression).baseExpression
    }

    override val PsiElement?.receiverExpression: PsiElement?
        get() = (this as? KtQualifiedExpression)?.receiverExpression

    override val PsiElement?.selectorExpression: PsiElement?
        get() = (this as? KtQualifiedExpression)?.selectorExpression

    override val PsiElement?.arrayExpression: PsiElement?
        get() = (this as? KtArrayAccessExpression)?.arrayExpression

    override val PsiElement?.indexExpressions: List?
        get() = (this as? KtArrayAccessExpression)?.indexExpressions

    override val PsiElement.isVararg: Boolean
        get() = (this as? KtParameter)?.isVarArg == true

    private fun KtModifierListOwner.getVisibility(publicByDefault: Boolean = false): Visibility =
        with(modifierList) {
            when {
                this == null -> null
                hasModifier(PRIVATE_KEYWORD) -> Visibilities.Private
                hasModifier(PUBLIC_KEYWORD) -> Visibilities.Public
                hasModifier(PROTECTED_KEYWORD) -> Visibilities.Protected
                hasModifier(INTERNAL_KEYWORD) -> Visibilities.Internal
                else -> null
            } ?: if (publicByDefault) Visibilities.Public else Visibilities.Unknown
        }

    private val KtConstructor<*>.constructorExplicitVisibility: Visibility?
        get() = getVisibility().takeUnless { it == Visibilities.Unknown }

    // See DescriptorUtils#getDefaultConstructorVisibility in core.descriptors
    private fun constructorDefaultVisibility(owner: KtClassOrObject): Visibility = when {
        owner is KtObjectDeclaration || owner.hasModifier(ENUM_KEYWORD) || owner is KtEnumEntry -> Visibilities.Private
        owner.hasModifier(SEALED_KEYWORD) -> Visibilities.Protected
        else -> Visibilities.Unknown
    }

    private val KtDeclaration.modality: Modality?
        get() = with(modifierList) {
            when {
                this == null -> null
                hasModifier(FINAL_KEYWORD) -> Modality.FINAL
                hasModifier(SEALED_KEYWORD) -> if (this@modality is KtClassOrObject) Modality.SEALED else null
                hasModifier(ABSTRACT_KEYWORD) -> Modality.ABSTRACT
                else -> if (hasModifier(OPEN_KEYWORD)) Modality.OPEN else null
            }
        }

    protected open inner class Visitor : KtVisitor(), DestructuringContext {

        override val KtDestructuringDeclarationEntry.returnTypeRef: FirTypeRef
            get() = typeReference.toFirOrImplicitType()

        @Suppress("ConflictingExtensionProperty")
        override val KtDestructuringDeclarationEntry.name: Name
            get() = if (nameIdentifier?.text == "_") {
                SpecialNames.UNDERSCORE_FOR_UNUSED_VAR
            } else {
                nameAsSafeName
            }

        override val KtDestructuringDeclarationEntry.source: KtSourceElement
            get() = toKtPsiSourceElement()

        override fun KtDestructuringDeclarationEntry.extractAnnotationsTo(
            target: FirAnnotationContainerBuilder,
            containerSymbol: FirBasedSymbol<*>,
        ) {
            (this as KtAnnotated).extractAnnotationsTo(target)
        }

        override fun createComponentCall(
            container: FirVariable,
            entrySource: KtSourceElement?,
            index: Int,
        ): FirExpression = buildOrLazyExpression(entrySource) {
            super.createComponentCall(container, entrySource, index)
        }

        private inline fun  KtElement?.convertSafe(): R? =
            this?.let { convertElement(it, null) } as? R

        private inline fun  KtElement.convert(): R =
            convertElement(this, null) as R

        private inline fun  buildOrLazy(build: () -> T, noinline lazy: () -> T): T {
            return when (mode) {
                BodyBuildingMode.NORMAL -> build()
                BodyBuildingMode.LAZY_BODIES -> runOnStubs(lazy)
            }
        }

        private inline fun buildOrLazyExpression(
            sourceElement: KtSourceElement?,
            buildExpression: () -> FirExpression,
        ): FirExpression {
            return buildOrLazy(buildExpression) {
                buildLazyExpression {
                    source = sourceElement
                }
            }
        }

        private inline fun buildOrLazyBlock(buildBlock: () -> FirBlock): FirBlock {
            return buildOrLazy(buildBlock, ::buildLazyBlock)
        }

        private inline fun buildOrLazyDelegatedConstructorCall(
            isThis: Boolean,
            constructedTypeRef: FirTypeRef,
            buildCall: () -> FirDelegatedConstructorCall,
        ): FirDelegatedConstructorCall {
            return buildOrLazy(buildCall) {
                buildLazyDelegatedConstructorCall {
                    this.isThis = isThis
                    this.constructedTypeRef = constructedTypeRef
                    calleeReference = if (isThis) {
                        buildExplicitThisReference {
                            source = null
                        }
                    } else {
                        buildExplicitSuperReference {
                            source = null
                            superTypeRef = constructedTypeRef
                        }
                    }
                }
            }
        }

        fun convertElement(element: KtElement, original: FirElement? = null): FirElement? =
            element.accept(this@Visitor, original)

        fun convertProperty(
            property: KtProperty,
            ownerRegularOrAnonymousObjectSymbol: FirClassSymbol<*>?,
        ): FirProperty = property.toFirProperty(
            ownerRegularOrAnonymousObjectSymbol,
            context
        )

        private fun KtTypeReference?.toFirOrImplicitType(): FirTypeRef =
            this?.toFirType() ?: FirImplicitTypeRefImplWithoutSource

        private fun KtTypeReference?.toFirOrUnitType(): FirTypeRef =
            this?.toFirType() ?: implicitUnitType

        protected fun KtTypeReference?.toFirOrErrorType(): FirTypeRef =
            this?.toFirType() ?: buildErrorTypeRef {
                source = this@toFirOrErrorType?.toFirSourceElement()
                diagnostic = ConeSyntaxDiagnostic(
                    if (this@toFirOrErrorType == null) "Incomplete code" else "Conversion failed"
                )
                this@toFirOrErrorType?.extractAnnotationsTo(this)
            }

        // Here we accept lambda as receiver to prevent expression calculation in stub mode
        private fun (() -> KtExpression?).toFirExpression(errorReason: String): FirExpression =
            this().toFirExpression(errorReason)

        private fun KtElement?.toFirExpression(
            errorReason: String,
            kind: DiagnosticKind = DiagnosticKind.ExpressionExpected,
        ): FirExpression = toFirExpression { ConeSimpleDiagnostic(errorReason, kind) }

        private inline fun KtElement?.toFirExpression(
            sourceWhenThisIsNull: KtElement? = null,
            sourceWhenInvalidExpression: KtElement? = this,
            isValidExpression: (FirExpression) -> Boolean = { !it.isStatementLikeExpression },
            diagnosticFn: () -> ConeDiagnostic,
        ): FirExpression {
            if (this == null) {
                return buildErrorExpression(source = sourceWhenThisIsNull?.toFirSourceElement(), diagnosticFn())
            }

            return when (val fir = convertElement(this, null)) {
                is FirExpression -> when {
                    isValidExpression(fir) -> checkSelectorInvariant(fir)
                    else -> buildErrorExpression {
                        nonExpressionElement = fir
                        diagnostic = diagnosticFn()
                        source = sourceWhenInvalidExpression?.toFirSourceElement()
                    }
                }
                else -> buildErrorExpression {
                    nonExpressionElement = fir
                    diagnostic = diagnosticFn()
                    source = fir?.source?.realElement() ?: toFirSourceElement()
                }
            }
        }

        private fun KtElement.checkSelectorInvariant(result: FirExpression): FirExpression {
            val callExpressionCallee = (this as? KtCallExpression)?.calleeExpression?.unwrapParenthesesLabelsAndAnnotations()

            if (this is KtNameReferenceExpression ||
                (this is KtCallExpression && callExpressionCallee !is KtLambdaExpression) ||
                getQualifiedExpressionForSelector() == null
            ) {
                return result
            }

            return buildErrorExpression {
                source = callExpressionCallee?.toFirSourceElement() ?: toFirSourceElement()
                diagnostic =
                    ConeSimpleDiagnostic(
                        "The expression cannot be a selector (occur after a dot)",
                        if (callExpressionCallee == null) DiagnosticKind.IllegalSelector else DiagnosticKind.NoReceiverAllowed
                    )
                expression = result
            }
        }

        private inline fun KtExpression.toFirStatement(errorReasonLazy: () -> String): FirStatement {
            return when (val fir = convertElement(this, null)) {
                is FirStatement -> fir
                else -> buildErrorExpression {
                    nonExpressionElement = fir
                    diagnostic = ConeSyntaxDiagnostic(errorReasonLazy())
                    source = toFirSourceElement()
                }
            }
        }

        private fun KtExpression.toFirStatement(): FirStatement =
            convert()

        private fun KtDeclaration.toFirDeclaration(
            delegatedSuperType: FirTypeRef,
            delegatedSelfType: FirResolvedTypeRef,
            owner: KtClassOrObject,
            ownerClassBuilder: FirClassBuilder,
            ownerTypeParameters: List,
        ): FirDeclaration {
            return when (this) {
                is KtSecondaryConstructor -> {
                    toFirConstructor(
                        if (isDelegatedCallToThis()) delegatedSelfType else delegatedSuperType,
                        delegatedSelfType,
                        owner,
                        ownerTypeParameters,
                    )
                }
                is KtEnumEntry -> {
                    val primaryConstructor = owner.primaryConstructor
                    val ownerClassHasDefaultConstructor =
                        primaryConstructor?.valueParameters?.isEmpty() ?: owner.secondaryConstructors.let { constructors ->
                            constructors.isEmpty() || constructors.any { it.valueParameters.isEmpty() }
                        }
                    toFirEnumEntry(delegatedSelfType, ownerClassHasDefaultConstructor)
                }
                is KtProperty -> {
                    convertProperty(
                        this@toFirDeclaration,
                        ownerClassBuilder.ownerRegularOrAnonymousObjectSymbol
                    )
                }
                is KtDestructuringDeclaration -> {
                    buildErrorTopLevelDestructuringDeclaration(toFirSourceElement())
                }
                is KtClassInitializer -> {
                    buildAnonymousInitializer(this, ownerClassBuilder.ownerRegularOrAnonymousObjectSymbol)
                }
                else -> convert()
            }
        }

        private fun KtExpression?.toFirBlock(): FirBlock =
            when (this) {
                is KtBlockExpression ->
                    accept(this@Visitor, null) as FirBlock
                null ->
                    buildEmptyExpressionBlock()
                else -> {
                    var firBlock: FirBlock? = null
                    if (this is KtAnnotatedExpression) {
                        val lastChild = children.lastOrNull()
                        if (lastChild is KtBlockExpression) {
                            firBlock = lastChild.toFirBlock()
                            extractAnnotationsTo(firBlock)
                        }
                    }
                    firBlock ?: FirSingleExpressionBlock(convert())
                }
            }

        private fun KtDeclarationWithBody.buildFirBody(): Pair =
            if (hasBody()) {
                buildOrLazyBlock {
                    if (hasBlockBody()) {
                        val block = bodyBlockExpression?.accept(this@Visitor, null) as? FirBlock
                        val contractDescription = when {
                            !hasContractEffectList() -> block?.let {
                                val blockSourcePsi = it.source?.psi
                                val diagnostic = when {
                                    blockSourcePsi == null || !isCallTheFirstStatement(blockSourcePsi) -> ConeContractShouldBeFirstStatement
                                    else -> null
                                }
                                processLegacyContractDescription(block, diagnostic)
                            }
                            else -> null
                        }
                        return@buildFirBody block to contractDescription
                    } else {
                        val result = { bodyExpression }.toFirExpression("Function has no body (but should)")
                        FirSingleExpressionBlock(result.toReturn(baseSource = result.source))
                    }
                } to null
            } else {
                null to null
            }

        private fun isCallTheFirstStatement(psi: PsiElement): Boolean =
            isCallTheFirstStatement(psi, { it.elementType }, { it.allChildren.toList() })

        private fun ValueArgument?.toFirExpression(): FirExpression {
            if (this == null) {
                return buildErrorExpression(
                    source = null,
                    ConeSyntaxDiagnostic("No argument given"),
                )
            }
            val name = this.getArgumentName()?.asName
            val firExpression = when (val expression = this.getArgumentExpression()) {
                is KtConstantExpression, is KtStringTemplateExpression -> {
                    expression.accept(this@Visitor, null) as FirExpression
                }

                else -> {
                    { expression }.toFirExpression("Argument is absent")
                }
            }
            val isSpread = getSpreadElement() != null
            return when {
                name != null -> buildNamedArgumentExpression {
                    source = (this@toFirExpression as? PsiElement)?.toFirSourceElement()
                    this.expression = firExpression
                    this.isSpread = isSpread
                    this.name = name
                }
                isSpread -> buildSpreadArgumentExpression {
                    source = (this@toFirExpression as? PsiElement)?.toFirSourceElement()
                    this.expression = firExpression
                }
                else -> firExpression
            }
        }

        private fun KtPropertyAccessor?.toFirPropertyAccessor(
            property: KtProperty,
            propertyTypeRef: FirTypeRef,
            propertySymbol: FirPropertySymbol,
            isGetter: Boolean,
            accessorAnnotationsFromProperty: List,
            parameterAnnotationsFromProperty: List,
        ): FirPropertyAccessor? {
            val defaultVisibility = this?.getVisibility()
            val accessorVisibility =
                if (defaultVisibility != null && defaultVisibility != Visibilities.Unknown) defaultVisibility else property.getVisibility()
            // Downward propagation of `inline`, `external` and `expect` modifiers (from property to its accessors)
            val status =
                FirDeclarationStatusImpl(accessorVisibility, this?.modality).apply {
                    isInline = property.hasModifier(INLINE_KEYWORD) ||
                            this@toFirPropertyAccessor?.hasModifier(INLINE_KEYWORD) == true
                    isExternal = property.hasModifier(EXTERNAL_KEYWORD) ||
                            this@toFirPropertyAccessor?.hasModifier(EXTERNAL_KEYWORD) == true
                    isExpect = property.hasModifier(EXPECT_KEYWORD) ||
                            this@toFirPropertyAccessor?.hasModifier(EXPECT_KEYWORD) == true
                }
            val propertyTypeRefToUse = propertyTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef)
            return when {
                this != null && hasBody() -> {
                    // Property has a non-default getter or setter.
                    // NOTE: We still need the setter even for a val property so we can report errors (e.g., VAL_WITH_SETTER).
                    val source = this.toFirSourceElement()
                    val accessorTarget = FirFunctionTarget(labelName = null, isLambda = false)
                    buildPropertyAccessor {
                        this.source = source
                        moduleData = baseModuleData
                        origin = FirDeclarationOrigin.Source
                        returnTypeRef = if (isGetter) {
                            returnTypeReference?.toFirType() ?: propertyTypeRefToUse
                        } else {
                            returnTypeReference.toFirOrUnitType()
                        }
                        this.isGetter = isGetter
                        this.status = status
                        annotations += accessorAnnotationsFromProperty
                        extractAnnotationsTo(this)
                        [email protected] += accessorTarget
                        symbol = FirPropertyAccessorSymbol()
                        extractValueParametersTo(
                            this, symbol, ValueParameterDeclaration.SETTER, propertyTypeRefToUse, parameterAnnotationsFromProperty
                        )
                        if (!isGetter && valueParameters.isEmpty()) {
                            valueParameters += buildDefaultSetterValueParameter {
                                this.source = source.fakeElement(KtFakeSourceElementKind.DefaultAccessor)
                                moduleData = baseModuleData
                                origin = FirDeclarationOrigin.Source
                                returnTypeRef = propertyTypeRefToUse
                                symbol = FirValueParameterSymbol(StandardNames.DEFAULT_VALUE_PARAMETER)
                                annotations += parameterAnnotationsFromProperty
                            }
                        }
                        val outerContractDescription = [email protected]()
                        val (body, innerContractDescription) = withForcedLocalContext {
                            [email protected]()
                        }
                        this.body = body
                        val contractDescription = outerContractDescription ?: innerContractDescription
                        contractDescription?.let {
                            this.contractDescription = it
                        }
                        this.propertySymbol = propertySymbol
                    }.also {
                        it.initContainingClassAttr()
                        bindFunctionTarget(accessorTarget, it)
                        [email protected]()
                    }
                }
                isGetter || property.isVar -> {
                    // Default getter for val/var properties, and default setter for var properties.
                    val propertySource =
                        this?.toFirSourceElement() ?: property.toKtPsiSourceElement(KtFakeSourceElementKind.DefaultAccessor)
                    val valueParameter = this?.valueParameters?.firstOrNull()

                    FirDefaultPropertyAccessor
                        .createGetterOrSetter(
                            propertySource,
                            baseModuleData,
                            FirDeclarationOrigin.Source,
                            propertyTypeRefToUse,
                            accessorVisibility,
                            propertySymbol,
                            isGetter,
                            parameterAnnotations = parameterAnnotationsFromProperty,
                            parameterSource = valueParameter?.toKtPsiSourceElement(),
                        )
                        .also {
                            if (this != null) {
                                it.extractAnnotationsFrom(this)
                            }
                            it.replaceAnnotations(it.annotations.smartPlus(accessorAnnotationsFromProperty))
                            it.status = status
                            it.initContainingClassAttr()

                            valueParameter?.typeReference?.toFirType()?.let { type ->
                                it.valueParameters.firstOrNull()?.replaceReturnTypeRef(type)
                            }
                        }
                }
                else -> {
                    // No default setter for val properties.
                    null
                }
            }
        }

        private fun obtainPropertyComponentStatus(
            componentVisibility: Visibility,
            declaration: KtDeclaration?,
            property: KtProperty,
        ): FirDeclarationStatus {
            return FirDeclarationStatusImpl(componentVisibility, declaration?.modality).apply {
                isInline = property.hasModifier(INLINE_KEYWORD) ||
                        declaration?.hasModifier(INLINE_KEYWORD) == true
                isExternal = property.hasModifier(EXTERNAL_KEYWORD) ||
                        declaration?.hasModifier(EXTERNAL_KEYWORD) == true
                isLateInit = declaration?.hasModifier(LATEINIT_KEYWORD) == true
            }
        }

        private fun KtBackingField?.toFirBackingField(
            property: KtProperty,
            propertySymbol: FirPropertySymbol,
            propertyReturnType: FirTypeRef,
            annotationsFromProperty: List,
        ): FirBackingField {
            val defaultVisibility = this?.getVisibility()
            val componentVisibility = if (defaultVisibility != null && defaultVisibility != Visibilities.Unknown) {
                defaultVisibility
            } else {
                Visibilities.Private
            }
            val status = obtainPropertyComponentStatus(componentVisibility, this, property)
            val backingFieldInitializer = this?.toInitializerExpression()
            val returnType = this?.returnTypeReference.toFirOrImplicitType()
            val source = this?.toFirSourceElement()
            return if (this != null) {
                buildBackingField {
                    this.source = source
                    moduleData = baseModuleData
                    origin = FirDeclarationOrigin.Source
                    returnTypeRef = returnType
                    this.status = status
                    extractAnnotationsTo(this)
                    this.annotations += annotationsFromProperty
                    name = BACKING_FIELD
                    symbol = FirBackingFieldSymbol(CallableId(name))
                    this.propertySymbol = propertySymbol
                    this.initializer = backingFieldInitializer
                    this.isVar = property.isVar
                    this.isVal = !property.isVar
                }
            } else {
                FirDefaultPropertyBackingField(
                    moduleData = baseModuleData,
                    origin = FirDeclarationOrigin.Source,
                    source = property.toFirSourceElement(KtFakeSourceElementKind.DefaultAccessor),
                    annotations = annotationsFromProperty.toMutableList(),
                    returnTypeRef = propertyReturnType.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor),
                    isVar = property.isVar,
                    propertySymbol = propertySymbol,
                    status = status,
                )
            }
        }

        private fun KtParameter.toFirValueParameter(
            defaultTypeRef: FirTypeRef?,
            functionSymbol: FirFunctionSymbol<*>,
            valueParameterDeclaration: ValueParameterDeclaration,
            additionalAnnotations: List = emptyList(),
        ): FirValueParameter {
            val name = convertValueParameterName(nameAsSafeName, valueParameterDeclaration) { nameIdentifier?.node?.text }
            return buildValueParameter {
                val parameterSource = toFirSourceElement()
                source = parameterSource
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                isVararg = isVarArg
                this.name = name
                symbol = FirValueParameterSymbol(name)
                withContainerSymbol(symbol, isLocal = valueParameterDeclaration != ValueParameterDeclaration.FUNCTION) {
                    returnTypeRef = when {
                        typeReference != null -> typeReference.toFirOrErrorType()
                        defaultTypeRef != null -> defaultTypeRef
                        valueParameterDeclaration.shouldExplicitParameterTypeBePresent -> createNoTypeForParameterTypeRef(parameterSource)
                        else -> null.toFirOrImplicitType()
                    }.let {
                        if (isVararg && it is FirErrorTypeRef) {
                            it.wrapIntoArray()
                        } else {
                            it
                        }
                    }

                    addAnnotationsFrom(
                        this@toFirValueParameter,
                        isFromPrimaryConstructor = valueParameterDeclaration == ValueParameterDeclaration.PRIMARY_CONSTRUCTOR
                    )
                }

                defaultValue = if (hasDefaultValue()) {
                    buildOrLazyExpression(null) { { [email protected] }.toFirExpression("Should have default value") }
                } else null
                isCrossinline = hasModifier(CROSSINLINE_KEYWORD)
                isNoinline = hasModifier(NOINLINE_KEYWORD)
                containingFunctionSymbol = functionSymbol
                annotations += additionalAnnotations
            }
        }

        private fun FirValueParameterBuilder.addAnnotationsFrom(ktParameter: KtParameter, isFromPrimaryConstructor: Boolean) {
            for (annotationEntry in ktParameter.annotationEntries) {
                annotationEntry.convert().takeIf {
                    !isFromPrimaryConstructor || it.useSiteTarget.appliesToPrimaryConstructorParameter()
                }?.let {
                    annotations += it
                }
            }
        }

        private fun KtParameter.toFirProperty(firParameter: FirValueParameter): FirProperty {
            require(hasValOrVar())
            val status = FirDeclarationStatusImpl(getVisibility(), modality).apply {
                isExpect = hasExpectModifier() || [email protected]
                isActual = hasActualModifier()
                isOverride = hasModifier(OVERRIDE_KEYWORD)
                isConst = hasModifier(CONST_KEYWORD)
                isLateInit = hasModifier(LATEINIT_KEYWORD)
            }

            val propertyName = nameAsSafeName
            val propertySymbol = FirPropertySymbol(callableIdForName(propertyName))
            withContainerSymbol(propertySymbol) {
                val propertySource = toFirSourceElement(KtFakeSourceElementKind.PropertyFromParameter)
                val parameterAnnotations = mutableListOf()
                for (annotationEntry in annotationEntries) {
                    parameterAnnotations += annotationEntry.convert()
                }

                return buildProperty {
                    source = propertySource
                    moduleData = baseModuleData
                    origin = FirDeclarationOrigin.Source
                    returnTypeRef = firParameter.returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.PropertyFromParameter)
                    name = propertyName
                    initializer = buildPropertyAccessExpression {
                        source = propertySource
                        calleeReference = buildPropertyFromParameterResolvedNamedReference {
                            source = propertySource
                            name = propertyName
                            resolvedSymbol = firParameter.symbol
                        }
                    }
                    isVar = isMutable
                    symbol = propertySymbol
                    isLocal = false
                    val defaultAccessorSource = propertySource.fakeElement(KtFakeSourceElementKind.DefaultAccessor)
                    backingField = FirDefaultPropertyBackingField(
                        moduleData = baseModuleData,
                        origin = FirDeclarationOrigin.Source,
                        source = defaultAccessorSource,
                        annotations = parameterAnnotations.filter {
                            it.useSiteTarget == FIELD || it.useSiteTarget == PROPERTY_DELEGATE_FIELD
                        }.toMutableList(),
                        returnTypeRef = returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor),
                        isVar = isVar,
                        propertySymbol = symbol,
                        status = status.copy(isLateInit = false),
                    )

                    this.status = status
                    getter = FirDefaultPropertyGetter(
                        defaultAccessorSource,
                        baseModuleData,
                        FirDeclarationOrigin.Source,
                        returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor),
                        getVisibility(),
                        symbol,
                        isInline = hasModifier(INLINE_KEYWORD),
                    ).also { getter ->
                        getter.initContainingClassAttr()
                        getter.replaceAnnotations(parameterAnnotations.filterUseSiteTarget(PROPERTY_GETTER))
                    }
                    setter = if (isMutable) FirDefaultPropertySetter(
                        defaultAccessorSource,
                        baseModuleData,
                        FirDeclarationOrigin.Source,
                        returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor),
                        getVisibility(),
                        symbol,
                        parameterAnnotations = parameterAnnotations.filterUseSiteTarget(SETTER_PARAMETER),
                        isInline = hasModifier(INLINE_KEYWORD),
                    ).also { setter ->
                        setter.initContainingClassAttr()
                        setter.replaceAnnotations(parameterAnnotations.filterUseSiteTarget(PROPERTY_SETTER))
                    } else null
                    annotations += parameterAnnotations.filterConstructorPropertyRelevantAnnotations(isMutable)

                    dispatchReceiverType = currentDispatchReceiverType()
                }.apply {
                    if (firParameter.isVararg) {
                        isFromVararg = true
                    }
                    firParameter.correspondingProperty = this
                    fromPrimaryConstructor = true
                }
            }
        }

        private fun FirDefaultPropertyAccessor.extractAnnotationsFrom(annotated: KtAnnotated) {
            annotated.extractAnnotationsTo(this)
        }

        private fun KtAnnotated.extractAnnotationsTo(container: FirAnnotationContainer) {
            if (annotationEntries.isEmpty()) return
            val annotations = buildList {
                addAll(container.annotations)
                for (annotationEntry in annotationEntries) {
                    add(annotationEntry.convert())
                }
            }
            container.replaceAnnotations(annotations)
        }

        private fun KtAnnotated.extractAnnotationsTo(container: FirAnnotationContainerBuilder) {
            for (annotationEntry in annotationEntries) {
                container.annotations += annotationEntry.convert()
            }
        }

        private fun KtTypeParameterListOwner.extractTypeParametersTo(
            container: FirTypeParameterRefsOwnerBuilder,
            declarationSymbol: FirBasedSymbol<*>,
        ) {
            for (typeParameter in typeParameters) {
                container.typeParameters += extractTypeParameter(typeParameter, declarationSymbol)
            }
        }

        private fun KtTypeParameterListOwner.extractTypeParametersTo(
            container: FirTypeParametersOwnerBuilder,
            declarationSymbol: FirBasedSymbol<*>,
        ) {
            for (typeParameter in typeParameters) {
                container.typeParameters += extractTypeParameter(typeParameter, declarationSymbol)
            }
        }

        private fun extractTypeParameter(parameter: KtTypeParameter, declarationSymbol: FirBasedSymbol<*>): FirTypeParameter {
            val parameterName = parameter.nameAsSafeName
            return buildTypeParameter {
                source = parameter.toFirSourceElement()
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                name = parameterName
                symbol = FirTypeParameterSymbol()
                containingDeclarationSymbol = declarationSymbol
                variance = parameter.variance
                isReified = parameter.hasModifier(REIFIED_KEYWORD)
                parameter.extractAnnotationsTo(this)
                val extendsBound = parameter.extendsBound
                if (extendsBound != null) {
                    bounds += extendsBound.convert()
                }
                val owner = parameter.getStrictParentOfType() ?: return@buildTypeParameter
                for (typeConstraint in owner.typeConstraints) {
                    val subjectName = typeConstraint.subjectTypeParameterName?.getReferencedNameAsName()

                    if (subjectName == parameterName) {
                        bounds += typeConstraint.boundTypeReference.toFirOrErrorType()
                    }

                    for (entry in typeConstraint.annotationEntries) {
                        annotations += buildErrorAnnotationCall {
                            source = entry.toFirSourceElement()
                            useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget()
                            annotationTypeRef = entry.typeReference.toFirOrErrorType()
                            diagnostic = ConeSimpleDiagnostic(
                                "Type parameter annotations are not allowed inside where clauses",
                                DiagnosticKind.AnnotationInWhereClause,
                            )
                            val name = (annotationTypeRef as? FirUserTypeRef)?.qualifier?.last()?.name
                                ?: Name.special("")
                            calleeReference = buildSimpleNamedReference {
                                source = (entry.typeReference?.typeElement as? KtUserType)?.referenceExpression?.toFirSourceElement()
                                this.name = name
                            }
                            entry.extractArgumentsTo(this)
                            typeArguments.appendTypeArguments(entry.typeArguments)
                            containingDeclarationSymbol = context.containerSymbol
                        }
                    }
                }
                addDefaultBoundIfNecessary()
            }
        }

        override fun visitTypeParameter(parameter: KtTypeParameter, data: FirElement?): FirElement {
            throw AssertionError("KtTypeParameter should be process via extractTypeParameter")
        }

        private fun  KtTypeParameterListOwner.fillDanglingConstraintsTo(to: T) where T : FirDeclaration, T : FirTypeParameterRefsOwner {
            val typeParamNames = typeParameters.mapNotNull { it.nameAsName }.toSet()
            val result = typeConstraints.mapNotNull { constraint ->
                constraint.subjectTypeParameterName?.getReferencedNameAsName()?.let { name ->
                    if (!typeParamNames.contains(name)) {
                        DanglingTypeConstraint(name, constraint.subjectTypeParameterName!!.toFirSourceElement())
                    } else {
                        null
                    }
                }

            }
            if (result.isNotEmpty()) {
                to.danglingTypeConstraints = result
            }
        }

        private fun KtDeclarationWithBody.extractValueParametersTo(
            container: FirFunctionBuilder,
            functionSymbol: FirFunctionSymbol<*>,
            valueParameterDeclaration: ValueParameterDeclaration,
            defaultTypeRef: FirTypeRef? = null,
            additionalAnnotations: List = emptyList(),
        ) {
            for (valueParameter in valueParameters) {
                container.valueParameters += valueParameter.toFirValueParameter(
                    defaultTypeRef, functionSymbol, valueParameterDeclaration, additionalAnnotations = additionalAnnotations
                )
            }
        }

        private fun KtCallElement.extractArgumentsTo(container: FirCallBuilder) {
            val argumentList = buildArgumentList {
                source = valueArgumentList?.toFirSourceElement()
                for (argument in valueArguments) {
                    val argumentExpression =
                        buildOrLazyExpression((argument as? PsiElement)?.toFirSourceElement()) { argument.toFirExpression() }
                    arguments += when (argument) {
                        is KtLambdaArgument -> argumentExpression.apply {
                            // TODO(KT-66553) remove and set in builder
                            @OptIn(RawFirApi::class)
                            (this as? FirAnonymousFunctionExpression)?.replaceIsTrailingLambda(true)
                        }
                        else -> argumentExpression
                    }
                }
            }
            container.argumentList = argumentList
        }

        /**
         * @param type the return type for new field.
         * In the case of null will be calculated inside [withContainerSymbol],
         * so it is crucial to decide to whom type annotation will be belonged
         */
        protected fun buildFieldForSupertypeDelegate(
            entry: KtDelegatedSuperTypeEntry,
            type: FirTypeRef?,
            fieldOrd: Int,
        ): FirField {
            val delegateSource = entry.toFirSourceElement(KtFakeSourceElementKind.ClassDelegationField)

            val delegateExpression = buildOrLazyExpression(delegateSource) {
                { entry.delegateExpression }
                    .toFirExpression("Should have delegate")
            }
            return buildField {
                source = delegateSource
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Synthetic.DelegateField
                name = NameUtils.delegateFieldName(fieldOrd)
                symbol = FirFieldSymbol(CallableId([email protected], name))
                returnTypeRef = type ?: withContainerSymbol(symbol) {
                    entry.typeReference.toFirOrErrorType()
                }

                isVar = false
                status = FirDeclarationStatusImpl(Visibilities.Private, Modality.FINAL)
                initializer = delegateExpression
                dispatchReceiverType = currentDispatchReceiverType()
            }
        }

        private fun KtClassOrObject.extractSuperTypeListEntriesTo(
            container: FirClassBuilder,
            delegatedSelfTypeRef: FirTypeRef?,
            delegatedEnumSuperTypeRef: FirTypeRef?,
            classKind: ClassKind,
            containerTypeParameters: List,
            containingClassIsExpectClass: Boolean,
        ): Pair?> {
            var superTypeCallEntry: KtSuperTypeCallEntry? = null
            val allSuperTypeCallEntries = mutableListOf>()
            var delegatedSuperTypeRef: FirTypeRef? = null
            val delegateFieldsMap = mutableMapOf()
            superTypeListEntries.forEachIndexed { index, superTypeListEntry ->
                when (superTypeListEntry) {
                    is KtSuperTypeEntry -> {
                        container.superTypeRefs += superTypeListEntry.typeReference.toFirOrErrorType()
                    }
                    is KtSuperTypeCallEntry -> {
                        delegatedSuperTypeRef = superTypeListEntry.calleeExpression.typeReference.toFirOrErrorType()
                        container.superTypeRefs += delegatedSuperTypeRef!!
                        superTypeCallEntry = superTypeListEntry
                        allSuperTypeCallEntries.add(superTypeListEntry to delegatedSuperTypeRef!!)
                    }
                    is KtDelegatedSuperTypeEntry -> {
                        val type = superTypeListEntry.typeReference.toFirOrErrorType()
                        container.superTypeRefs += type

                        /**
                         * [FirAnnotationCall.containingDeclarationSymbol] have to be from [container], so we should calculate it here
                         */
                        val delegateField = buildFieldForSupertypeDelegate(superTypeListEntry, type, delegateFieldsMap.size)
                        delegateFieldsMap[index] = delegateField.symbol
                    }
                }
            }

            when {
                this is KtClass && classKind == ClassKind.ENUM_CLASS && superTypeCallEntry == null -> {
                    /*
                     * kotlin.Enum constructor has (name: String, ordinal: Int) signature,
                     *   so we should generate non-trivial constructors for enum and it's entry
                     *   for correct resolve of super constructor call or just call kotlin.Any constructor
                     *   and convert it to right call at backend, because of it doesn't affects frontend work
                     */
                    delegatedSuperTypeRef = buildResolvedTypeRef {
                        coneType = ConeClassLikeTypeImpl(
                            implicitEnumType.coneType.lookupTag,
                            delegatedSelfTypeRef?.coneType?.let { arrayOf(it) } ?: emptyArray(),
                            isMarkedNullable = false,
                        )
                        source = container.source?.fakeElement(KtFakeSourceElementKind.EnumSuperTypeRef)
                    }
                    container.superTypeRefs += delegatedSuperTypeRef!!
                }
                this is KtClass && classKind == ClassKind.ANNOTATION_CLASS -> {
                    container.superTypeRefs += implicitAnnotationType
                    delegatedSuperTypeRef = implicitAnyType
                }
            }

            val constructedClassId = [email protected]
            val isKotlinAny = constructedClassId == StandardClassIds.Any
            val defaultDelegatedSuperTypeRef =
                when {
                    classKind == ClassKind.ENUM_ENTRY && this is KtClass -> delegatedEnumSuperTypeRef ?: implicitAnyType
                    container.superTypeRefs.isEmpty() && !isKotlinAny -> implicitAnyType
                    else -> FirImplicitTypeRefImplWithoutSource
                }

            if (container.superTypeRefs.isEmpty() && !isKotlinAny) {
                val classIsKotlinNothing = constructedClassId == StandardClassIds.Nothing
                // kotlin.Nothing doesn't have `Any` supertype, but does have delegating constructor call to Any
                if (!classIsKotlinNothing) {
                    container.superTypeRefs += implicitAnyType
                }
                delegatedSuperTypeRef = implicitAnyType
            }

            // TODO: in case we have no primary constructor,
            // it may be not possible to determine delegated super type right here
            delegatedSuperTypeRef = delegatedSuperTypeRef ?: defaultDelegatedSuperTypeRef

            // We are never here as part of enum entry
            val shouldGenerateImplicitPrimaryConstructor =
                !hasSecondaryConstructors() &&
                        !containingClassIsExpectClass &&
                        (this !is KtClass || !this.isInterface())

            val hasPrimaryConstructor = primaryConstructor != null || shouldGenerateImplicitPrimaryConstructor
            if (hasPrimaryConstructor || superTypeCallEntry != null) {
                val firPrimaryConstructor = primaryConstructor.toFirConstructor(
                    superTypeCallEntry,
                    delegatedSuperTypeRef,
                    delegatedSelfTypeRef ?: delegatedSuperTypeRef!!,
                    owner = this,
                    containerTypeParameters,
                    allSuperTypeCallEntries,
                    containingClassIsExpectClass,
                    copyConstructedTypeRefWithImplicitSource = true,
                    isErrorConstructor = !hasPrimaryConstructor,
                    isImplicitlyActual = container.status.isActual && (container.status.isInline || classKind == ClassKind.ANNOTATION_CLASS),
                    isKotlinAny = isKotlinAny,
                )
                container.declarations += firPrimaryConstructor
            }

            delegateFieldsMap.values.mapTo(container.declarations) { it.fir }
            return delegatedSuperTypeRef!! to delegateFieldsMap.takeIf { it.isNotEmpty() }
        }

        /**
         * @param delegatedSuperTypeRef can be null if containingClassIsExpectClass is true
         */
        protected fun KtPrimaryConstructor?.toFirConstructor(
            superTypeCallEntry: KtSuperTypeCallEntry?,
            delegatedSuperTypeRef: FirTypeRef?,
            delegatedSelfTypeRef: FirTypeRef,
            owner: KtClassOrObject,
            ownerTypeParameters: List,
            allSuperTypeCallEntries: List>,
            containingClassIsExpectClass: Boolean,
            copyConstructedTypeRefWithImplicitSource: Boolean,
            isErrorConstructor: Boolean = false,
            isImplicitlyActual: Boolean = false,
            isKotlinAny: Boolean = false,
        ): FirConstructor {
            val constructorSymbol = FirConstructorSymbol(callableIdForClassConstructor())
            withContainerSymbol(constructorSymbol) {
                val constructorSource = this?.toFirSourceElement()
                    ?: owner.toKtPsiSourceElement(KtFakeSourceElementKind.ImplicitConstructor)

                fun buildDelegatedCall(
                    superTypeCallEntry: KtSuperTypeCallEntry?,
                    delegatedTypeRef: FirTypeRef,
                ): FirDelegatedConstructorCall? {
                    val constructorCall = superTypeCallEntry?.toFirSourceElement()
                    val constructedTypeRef = if (copyConstructedTypeRefWithImplicitSource) {
                        delegatedTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef)
                    } else {
                        delegatedTypeRef
                    }
                    return buildOrLazyDelegatedConstructorCall(isThis = false, constructedTypeRef) {
                        buildDelegatedConstructorCall {
                            source = constructorCall ?: constructorSource.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall)
                            this.constructedTypeRef = constructedTypeRef
                            isThis = false
                            calleeReference = buildExplicitSuperReference {
                                source =
                                    superTypeCallEntry?.calleeExpression?.toFirSourceElement(KtFakeSourceElementKind.DelegatingConstructorCall)
                                        ?: [email protected]?.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall)
                                superTypeRef = [email protected]
                            }
                            superTypeCallEntry?.extractArgumentsTo(this)
                        }
                    }
                }

                val generateDelegatedSuperCall = shouldGenerateDelegatedSuperCall(
                    isAnySuperCall = isKotlinAny,
                    isExpectClass = containingClassIsExpectClass,
                    isEnumEntry = owner is KtEnumEntry,
                    hasExplicitDelegatedCalls = allSuperTypeCallEntries.isNotEmpty()
                )

                val firDelegatedCall = runIf(generateDelegatedSuperCall) {
                    if (allSuperTypeCallEntries.size <= 1) {
                        buildDelegatedCall(superTypeCallEntry, delegatedSuperTypeRef!!)
                    } else {
                        buildMultiDelegatedConstructorCall {
                            allSuperTypeCallEntries.mapTo(delegatedConstructorCalls) { (superTypeCallEntry, delegatedTypeRef) ->
                                buildDelegatedCall(superTypeCallEntry, delegatedTypeRef)!!
                            }
                        }
                    }
                }

                val explicitVisibility = this?.constructorExplicitVisibility
                val status = FirDeclarationStatusImpl(explicitVisibility ?: constructorDefaultVisibility(owner), Modality.FINAL).apply {
                    isExpect = this@toFirConstructor?.hasExpectModifier() == true || [email protected]
                    isActual = this@toFirConstructor?.hasActualModifier() == true || isImplicitlyActual

                    // a warning about inner script class is reported on the class itself
                    isInner = owner.parent.parent !is KtScript && owner.hasModifier(INNER_KEYWORD)
                    isFromSealedClass = owner.hasModifier(SEALED_KEYWORD) && explicitVisibility !== Visibilities.Private
                    isFromEnumClass = owner.hasModifier(ENUM_KEYWORD)
                }

                val builder = when {
                    isErrorConstructor -> createErrorConstructorBuilder(ConeNoConstructorError)
                    else -> FirPrimaryConstructorBuilder()
                }

                builder.apply {
                    source = constructorSource
                    moduleData = baseModuleData
                    origin = FirDeclarationOrigin.Source
                    returnTypeRef = delegatedSelfTypeRef
                    this.status = status
                    dispatchReceiverType = owner.obtainDispatchReceiverForConstructor()
                    symbol = constructorSymbol
                    delegatedConstructor = firDelegatedCall
                    typeParameters += constructorTypeParametersFromConstructedClass(ownerTypeParameters)
                    this.contextReceivers.addAll(convertContextReceivers(owner.contextReceivers))
                    this@toFirConstructor?.extractAnnotationsTo(this)
                    this@toFirConstructor?.extractValueParametersTo(this, symbol, ValueParameterDeclaration.PRIMARY_CONSTRUCTOR)
                    this.body = null
                }

                return builder.build().apply {
                    containingClassForStaticMemberAttr = currentDispatchReceiverType()!!.lookupTag
                }
            }
        }

        private fun KtClassOrObject.obtainDispatchReceiverForConstructor(): ConeClassLikeType? =
            if (hasModifier(INNER_KEYWORD)) dispatchReceiverForInnerClassConstructor() else null

        override fun visitKtFile(file: KtFile, data: FirElement?): FirElement {
            context.packageFqName = when (mode) {
                BodyBuildingMode.NORMAL -> file.properPackageFqName
                BodyBuildingMode.LAZY_BODIES -> file.stub?.getPackageFqName() ?: file.properPackageFqName
            }
            return buildFile {
                source = file.toFirSourceElement()
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                name = file.name
                sourceFile = KtPsiSourceFile(file)
                sourceFileLinesMapping = KtPsiSourceFileLinesMapping(file)
                packageDirective = buildPackageDirective {
                    packageFqName = context.packageFqName
                    source = file.packageDirective?.toKtPsiSourceElement()
                }

                file.fileAnnotationList?.let {
                    withContainerSymbol(symbol) {
                        for (annotationEntry in it.annotationEntries) {
                            annotations += annotationEntry.convert()
                        }
                    }
                }

                for (importDirective in file.importDirectives) {
                    imports += buildImport {
                        source = importDirective.toFirSourceElement()
                        importedFqName = importDirective.importedFqName
                        isAllUnder = importDirective.isAllUnder
                        aliasName = importDirective.aliasName?.let { Name.identifier(it) }
                        aliasSource = importDirective.alias?.nameIdentifier?.toFirSourceElement()
                    }
                }

                if (file is KtCodeFragment) {
                    declarations += convertCodeFragment(file, this)
                } else {
                    for (declaration in file.declarations) {
                        declarations += when (declaration) {
                            is KtScript -> {
                                requireWithAttachment(
                                    file.declarations.size == 1,
                                    message = { "Expect the script to be the only declaration in the file" },
                                ) {
                                    withEntry("fileName", file.name)
                                }

                                convertScript(declaration, name, sourceFile) {
                                    for (configurator in baseSession.extensionService.scriptConfigurators) {
                                        with(configurator) { configureContainingFile(this@buildFile) }
                                    }
                                }
                            }
                            is KtDestructuringDeclaration -> buildErrorTopLevelDestructuringDeclaration(declaration.toFirSourceElement())
                            else -> declaration.convert()
                        }
                    }

                    for (danglingModifierList in file.danglingModifierLists) {
                        declarations += buildErrorTopLevelDeclarationForDanglingModifierList(danglingModifierList)
                    }
                }
            }
        }

        private val KtFile.properPackageFqName: FqName
            get() = packageDirective?.let(::parsePackageName) ?: FqName.ROOT

        private fun parsePackageName(node: KtPackageDirective): FqName {
            var packageName: FqName = FqName.ROOT
            val parts = node.getPackageNames()

            for (part in parts) {
                packageName = packageName.child(Name.identifier(part.getReferencedName()))
            }

            return packageName
        }

        protected fun configureScriptDestructuringDeclarationEntry(declaration: FirVariable, container: FirVariable) {
            (declaration as FirProperty).destructuringDeclarationContainerVariable = container.symbol
        }

        protected fun buildScriptDestructuringDeclaration(destructuringDeclaration: KtDestructuringDeclaration): FirVariable {
            val initializer = destructuringDeclaration.initializer
            val firInitializer = buildOrLazyExpression(initializer?.toFirSourceElement()) {
                initializer.toFirExpression { ConeSyntaxDiagnostic("Initializer required for destructuring declaration") }
            }

            val destructuringContainerVar = generateTemporaryVariable(
                moduleData = baseModuleData,
                source = destructuringDeclaration.toFirSourceElement(),
                specialName = "destruct",
                initializer = firInitializer,
                origin = FirDeclarationOrigin.Synthetic.ScriptTopLevelDestructuringDeclarationContainer,
                extractAnnotationsTo = { extractAnnotationsTo(it) },
            ).apply {
                isDestructuringDeclarationContainerVariable = true
            }

            return destructuringContainerVar
        }

        private fun convertScript(
            script: KtScript,
            fileName: String,
            sourceFile: KtSourceFile?,
            setup: FirScriptBuilder.() -> Unit = {},
        ): FirScript {
            val scriptName = Name.special("")
            val scriptSymbol = FirScriptSymbol(context.packageFqName.child(scriptName))

            return buildScript {
                source = script.toFirSourceElement()
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                name = scriptName
                symbol = scriptSymbol

                val scriptDeclarationsIter = script.declarations.listIterator()
                withContainerScriptSymbol(symbol) {
                    while (scriptDeclarationsIter.hasNext()) {
                        val declaration = scriptDeclarationsIter.next()
                        val isLast = !scriptDeclarationsIter.hasNext()
                        when (declaration) {
                            is KtScriptInitializer -> {
                                val initializer = buildAnonymousInitializer(
                                    initializer = declaration,
                                    containingDeclarationSymbol = scriptSymbol,
                                    // the last one need to be analyzed in script configurator to decide on result property
                                    // therefore no lazy conversion in this case
                                    allowLazyBody = !isLast,
                                    // the last anonymous initializer could be converted to a property and its symbol will be dropped
                                    // therefore we should not rely on it as a containing declaration symbol, and use the parent one instead
                                    isLocal = isLast,
                                )

                                declarations.add(initializer)
                            }
                            is KtDestructuringDeclaration -> {
                                val destructuringContainerVar = buildScriptDestructuringDeclaration(declaration)
                                declarations.add(destructuringContainerVar)

                                addDestructuringVariables(
                                    declarations,
                                    this@Visitor,
                                    moduleData,
                                    declaration,
                                    destructuringContainerVar,
                                    tmpVariable = false,
                                    forceLocal = false,
                                ) {
                                    configureScriptDestructuringDeclarationEntry(it, destructuringContainerVar)
                                }
                            }
                            else -> {
                                val firStatement = declaration.toFirStatement()
                                if (firStatement is FirDeclaration) {
                                    declarations.add(firStatement)
                                } else {
                                    error("unexpected declaration type in script")
                                }
                            }
                        }
                    }
                    setup()
                    if (sourceFile != null) {
                        for (configurator in baseSession.extensionService.scriptConfigurators) {
                            with(configurator) { configure(sourceFile, context) }
                        }
                    }
                }
            }
        }

        private fun convertCodeFragment(
            file: KtCodeFragment,
            // We ask to pass the fileBuilder explicitly despite it's not in use: FirFile should be always a parent
            @Suppress("unused") fileBuilder: FirFileBuilder
        ): FirCodeFragment = buildCodeFragment {
            source = file.toFirSourceElement()
            moduleData = baseModuleData
            origin = FirDeclarationOrigin.Source
            symbol = FirCodeFragmentSymbol()
            withContainerSymbol(symbol) {
                block = buildOrLazyBlock {
                    withForcedLocalContext {
                        when (file) {
                            is KtExpressionCodeFragment -> file.getContentElement()?.toFirBlock() ?: buildEmptyExpressionBlock()
                            is KtBlockCodeFragment -> configureBlockWithoutBuilding(file.getContentElement()).build()
                            is KtTypeCodeFragment -> convertTypeCodeFragmentBlock(file)
                            else -> error("Unexpected code fragment type: ${file::class}")
                        }
                    }
                }
            }
        }

        private fun convertTypeCodeFragmentBlock(file: KtTypeCodeFragment): FirBlock {
            return buildBlock {
                val functionSymbol = FirAnonymousFunctionSymbol()

                statements += buildAnonymousFunctionExpression {
                    anonymousFunction = buildAnonymousFunction {
                        moduleData = baseModuleData
                        origin = FirDeclarationOrigin.Source
                        symbol = functionSymbol

                        hasExplicitParameterList = true
                        isLambda = false

                        // Place the type reference in the parameter position
                        // so it doesn't require special handling in the function body
                        valueParameters += buildValueParameter {
                            moduleData = baseModuleData
                            origin = FirDeclarationOrigin.Source
                            name = StandardNames.DEFAULT_VALUE_PARAMETER

                            symbol = FirValueParameterSymbol(name)
                            containingFunctionSymbol = functionSymbol

                            returnTypeRef = file.getContentElement().toFirOrErrorType()
                            isCrossinline = false
                            isNoinline = false
                            isVararg = false
                        }

                        returnTypeRef = implicitUnitType

                        body = buildBlock()
                    }
                }
            }
        }

        override fun visitScript(script: KtScript, data: FirElement?): FirElement {
            val ktFile = script.containingKtFile
            val fileName = ktFile.name
            val fileForSource = (data as? FirScript)?.psi?.containingFile as? KtFile ?: ktFile
            return convertScript(script, fileName, KtPsiSourceFile(fileForSource))
        }

        protected fun KtEnumEntry.toFirEnumEntry(
            delegatedEnumSelfTypeRef: FirResolvedTypeRef,
            ownerClassHasDefaultConstructor: Boolean,
        ): FirDeclaration {
            val ktEnumEntry = this@toFirEnumEntry
            val containingClassIsExpectClass = hasExpectModifier() || [email protected]
            val enumSymbol = FirEnumEntrySymbol(callableIdForName(nameAsSafeName))
            return withContainerSymbol(enumSymbol) {
                buildEnumEntry {
                    source = toFirSourceElement()
                    moduleData = baseModuleData
                    origin = FirDeclarationOrigin.Source
                    returnTypeRef = delegatedEnumSelfTypeRef
                    name = nameAsSafeName
                    status = FirDeclarationStatusImpl(Visibilities.Public, Modality.FINAL).apply {
                        isStatic = true
                        isExpect = containingClassIsExpectClass
                    }
                    symbol = enumSymbol
                    if (ownerClassHasDefaultConstructor && ktEnumEntry.initializerList == null &&
                        ktEnumEntry.annotationEntries.isEmpty() && ktEnumEntry.body == null
                    ) {
                        return@buildEnumEntry
                    }
                    extractAnnotationsTo(this)
                    initializer = buildOrLazyExpression(toFirSourceElement(KtFakeSourceElementKind.EnumInitializer)) {
                        withChildClassName(nameAsSafeName, isExpect = false) {
                            buildAnonymousObjectExpression {
                                val enumEntrySource = toFirSourceElement(KtFakeSourceElementKind.EnumInitializer)
                                source = enumEntrySource
                                anonymousObject = buildAnonymousObject {
                                    source = enumEntrySource
                                    moduleData = baseModuleData
                                    origin = FirDeclarationOrigin.Source
                                    classKind = ClassKind.ENUM_ENTRY
                                    scopeProvider = [email protected]
                                    symbol = FirAnonymousObjectSymbol([email protected])
                                    status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL)

                                    val delegatedEntrySelfType = buildResolvedTypeRef {
                                        coneType =
                                            ConeClassLikeTypeImpl(
                                                [email protected](),
                                                emptyArray(),
                                                isMarkedNullable = false
                                            )
                                    }
                                    registerSelfType(delegatedEntrySelfType)

                                    superTypeRefs += delegatedEnumSelfTypeRef
                                    val superTypeCallEntry = superTypeListEntries.firstIsInstanceOrNull()
                                    val correctedEnumSelfTypeRef = buildResolvedTypeRef {
                                        source = superTypeCallEntry?.calleeExpression?.typeReference?.toFirSourceElement()
                                        coneType = delegatedEnumSelfTypeRef.coneType
                                    }
                                    declarations += primaryConstructor.toFirConstructor(
                                        superTypeCallEntry,
                                        correctedEnumSelfTypeRef,
                                        delegatedEntrySelfType,
                                        owner = ktEnumEntry,
                                        typeParameters,
                                        allSuperTypeCallEntries = emptyList(),
                                        containingClassIsExpectClass,
                                        copyConstructedTypeRefWithImplicitSource = true,
                                    )
                                    // Use ANONYMOUS_OBJECT_NAME for the owner class id for enum entry declarations (see KT-42351)
                                    withChildClassName(SpecialNames.ANONYMOUS, forceLocalContext = true, isExpect = false) {
                                        for (declaration in ktEnumEntry.declarations) {
                                            declarations += declaration.toFirDeclaration(
                                                correctedEnumSelfTypeRef,
                                                delegatedSelfType = delegatedEntrySelfType,
                                                ktEnumEntry,
                                                ownerClassBuilder = this,
                                                ownerTypeParameters = emptyList()
                                            )
                                        }
                                    }
                                }
                            }
                        }
                    }
                }.apply {
                    containingClassForStaticMemberAttr = currentDispatchReceiverType()!!.lookupTag
                }
            }
        }

        private fun convertContextReceivers(receivers: List): List {
            return receivers.map { contextReceiverElement ->
                buildContextReceiver {
                    this.source = contextReceiverElement.toFirSourceElement()
                    this.customLabelName = contextReceiverElement.labelNameAsName()
                    this.labelNameFromTypeRef = contextReceiverElement.typeReference()?.nameForReceiverLabel()?.let(Name::identifier)

                    contextReceiverElement.typeReference()?.toFirType()?.let {
                        this.typeRef = it
                    }
                }
            }
        }

        override fun visitClassOrObject(classOrObject: KtClassOrObject, data: FirElement?): FirElement {
            val isLocalWithinParent = classOrObject.parent !is KtClassBody && classOrObject.isLocal
            val classIsExpect = classOrObject.hasExpectModifier() || context.containerIsExpect
            val sourceElement = classOrObject.toFirSourceElement()
            return withChildClassName(
                classOrObject.nameAsSafeName,
                isExpect = classIsExpect,
                forceLocalContext = isLocalWithinParent,
            ) {
                val classSymbol = FirRegularClassSymbol(context.currentClassId)
                withContainerSymbol(classSymbol) {
                    val isLocal = context.inLocalContext
                    val classKind = when (classOrObject) {
                        is KtObjectDeclaration -> ClassKind.OBJECT
                        is KtClass -> when {
                            classOrObject.isInterface() -> ClassKind.INTERFACE
                            classOrObject.isEnum() -> ClassKind.ENUM_CLASS
                            classOrObject.isAnnotation() -> ClassKind.ANNOTATION_CLASS
                            else -> ClassKind.CLASS
                        }
                        else -> throw AssertionError("Unexpected class or object: ${classOrObject.text}")
                    }
                    val status = FirDeclarationStatusImpl(
                        if (isLocal) Visibilities.Local else classOrObject.getVisibility(publicByDefault = true),
                        classOrObject.modality,
                    ).apply {
                        isExpect = classIsExpect
                        isActual = classOrObject.hasActualModifier()
                        isInner = classOrObject.hasModifier(INNER_KEYWORD) && classOrObject.parent.parent !is KtScript
                        isCompanion = (classOrObject as? KtObjectDeclaration)?.isCompanion() == true
                        isData = classOrObject.hasModifier(DATA_KEYWORD)
                        isInline = classOrObject.hasModifier(INLINE_KEYWORD) || classOrObject.hasModifier(VALUE_KEYWORD)
                        isFun = classOrObject.hasModifier(FUN_KEYWORD)
                        isExternal = classOrObject.hasModifier(EXTERNAL_KEYWORD)
                    }

                    withCapturedTypeParameters(status.isInner || isLocal, sourceElement, listOf()) {
                        var delegatedFieldsMap: Map?
                        buildRegularClass {
                            source = sourceElement
                            moduleData = baseModuleData
                            origin = FirDeclarationOrigin.Source
                            name = classOrObject.nameAsSafeName
                            this.status = status
                            this.classKind = classKind
                            scopeProvider = baseScopeProvider
                            symbol = classSymbol

                            classOrObject.extractAnnotationsTo(this)
                            classOrObject.extractTypeParametersTo(this, symbol)

                            context.appendOuterTypeParameters(ignoreLastLevel = true, typeParameters)
                            context.pushFirTypeParameters(
                                status.isInner || isLocal,
                                typeParameters.subList(0, classOrObject.typeParameters.size)
                            )

                            val delegatedSelfType = classOrObject.toDelegatedSelfType(this)
                            registerSelfType(delegatedSelfType)

                            val (delegatedSuperType, extractedDelegatedFieldsMap) = classOrObject.extractSuperTypeListEntriesTo(
                                this,
                                delegatedSelfType,
                                null,
                                classKind,
                                typeParameters,
                                containingClassIsExpectClass = classIsExpect
                            )
                            delegatedFieldsMap = extractedDelegatedFieldsMap

                            val primaryConstructor = classOrObject.primaryConstructor
                            val firPrimaryConstructor = declarations.firstOrNull { it is FirConstructor } as? FirConstructor
                            if (primaryConstructor != null && firPrimaryConstructor != null) {
                                primaryConstructor.valueParameters.zip(
                                    firPrimaryConstructor.valueParameters
                                ).forEach { (ktParameter, firParameter) ->
                                    if (ktParameter.hasValOrVar()) {
                                        addDeclaration(ktParameter.toFirProperty(firParameter))
                                    }
                                }
                            }

                            for (declaration in classOrObject.declarations) {
                                addDeclaration(
                                    declaration.toFirDeclaration(
                                        delegatedSuperType,
                                        delegatedSelfType,
                                        classOrObject,
                                        this,
                                        typeParameters
                                    )
                                )
                            }
                            for (danglingModifier in classOrObject.body?.danglingModifierLists ?: emptyList()) {
                                addDeclaration(
                                    buildErrorTopLevelDeclarationForDanglingModifierList(danglingModifier).apply {
                                        containingClassAttr = currentDispatchReceiverType()?.lookupTag
                                    }
                                )
                            }

                            if (classOrObject.hasModifier(DATA_KEYWORD) && firPrimaryConstructor != null) {
                                val zippedParameters =
                                    classOrObject.primaryConstructorParameters.filter { it.hasValOrVar() } zip declarations.filterIsInstance()
                                DataClassMembersGenerator(
                                    classOrObject,
                                    this,
                                    zippedParameters,
                                    context.packageFqName,
                                    context.className,
                                    createClassTypeRefWithSourceKind = { firPrimaryConstructor.returnTypeRef.copyWithNewSourceKind(it) },
                                    createParameterTypeRefWithSourceKind = { property, newKind ->
                                        property.returnTypeRef.copyWithNewSourceKind(newKind)
                                    },
                                    addValueParameterAnnotations = {
                                        addAnnotationsFrom(
                                            it as KtParameter,
                                            isFromPrimaryConstructor = true,
                                        )
                                    },
                                ).generate()
                            }

                            if (classOrObject.hasModifier(ENUM_KEYWORD)) {
                                generateValuesFunction(
                                    baseModuleData,
                                    context.packageFqName,
                                    context.className,
                                    classIsExpect
                                )
                                generateValueOfFunction(
                                    baseModuleData, context.packageFqName, context.className,
                                    classIsExpect
                                )
                                generateEntriesGetter(
                                    baseModuleData, context.packageFqName, context.className,
                                    classIsExpect
                                )
                            }

                            initCompanionObjectSymbolAttr()

                            context.popFirTypeParameters()
                            contextReceivers.addAll(convertContextReceivers(classOrObject.contextReceivers))
                        }.also {
                            it.delegateFieldsMap = delegatedFieldsMap
                        }
                    }.also {
                        classOrObject.fillDanglingConstraintsTo(it)
                    }
                }
            }.also {
                if (classOrObject.parent is KtClassBody) {
                    it.initContainingClassForLocalAttr()
                }
                context.containingScriptSymbol?.let { script ->
                    it.containingScriptSymbolAttr = script
                }
            }
        }

        override fun visitObjectLiteralExpression(expression: KtObjectLiteralExpression, data: FirElement?): FirElement {
            return withChildClassName(SpecialNames.ANONYMOUS, forceLocalContext = true, isExpect = false) {
                var delegatedFieldsMap: Map?
                buildAnonymousObjectExpression {
                    source = expression.toFirSourceElement()
                    anonymousObject = buildAnonymousObject {
                        val objectDeclaration = expression.objectDeclaration
                        source = objectDeclaration.toFirSourceElement()
                        moduleData = baseModuleData
                        origin = FirDeclarationOrigin.Source
                        classKind = ClassKind.CLASS
                        scopeProvider = baseScopeProvider
                        symbol = FirAnonymousObjectSymbol(context.packageFqName)
                        status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL)
                        context.appendOuterTypeParameters(ignoreLastLevel = false, typeParameters)
                        val delegatedSelfType = objectDeclaration.toDelegatedSelfType(this)
                        registerSelfType(delegatedSelfType)
                        objectDeclaration.extractAnnotationsTo(this)
                        val (delegatedSuperType, extractedDelegatedFieldsMap) = objectDeclaration.extractSuperTypeListEntriesTo(
                            this,
                            delegatedSelfType,
                            null,
                            ClassKind.CLASS,
                            containerTypeParameters = emptyList(),
                            containingClassIsExpectClass = false
                        )
                        delegatedFieldsMap = extractedDelegatedFieldsMap

                        for (declaration in objectDeclaration.declarations) {
                            declarations += declaration.toFirDeclaration(
                                delegatedSuperType,
                                delegatedSelfType,
                                owner = objectDeclaration,
                                ownerClassBuilder = this,
                                ownerTypeParameters = emptyList()
                            )
                        }

                        for (danglingModifier in objectDeclaration.body?.danglingModifierLists ?: emptyList()) {
                            declarations += buildErrorTopLevelDeclarationForDanglingModifierList(danglingModifier).apply {
                                containingClassAttr = currentDispatchReceiverType()?.lookupTag
                            }
                        }
                    }.also {
                        it.delegateFieldsMap = delegatedFieldsMap
                    }
                }
            }
        }

        override fun visitTypeAlias(typeAlias: KtTypeAlias, data: FirElement?): FirElement {
            val typeAliasIsExpect = typeAlias.hasExpectModifier() || context.containerIsExpect
            return withChildClassName(typeAlias.nameAsSafeName, isExpect = typeAliasIsExpect) {
                buildTypeAlias {
                    symbol = FirTypeAliasSymbol(context.currentClassId)
                    withContainerSymbol(symbol) {
                        source = typeAlias.toFirSourceElement()
                        moduleData = baseModuleData
                        origin = FirDeclarationOrigin.Source
                        name = typeAlias.nameAsSafeName
                        val isLocal = context.inLocalContext
                        status = FirDeclarationStatusImpl(
                            if (isLocal) Visibilities.Local else typeAlias.getVisibility(publicByDefault = true),
                            Modality.FINAL
                        ).apply {
                            isExpect = typeAliasIsExpect
                            isActual = typeAlias.hasActualModifier()
                        }
                        expandedTypeRef = typeAlias.getTypeReference().toFirOrErrorType()
                        typeAlias.extractAnnotationsTo(this)
                        typeAlias.extractTypeParametersTo(this, symbol)
                    }
                }
            }
        }

        override fun visitNamedFunction(function: KtNamedFunction, data: FirElement?): FirElement {
            val isAnonymousFunction = function.isAnonymous
            val isLocalFunction = function.isLocal
            val functionSymbol: FirFunctionSymbol<*> = if (isAnonymousFunction) {
                FirAnonymousFunctionSymbol()
            } else {
                FirNamedFunctionSymbol(callableIdForName(function.nameAsSafeName))
            }

            withContainerSymbol(functionSymbol, isLocalFunction) {
                val typeReference = function.typeReference
                val returnType = if (function.hasBlockBody()) {
                    typeReference.toFirOrUnitType()
                } else {
                    typeReference.toFirOrImplicitType()
                }

                val receiverType = function.receiverTypeReference?.toFirType()
                val labelName: String?

                val functionBuilder = if (isAnonymousFunction) {
                    FirAnonymousFunctionBuilder().apply {
                        receiverParameter = receiverType?.convertToReceiverParameter()
                        symbol = functionSymbol as FirAnonymousFunctionSymbol
                        isLambda = false
                        hasExplicitParameterList = true
                        label = context.getLastLabel(function)
                        labelName = label?.name ?: context.calleeNamesForLambda.lastOrNull()?.identifier
                        if (function.hasModifier(SUSPEND_KEYWORD)) {
                            status = FirResolvedDeclarationStatusImpl.DEFAULT_STATUS_FOR_SUSPEND_FUNCTION_EXPRESSION
                        }
                    }
                } else {
                    FirSimpleFunctionBuilder().apply {
                        receiverParameter = receiverType?.convertToReceiverParameter()
                        name = function.nameAsSafeName
                        labelName = context.getLastLabel(function)?.name ?: runIf(!name.isSpecial) { name.identifier }
                        symbol = functionSymbol as FirNamedFunctionSymbol
                        dispatchReceiverType = runIf(!isLocalFunction) { currentDispatchReceiverType() }
                        status = FirDeclarationStatusImpl(
                            if (isLocalFunction) Visibilities.Local else function.getVisibility(),
                            function.modality,
                        ).apply {
                            isExpect = function.hasExpectModifier() || context.containerIsExpect
                            isActual = function.hasActualModifier()
                            isOverride = function.hasModifier(OVERRIDE_KEYWORD)
                            isOperator = function.hasModifier(OPERATOR_KEYWORD)
                            isInfix = function.hasModifier(INFIX_KEYWORD)
                            isInline = function.hasModifier(INLINE_KEYWORD)
                            isTailRec = function.hasModifier(TAILREC_KEYWORD)
                            isExternal = function.hasModifier(EXTERNAL_KEYWORD)
                            isSuspend = function.hasModifier(SUSPEND_KEYWORD)
                        }

                        contextReceivers.addAll(convertContextReceivers(function.contextReceivers))
                    }
                }

                val target = FirFunctionTarget(labelName, isLambda = false)
                val functionSource = function.toFirSourceElement()
                val firFunction = functionBuilder.apply {
                    source = functionSource
                    moduleData = baseModuleData
                    origin = FirDeclarationOrigin.Source
                    returnTypeRef = returnType

                    context.firFunctionTargets += target
                    function.extractAnnotationsTo(this)
                    if (this is FirSimpleFunctionBuilder) {
                        function.extractTypeParametersTo(this, symbol)
                    }
                    for (valueParameter in function.valueParameters) {
                        valueParameters += valueParameter.toFirValueParameter(
                            null,
                            functionSymbol,
                            if (isAnonymousFunction) ValueParameterDeclaration.LAMBDA else ValueParameterDeclaration.FUNCTION,
                        )
                    }
                    val actualTypeParameters = if (this is FirSimpleFunctionBuilder)
                        this.typeParameters
                    else
                        listOf()
                    withCapturedTypeParameters(true, functionSource, actualTypeParameters) {
                        val outerContractDescription = function.obtainContractDescription()
                        val (body, innerContractDescription) = withForcedLocalContext {
                            function.buildFirBody()
                        }
                        this.body = body
                        val contractDescription = outerContractDescription ?: innerContractDescription
                        contractDescription?.let {
                            if (this is FirSimpleFunctionBuilder) {
                                this.contractDescription = it
                            } else if (this is FirAnonymousFunctionBuilder) {
                                this.contractDescription = it
                            }
                        }
                    }
                    context.firFunctionTargets.removeLast()
                }.build().also {
                    bindFunctionTarget(target, it)
                    if (it is FirSimpleFunction) {
                        function.fillDanglingConstraintsTo(it)
                    }
                }

                return if (firFunction is FirAnonymousFunction) {
                    buildAnonymousFunctionExpression {
                        source = functionSource
                        anonymousFunction = firFunction
                    }
                } else {
                    firFunction
                }
            }
        }

        private fun KtDeclarationWithBody.obtainContractDescription(): FirContractDescription? {
            return when (val description = contractDescription) {
                null -> null
                else -> buildRawContractDescription {
                    source = description.toFirSourceElement()
                    description.extractRawEffects(rawEffects)
                }
            }
        }

        private fun KtContractEffectList.extractRawEffects(destination: MutableList) {
            getContractEffects().mapTo(destination) { effect ->
                buildOrLazyExpression(effect.toFirSourceElement()) {
                    effect.getExpression().accept(this@Visitor, null) as FirExpression
                }
            }
        }

        override fun visitLambdaExpression(expression: KtLambdaExpression, data: FirElement?): FirElement {
            val literal = expression.functionLiteral
            val literalSource = literal.toFirSourceElement()

            val target: FirFunctionTarget
            val anonymousFunction = buildAnonymousFunction {
                source = literalSource
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                returnTypeRef = FirImplicitTypeRefImplWithoutSource
                receiverParameter = literalSource.asReceiverParameter()
                symbol = FirAnonymousFunctionSymbol()
                isLambda = true
                hasExplicitParameterList = expression.functionLiteral.arrow != null

                val destructuringVariables = mutableListOf()
                for (valueParameter in literal.valueParameters) {
                    val multiDeclaration = valueParameter.destructuringDeclaration
                    valueParameters += if (multiDeclaration != null) {
                        val name = SpecialNames.DESTRUCT
                        val multiParameter = buildValueParameter {
                            source = valueParameter.toFirSourceElement()
                            containingFunctionSymbol = [email protected]
                            moduleData = baseModuleData
                            origin = FirDeclarationOrigin.Source
                            returnTypeRef = valueParameter.typeReference.toFirOrImplicitType()
                            this.name = name
                            symbol = FirValueParameterSymbol(name)
                            isCrossinline = false
                            isNoinline = false
                            isVararg = false
                        }
                        addDestructuringVariables(
                            destructuringVariables,
                            this@Visitor,
                            baseModuleData,
                            multiDeclaration,
                            multiParameter,
                            tmpVariable = false,
                            forceLocal = true,
                        )
                        multiParameter
                    } else {
                        val typeRef = valueParameter.typeReference.toFirOrImplicitType()
                        valueParameter.toFirValueParameter(typeRef, symbol, ValueParameterDeclaration.LAMBDA)
                    }
                }
                val expressionSource = expression.toFirSourceElement()
                label = context.getLastLabel(expression) ?: context.calleeNamesForLambda.lastOrNull()?.let {
                    buildLabel {
                        source = expressionSource.fakeElement(KtFakeSourceElementKind.GeneratedLambdaLabel)
                        name = it.asString()
                    }
                }
                target = FirFunctionTarget(label?.name, isLambda = true).also {
                    context.firFunctionTargets += it
                }
                val ktBody = literal.bodyExpression
                body = withForcedLocalContext {
                    if (ktBody == null) {
                        val errorExpression = buildErrorExpression(source, ConeSyntaxDiagnostic("Lambda has no body"))
                        FirSingleExpressionBlock(errorExpression.toReturn())
                    } else {
                        val kind = runIf(destructuringVariables.isNotEmpty()) {
                            KtFakeSourceElementKind.LambdaDestructuringBlock
                        }
                        val bodyBlock = configureBlockWithoutBuilding(ktBody, kind).apply {
                            if (statements.isEmpty()) {
                                statements.add(
                                    buildReturnExpression {
                                        source = expressionSource.fakeElement(KtFakeSourceElementKind.ImplicitReturn.FromExpressionBody)
                                        this.target = target
                                        result = buildUnitExpression {
                                            source = expressionSource.fakeElement(KtFakeSourceElementKind.ImplicitUnit.ForEmptyLambda)
                                        }
                                    }
                                )
                            }
                        }.build()

                        if (destructuringVariables.isNotEmpty()) {
                            // Destructured variables must be in a separate block so that they can be shadowed.
                            buildBlock {
                                source = bodyBlock.source?.realElement()
                                statements.addAll(destructuringVariables)
                                statements.add(bodyBlock)
                            }
                        } else {
                            bodyBlock
                        }
                    }
                }
                context.firFunctionTargets.removeLast()
            }.also {
                bindFunctionTarget(target, it)
            }
            return buildAnonymousFunctionExpression {
                source = expression.toKtPsiSourceElement()
                this.anonymousFunction = anonymousFunction
            }
        }

        protected fun KtSecondaryConstructor.toFirConstructor(
            delegatedTypeRef: FirTypeRef,
            selfTypeRef: FirTypeRef,
            owner: KtClassOrObject,
            ownerTypeParameters: List,
        ): FirConstructor {
            val target = FirFunctionTarget(labelName = null, isLambda = false)
            return buildConstructor {
                symbol = FirConstructorSymbol(callableIdForClassConstructor())
                withContainerSymbol(symbol) {
                    source = [email protected]()
                    moduleData = baseModuleData
                    origin = FirDeclarationOrigin.Source
                    returnTypeRef = selfTypeRef
                    val explicitVisibility = constructorExplicitVisibility
                    status = FirDeclarationStatusImpl(explicitVisibility ?: constructorDefaultVisibility(owner), Modality.FINAL).apply {
                        isExpect = hasExpectModifier() || [email protected]
                        isActual = hasActualModifier()
                        isInner = owner.hasModifier(INNER_KEYWORD)
                        isFromSealedClass = owner.hasModifier(SEALED_KEYWORD) && explicitVisibility !== Visibilities.Private
                        isFromEnumClass = owner.hasModifier(ENUM_KEYWORD)
                    }
                    dispatchReceiverType = owner.obtainDispatchReceiverForConstructor()
                    contextReceivers.addAll(convertContextReceivers(owner.contextReceivers))
                    if (!owner.hasModifier(EXTERNAL_KEYWORD) && !status.isExpect || isExplicitDelegationCall()) {
                        delegatedConstructor = buildOrLazyDelegatedConstructorCall(
                            isThis = isDelegatedCallToThis(),
                            constructedTypeRef = delegatedTypeRef,
                        ) {
                            getDelegationCall().convert(delegatedTypeRef)
                        }
                    }
                    [email protected] += target
                    extractAnnotationsTo(this)
                    typeParameters += constructorTypeParametersFromConstructedClass(ownerTypeParameters)
                    extractValueParametersTo(this, symbol, ValueParameterDeclaration.FUNCTION)

                    val (body, contractDescription) = withForcedLocalContext {
                        buildFirBody()
                    }
                    contractDescription?.let { this.contractDescription = it }
                    this.body = body
                    [email protected]()
                }
            }.also {
                it.containingClassForStaticMemberAttr = currentDispatchReceiverType()!!.lookupTag
                bindFunctionTarget(target, it)
            }
        }

        private fun KtConstructorDelegationCall.convert(
            delegatedType: FirTypeRef,
        ): FirDelegatedConstructorCall {
            val isThis = isCallToThis //|| (isImplicit && hasPrimaryConstructor)
            val source = if (isImplicit) {
                this.toFirSourceElement(KtFakeSourceElementKind.ImplicitConstructor)
            } else {
                this.toFirSourceElement()
            }
            return buildDelegatedConstructorCall {
                this.source = source
                constructedTypeRef = delegatedType.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef)
                this.isThis = isThis
                val calleeKind =
                    if (isImplicit) KtFakeSourceElementKind.ImplicitConstructor else KtFakeSourceElementKind.DelegatingConstructorCall
                val calleeSource = [email protected]?.toFirSourceElement(calleeKind)
                    ?: source.fakeElement(calleeKind)
                this.calleeReference = if (isThis) {
                    buildExplicitThisReference {
                        this.source = calleeSource
                    }
                } else {
                    buildExplicitSuperReference {
                        this.source = calleeSource
                        this.superTypeRef = [email protected]
                    }
                }
                extractArgumentsTo(this)
            }
        }

        private fun KtDeclarationWithInitializer.toInitializerExpression() =
            runIf(hasInitializer()) {
                [email protected] += null

                val expression = buildOrLazyExpression(null) {
                    withForcedLocalContext {
                        initializer.toFirExpression("Should have initializer")
                    }
                }

                [email protected]()
                expression
            }

        private fun  KtProperty.toFirProperty(
            ownerRegularOrAnonymousObjectSymbol: FirClassSymbol<*>?,
            context: Context,
        ): FirProperty {
            val propertyName = nameAsSafeName
            val propertySymbol = if (isLocal) {
                FirPropertySymbol(propertyName)
            } else {
                FirPropertySymbol(callableIdForName(propertyName))
            }

            withContainerSymbol(propertySymbol, isLocal) {
                val propertyType = typeReference.toFirOrImplicitType()
                val isVar = isVar
                val propertyInitializer = toInitializerExpression()

                val propertySource = toFirSourceElement()

                return buildProperty {
                    source = propertySource
                    moduleData = baseModuleData
                    origin = FirDeclarationOrigin.Source
                    returnTypeRef = propertyType
                    name = propertyName
                    this.isVar = isVar

                    receiverParameter = receiverTypeReference?.toFirType()?.convertToReceiverParameter()
                    initializer = propertyInitializer

                    val propertyAnnotations = mutableListOf()
                    for (annotationEntry in annotationEntries) {
                        propertyAnnotations += annotationEntry.convert()
                    }
                    if ([email protected]) {
                        isLocal = true
                        symbol = propertySymbol

                        extractTypeParametersTo(this, symbol)
                        backingField = [email protected](
                            this@toFirProperty,
                            propertySymbol = symbol,
                            propertyType,
                            emptyList(),
                        )

                        status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL).apply {
                            isLateInit = hasModifier(LATEINIT_KEYWORD)
                        }

                        if (hasDelegate()) {
                            fun extractDelegateExpression() =
                                [email protected]?.expression.toFirExpression("Incorrect delegate expression")

                            val delegateBuilder = FirWrappedDelegateExpressionBuilder().apply {
                                val delegateFirExpression = extractDelegateExpression()
                                source = delegateFirExpression.source?.fakeElement(KtFakeSourceElementKind.WrappedDelegate)
                                    ?: [email protected]?.toFirSourceElement(KtFakeSourceElementKind.WrappedDelegate)
                                expression = delegateFirExpression
                            }

                            generateAccessorsByDelegate(
                                delegateBuilder,
                                baseModuleData,
                                ownerRegularOrAnonymousObjectSymbol = null,
                                context = context,
                                isExtension = false,
                            )
                        }
                    } else {
                        isLocal = false
                        symbol = propertySymbol
                        dispatchReceiverType = currentDispatchReceiverType()
                        extractTypeParametersTo(this, symbol)
                        withCapturedTypeParameters(true, propertySource, this.typeParameters) {
                            backingField = [email protected](
                                this@toFirProperty,
                                propertySymbol = symbol,
                                propertyType,
                                propertyAnnotations.filter { it.useSiteTarget == FIELD || it.useSiteTarget == PROPERTY_DELEGATE_FIELD },
                            )

                            getter = [email protected](
                                this@toFirProperty,
                                propertyType,
                                propertySymbol = symbol,
                                isGetter = true,
                                accessorAnnotationsFromProperty = propertyAnnotations.filterUseSiteTarget(PROPERTY_GETTER),
                                parameterAnnotationsFromProperty = emptyList()
                            )

                            setter = [email protected](
                                this@toFirProperty,
                                propertyType,
                                propertySymbol = symbol,
                                isGetter = false,
                                accessorAnnotationsFromProperty = propertyAnnotations.filterUseSiteTarget(PROPERTY_SETTER),
                                parameterAnnotationsFromProperty = propertyAnnotations.filterUseSiteTarget(SETTER_PARAMETER)
                            )

                            status = FirDeclarationStatusImpl(getVisibility(), modality).apply {
                                isExpect = hasExpectModifier() || [email protected]
                                isActual = hasActualModifier()
                                isOverride = hasModifier(OVERRIDE_KEYWORD)
                                isConst = hasModifier(CONST_KEYWORD)
                                isLateInit = hasModifier(LATEINIT_KEYWORD)
                                isExternal = hasModifier(EXTERNAL_KEYWORD)
                            }

                            val psiPropertyDelegate = [email protected]
                            if (psiPropertyDelegate != null) {
                                fun extractDelegateExpression(): FirExpression {
                                    return buildOrLazyExpression(psiPropertyDelegate.expression?.toFirSourceElement(KtFakeSourceElementKind.WrappedDelegate)) {
                                        psiPropertyDelegate.expression?.toFirExpression("Should have delegate") ?: buildErrorExpression {
                                            diagnostic = ConeSimpleDiagnostic("Should have delegate", DiagnosticKind.ExpressionExpected)
                                        }
                                    }
                                }

                                val delegateBuilder = FirWrappedDelegateExpressionBuilder().apply {
                                    val delegateExpression = extractDelegateExpression()
                                    source = (psiPropertyDelegate.expression ?: psiPropertyDelegate)
                                        .toFirSourceElement(KtFakeSourceElementKind.WrappedDelegate)

                                    expression = delegateExpression
                                }

                                val (lazyDelegateExpression: FirLazyExpression?, lazyBody: FirLazyBlock?) = buildOrLazy(
                                    build = { null to null },
                                    lazy = { buildLazyExpression { source = delegateBuilder.source } to buildLazyBlock() },
                                )

                                generateAccessorsByDelegate(
                                    delegateBuilder,
                                    baseModuleData,
                                    ownerRegularOrAnonymousObjectSymbol,
                                    context,
                                    isExtension = receiverTypeReference != null,
                                    lazyDelegateExpression = lazyDelegateExpression,
                                    lazyBodyForGeneratedAccessors = lazyBody,
                                    ::bindFunctionTarget,
                                )
                            }
                        }
                    }
                    annotations += when {
                        isLocal -> propertyAnnotations
                        else -> propertyAnnotations.filterStandalonePropertyRelevantAnnotations(isVar)
                    }

                    contextReceivers.addAll(convertContextReceivers([email protected]))
                }.also {
                    if (!isLocal) {
                        fillDanglingConstraintsTo(it)
                    }
                }
            }
        }

        /**
         * Builds [FirAnonymousInitializer] from [KtAnonymousInitializer]
         *
         * @param initializer Source [KtAnonymousInitializer]
         * @param containingDeclarationSymbol containing declaration symbol, if any
         * @param allowLazyBody if `true`, [FirLazyBlock] is used in the IDE mode
         * @param isLocal if `true`, the initializer is not used as a containing declaration for the contents of the initializer
         */
        protected fun buildAnonymousInitializer(
            initializer: KtAnonymousInitializer,
            containingDeclarationSymbol: FirBasedSymbol<*>,
            allowLazyBody: Boolean = true,
            isLocal: Boolean = false,
        ): FirAnonymousInitializer = buildAnonymousInitializer {
            withContainerSymbol(symbol, isLocal) {
                source = initializer.toFirSourceElement()
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                body = if (allowLazyBody) {
                    buildOrLazyBlock {
                        withForcedLocalContext {
                            initializer.body.toFirBlock()
                        }
                    }
                } else {
                    withForcedLocalContext {
                        initializer.body.toFirBlock()
                    }
                }

                this.containingDeclarationSymbol = containingDeclarationSymbol
                initializer.extractAnnotationsTo(this)
            }
        }

        override fun visitProperty(property: KtProperty, data: FirElement?): FirElement {
            return property.toFirProperty(
                ownerRegularOrAnonymousObjectSymbol = null,
                context = context
            )
        }

        override fun visitTypeReference(typeReference: KtTypeReference, data: FirElement?): FirElement {
            return typeReference.toFirType()
        }

        private fun KtTypeReference.toFirType(): FirTypeRef {
            val typeElement = typeElement
            val source = toFirSourceElement()
            val isNullable = typeElement is KtNullableType

            // There can be KtDeclarationModifierLists in the KtTypeReference AND the descendant KtNullableTypes.
            // We aggregate them to get modifiers and annotations. Not only that, there could be multiple modifier lists on each. Examples:
            //
            //   `@A() (@B Int)`   -> Has 2 modifier lists (@A and @B) on KtTypeReference
            //   `(@A() (@B Int))? -> No modifier list on KtTypeReference, but 2 modifier lists (@A and @B) on child KtNullableType
            //   `@A() (@B Int)?   -> Has 1 modifier list (@A) on KtTypeReference, and 1 modifier list (@B) on child KtNullableType
            //   `@A (@B() (@C() (@Bar D)?)?)?` -> Has 1 modifier list (@A) on KtTypeReference and 1 modifier list on each of the
            //                                     3 descendant KtNullableTypes (@B, @C, @D)
            //
            // We need to examine all modifier lists for some cases:
            // 1. `@A Int?` and `(@A Int)?` are effectively the same, but in the latter, the modifier list is on the child KtNullableType
            // 2. `(suspend @A () -> Int)?` is a nullable suspend function type but the modifier list is on the child KtNullableType
            //
            // `getModifierList()` only returns the first one so we have to get all modifier list children.
            // TODO: Report MODIFIER_LIST_NOT_ALLOWED error when there are multiple modifier lists. How do we report on each of them?
            fun KtElementImplStub<*>.getAllModifierLists(): Array =
                getStubOrPsiChildren(KtStubElementTypes.MODIFIER_LIST, KtStubElementTypes.MODIFIER_LIST.arrayFactory)

            val allModifierLists = mutableListOf(*getAllModifierLists())

            fun KtTypeElement?.unwrapNullable(): KtTypeElement? =
                when (this) {
                    is KtNullableType -> {
                        allModifierLists += getAllModifierLists()
                        this.innerType.unwrapNullable()
                    }
                    else -> this
                }

            val firTypeBuilder = when (val unwrappedElement = typeElement.unwrapNullable()) {
                is KtDynamicType -> FirDynamicTypeRefBuilder().apply {
                    this.source = source
                    isMarkedNullable = isNullable
                }
                is KtUserType -> {
                    val referenceExpression = unwrappedElement.referenceExpression
                    if (referenceExpression != null) {
                        convertKtTypeElement(source, isNullable, unwrappedElement, referenceExpression)
                    } else {
                        FirErrorTypeRefBuilder().apply {
                            this.source = source
                            diagnostic = ConeSyntaxDiagnostic("Incomplete user type")

                            val qualifier = unwrappedElement.qualifier
                            val reference = qualifier?.referenceExpression
                            if (qualifier != null && reference != null) {
                                partiallyResolvedTypeRef = convertKtTypeElement(
                                    qualifier.toFirSourceElement(),
                                    isNullable = false,
                                    qualifier,
                                    reference
                                ).build()
                            }
                        }
                    }
                }
                is KtFunctionType -> {
                    FirFunctionTypeRefBuilder().apply {
                        this.source = source
                        isMarkedNullable = isNullable
                        isSuspend = allModifierLists.any { it.hasSuspendModifier() }
                        receiverTypeRef = unwrappedElement.receiverTypeReference?.toFirType()
                        // TODO: probably implicit type should not be here
                        returnTypeRef = unwrappedElement.returnTypeReference.toFirOrErrorType()
                        for (valueParameter in unwrappedElement.parameters) {
                            parameters += buildFunctionTypeParameter {
                                val parameterSource = valueParameter.toFirSourceElement()
                                this.source = parameterSource
                                name = valueParameter.nameAsName
                                returnTypeRef = when {
                                    valueParameter.typeReference != null -> valueParameter.typeReference.toFirOrErrorType()
                                    else -> createNoTypeForParameterTypeRef(parameterSource)
                                }
                            }
                        }

                        contextReceiverTypeRefs.addAll(
                            unwrappedElement.contextReceiversTypeReferences.mapNotNull {
                                it.toFirType()
                            }
                        )
                    }
                }
                is KtIntersectionType -> FirIntersectionTypeRefBuilder().apply {
                    this.source = source
                    isMarkedNullable = isNullable
                    leftType = unwrappedElement.getLeftTypeRef().toFirOrErrorType()
                    rightType = unwrappedElement.getRightTypeRef().toFirOrErrorType()
                }
                null -> FirErrorTypeRefBuilder().apply {
                    this.source = source
                    diagnostic = ConeSyntaxDiagnostic("Incomplete code")
                }
                else -> errorWithAttachment("Unexpected type element: ${unwrappedElement::class.simpleName}") {
                    withPsiEntry("unwrappedElement", unwrappedElement)
                }
            }

            for (modifierList in allModifierLists) {
                for (annotationEntry in modifierList.annotationEntries) {
                    firTypeBuilder.annotations += annotationEntry.convert()
                }
            }

            return firTypeBuilder.build() as FirTypeRef
        }

        private fun convertKtTypeElement(
            source: KtPsiSourceElement,
            isNullable: Boolean,
            ktUserType: KtUserType,
            reference: KtSimpleNameExpression,
        ): FirUserTypeRefBuilder {
            var referenceExpression: KtSimpleNameExpression? = reference
            return FirUserTypeRefBuilder().apply {
                this.source = source
                isMarkedNullable = isNullable
                var ktQualifier: KtUserType? = ktUserType

                do {
                    val firQualifier = FirQualifierPartImpl(
                        referenceExpression!!.toFirSourceElement(),
                        referenceExpression!!.getReferencedNameAsName(),
                        FirTypeArgumentListImpl(ktQualifier?.typeArgumentList?.toKtPsiSourceElement() ?: source).apply {
                            typeArguments.appendTypeArguments(ktQualifier!!.typeArguments)
                        }
                    )
                    qualifier.add(firQualifier)

                    ktQualifier = ktQualifier!!.qualifier
                    referenceExpression = ktQualifier?.referenceExpression
                } while (referenceExpression != null)

                qualifier.reverse()
            }
        }

        override fun visitAnnotationEntry(annotationEntry: KtAnnotationEntry, data: FirElement?): FirElement {
            return buildAnnotationCall {
                source = annotationEntry.toFirSourceElement()
                useSiteTarget = annotationEntry.useSiteTarget?.getAnnotationUseSiteTarget()
                annotationTypeRef = annotationEntry.typeReference.toFirOrErrorType()
                annotationEntry.extractArgumentsTo(this)
                val name = (annotationTypeRef as? FirUserTypeRef)?.qualifier?.last()?.name ?: Name.special("")
                calleeReference = buildSimpleNamedReference {
                    source = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referenceExpression?.toFirSourceElement()
                    this.name = name
                }
                typeArguments.appendTypeArguments(annotationEntry.typeArguments)
                containingDeclarationSymbol = context.containerSymbol
            }
        }

        override fun visitTypeProjection(typeProjection: KtTypeProjection, data: FirElement?): FirElement {
            val projectionKind = typeProjection.projectionKind
            val projectionSource = typeProjection.toFirSourceElement()
            if (projectionKind == KtProjectionKind.STAR) {
                return buildStarProjection {
                    source = projectionSource
                }
            }
            val argumentList = typeProjection.parent as? KtTypeArgumentList
            val typeReference = typeProjection.typeReference
            if (argumentList?.parent is KtCallExpression && typeReference?.isPlaceholder == true) {
                return buildPlaceholderProjection {
                    source = projectionSource
                }
            }
            val firType = typeReference.toFirOrErrorType()
            return buildTypeProjectionWithVariance {
                source = projectionSource
                typeRef = firType
                variance = when (projectionKind) {
                    KtProjectionKind.IN -> Variance.IN_VARIANCE
                    KtProjectionKind.OUT -> Variance.OUT_VARIANCE
                    KtProjectionKind.NONE -> Variance.INVARIANT
                    KtProjectionKind.STAR -> shouldNotBeCalled()
                }
            }
        }

        override fun visitBlockExpression(expression: KtBlockExpression, data: FirElement?): FirElement {
            return configureBlockWithoutBuilding(expression).build()
        }

        private fun configureBlockWithoutBuilding(expression: KtBlockExpression, kind: KtFakeSourceElementKind? = null): FirBlockBuilder {
            return FirBlockBuilder().apply {
                source = expression.toFirSourceElement(kind)
                for (statement in expression.statements) {
                    val firStatement = statement.toFirStatement { "Statement expected: ${statement.text}" }
                    val isForLoopBlock =
                        firStatement is FirBlock && firStatement.source?.kind == KtFakeSourceElementKind.DesugaredForLoop
                    if (firStatement !is FirBlock || isForLoopBlock || firStatement.annotations.isNotEmpty()) {
                        statements += firStatement
                    } else {
                        statements += firStatement.statements
                    }
                }
            }
        }

        override fun visitSimpleNameExpression(expression: KtSimpleNameExpression, data: FirElement?): FirElement {
            val qualifiedSource = when {
                expression.getQualifiedExpressionForSelector() != null -> expression.parent
                else -> expression
            }.toFirSourceElement()

            val expressionSource = expression.toFirSourceElement()
            var diagnostic: ConeDiagnostic? = null
            val rawText = expression.getReferencedNameElement().node.text
            if (rawText.isUnderscore) {
                diagnostic = ConeUnderscoreUsageWithoutBackticks(expressionSource)
            }

            return generateAccessExpression(
                qualifiedSource,
                expressionSource,
                expression.getReferencedNameAsName(),
                diagnostic
            )
        }

        override fun visitConstantExpression(expression: KtConstantExpression, data: FirElement?): FirElement =
            generateConstantExpressionByLiteral(expression)

        override fun visitStringTemplateExpression(expression: KtStringTemplateExpression, data: FirElement?): FirElement {
            return expression.entries.toInterpolatingCall(
                expression,
                getElementType = { element ->
                    when (element) {
                        is KtLiteralStringTemplateEntry -> KtNodeTypes.LITERAL_STRING_TEMPLATE_ENTRY
                        is KtEscapeStringTemplateEntry -> KtNodeTypes.ESCAPE_STRING_TEMPLATE_ENTRY
                        is KtSimpleNameStringTemplateEntry -> KtNodeTypes.SHORT_STRING_TEMPLATE_ENTRY
                        is KtBlockStringTemplateEntry -> KtNodeTypes.LONG_STRING_TEMPLATE_ENTRY
                        else -> errorWithAttachment("invalid node type ${element::class}") {
                            withPsiEntry("element", element)
                        }
                    }
                },
                convertTemplateEntry = { errorReason ->
                    (this as KtStringTemplateEntryWithExpression).getChildrenOfType().map { it.toFirExpression(errorReason) }
                },
                prefix = { expression.interpolationPrefix?.text ?: "" },
            )
        }

        override fun visitReturnExpression(expression: KtReturnExpression, data: FirElement?): FirElement {
            val source = expression.toFirSourceElement(KtFakeSourceElementKind.ImplicitUnit.Return)
            val result = expression.returnedExpression?.toFirExpression("Incorrect return expression")
                ?: buildUnitExpression { this.source = source }
            return result.toReturn(source, expression.getTargetLabel()?.getReferencedName(), fromKtReturnExpression = true)
        }

        override fun visitTryExpression(expression: KtTryExpression, data: FirElement?): FirElement {
            return buildTryExpression {
                source = expression.toFirSourceElement()
                tryBlock = expression.tryBlock.toFirBlock()
                finallyBlock = expression.finallyBlock?.finalExpression?.toFirBlock()
                for (clause in expression.catchClauses) {
                    val parameter = clause.catchParameter?.let { ktParameter ->
                        val name = convertValueParameterName(
                            ktParameter.nameAsSafeName,
                            ValueParameterDeclaration.CATCH
                        ) { ktParameter.nameIdentifier?.node?.text }

                        buildProperty {
                            val parameterSource = ktParameter.toFirSourceElement()
                            source = parameterSource
                            moduleData = baseModuleData
                            origin = FirDeclarationOrigin.Source
                            returnTypeRef = when {
                                ktParameter.typeReference != null -> ktParameter.typeReference.toFirOrErrorType()
                                else -> createNoTypeForParameterTypeRef(parameterSource)
                            }
                            isVar = false
                            status = FirResolvedDeclarationStatusImpl(Visibilities.Local, Modality.FINAL, EffectiveVisibility.Local)
                            isLocal = true
                            this.name = name
                            symbol = FirPropertySymbol(CallableId(name))
                            for (annotationEntry in ktParameter.annotationEntries) {
                                this.annotations += annotationEntry.convert()
                            }
                        }.also {
                            it.isCatchParameter = true
                        }
                    } ?: continue
                    catches += buildCatch {
                        source = clause.toFirSourceElement()
                        this.parameter = parameter
                        block = clause.catchBody.toFirBlock()
                    }
                }
            }
        }

        override fun visitIfExpression(expression: KtIfExpression, data: FirElement?): FirElement {
            return buildWhenExpression {
                source = expression.toFirSourceElement()

                val ktCondition = expression.condition
                branches += buildRegularWhenBranch {
                    source = ktCondition?.toFirSourceElement(KtFakeSourceElementKind.WhenCondition)
                    condition = ktCondition.toFirExpression("If statement should have condition")
                    result = expression.then.toFirBlock()
                }

                if (expression.`else` != null) {
                    branches += buildRegularWhenBranch {
                        source = expression.elseKeyword?.toKtPsiSourceElement()
                        condition = buildElseIfTrueCondition()
                        result = expression.`else`.toFirBlock()
                    }
                }

                usedAsExpression = expression.usedAsExpression
            }
        }

        override fun visitWhenExpression(expression: KtWhenExpression, data: FirElement?): FirElement {
            val ktSubjectExpression = expression.subjectExpression
            val subjectExpression = when (ktSubjectExpression) {
                is KtVariableDeclaration -> ktSubjectExpression.initializer
                else -> ktSubjectExpression
            }?.toFirExpression("Incorrect when subject expression: ${ktSubjectExpression?.text}")
            val subjectVariable = when (ktSubjectExpression) {
                is KtVariableDeclaration -> {
                    val name = ktSubjectExpression.nameAsSafeName
                    buildProperty {
                        source = ktSubjectExpression.toFirSourceElement()
                        moduleData = baseModuleData
                        origin = FirDeclarationOrigin.Source
                        returnTypeRef = ktSubjectExpression.typeReference.toFirOrImplicitType()
                        this.name = name
                        initializer = subjectExpression
                        delegate = null
                        isVar = false
                        symbol = FirPropertySymbol(name)
                        isLocal = true
                        status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL)
                        ktSubjectExpression.extractAnnotationsTo(this)
                    }
                }
                else -> null
            }
            val hasSubject = subjectExpression != null

            @OptIn(FirContractViolation::class)
            val ref = FirExpressionRef()
            var shouldBind = hasSubject
            return buildWhenExpression {
                source = expression.toFirSourceElement()
                this.subject = subjectExpression
                this.subjectVariable = subjectVariable
                usedAsExpression = expression.usedAsExpression

                for (entry in expression.entries) {
                    val entrySource = entry.toFirSourceElement()
                    val entryGuard = entry.guard?.let { it.getExpression().toFirExpression("No expression in guard") }
                    val branchBody = entry.expression.toFirBlock()
                    branches += if (entry.elseKeyword == null) {
                        if (hasSubject) {
                            buildWhenBranch(hasGuard = entryGuard != null) {
                                source = entrySource
                                condition = entry.conditions.toFirWhenCondition(
                                    ref,
                                    { toFirExpression(it) },
                                    { toFirOrErrorType() },
                                ).guardedBy(entryGuard)
                                result = branchBody
                            }
                        } else {
                            val ktCondition = entry.conditions.first()
                            buildWhenBranch(hasGuard = entryGuard != null) {
                                source = entrySource
                                condition =
                                    if (entry.conditions.size == 1 && ktCondition is KtWhenConditionWithExpression) {
                                        (ktCondition.expression ?: ktCondition).toFirExpression(
                                            "No expression in condition with expression",
                                            DiagnosticKind.ExpressionExpected,
                                        )
                                    } else {
                                        buildBalancedOrExpressionTree(entry.conditions.map { condition ->
                                            if (condition is KtWhenConditionWithExpression) {
                                                condition.expression.toFirExpression(
                                                    "No expression in condition with expression",
                                                    DiagnosticKind.ExpressionExpected
                                                )
                                            } else {
                                                shouldBind = true
                                                buildErrorExpression {
                                                    source = condition.toFirSourceElement()
                                                    nonExpressionElement = condition.toFirWhenCondition(
                                                        ref,
                                                        { toFirExpression(it) },
                                                        { toFirOrErrorType() },
                                                    )
                                                    diagnostic = ConeSimpleDiagnostic(
                                                        "No expression in condition with expression",
                                                        DiagnosticKind.ExpressionExpected,
                                                    )
                                                }
                                            }
                                        })
                                    }.guardedBy(entryGuard)
                                result = branchBody
                            }
                        }
                    } else {
                        buildWhenBranch(hasGuard = entryGuard != null) {
                            source = entrySource
                            condition = entryGuard ?: buildElseIfTrueCondition()
                            result = branchBody
                        }
                    }
                }
            }.also {
                if (shouldBind) {
                    ref.bind(it)
                }
            }
        }

        private val KtExpression.usedAsExpression: Boolean
            get() {
                var parent = parent
                while (parent.elementType == KtNodeTypes.ANNOTATED_EXPRESSION ||
                    parent.elementType == KtNodeTypes.LABELED_EXPRESSION
                ) {
                    parent = parent.parent
                }

                when (parent.elementType) {
                    KtNodeTypes.THEN, KtNodeTypes.ELSE, KtNodeTypes.WHEN_ENTRY -> {
                        return (parent.parent as? KtExpression)?.usedAsExpression != false
                    }
                }

                fun PsiElement.getLastChildExpression() = children.asList().asReversed().firstIsInstanceOrNull()

                return when (parent) {
//                  todo KT-62472 Replace with the following when K2 is used in TeamCity
//                    is KtBlockExpression, is KtTryExpression -> parent.getLastChildExpression() == this && parent.usedAsExpression
                    is KtBlockExpression -> parent.getLastChildExpression() == this && parent.usedAsExpression
                    is KtTryExpression -> parent.getLastChildExpression() == this && parent.usedAsExpression
                    is KtCatchClause -> (parent.parent as? KtTryExpression)?.usedAsExpression == true
                    is KtClassInitializer, is KtScriptInitializer, is KtSecondaryConstructor, is KtFunctionLiteral, is KtFinallySection -> false
                    is KtDotQualifiedExpression -> parent.firstChild == this
//                  todo KT-62472 Replace with the following when K2 is used in TeamCity
//                    is KtFunction, is KtPropertyAccessor -> parent.hasBody() && !parent.hasBlockBody()
                    is KtFunction -> parent.hasBody() && !parent.hasBlockBody()
                    is KtPropertyAccessor -> parent.hasBody() && !parent.hasBlockBody()
                    is KtContainerNodeForControlStructureBody -> when (parent.parent.elementType) {
                        KtNodeTypes.FOR, KtNodeTypes.WHILE, KtNodeTypes.DO_WHILE -> false
                        else -> true
                    }
                    else -> true
                }
            }

        override fun visitDoWhileExpression(expression: KtDoWhileExpression, data: FirElement?): FirElement {
            val target: FirLoopTarget
            return FirDoWhileLoopBuilder().apply {
                source = expression.toFirSourceElement()
                // For break/continue in the do-while loop condition, prepare the loop target first so that it can refer to the same loop.
                target = prepareTarget(expression)
                condition = expression.condition.toFirExpression("No condition in do-while loop")
            }.configure(target) { expression.body.toFirBlock() }
        }

        override fun visitWhileExpression(expression: KtWhileExpression, data: FirElement?): FirElement {
            val target: FirLoopTarget
            return FirWhileLoopBuilder().apply {
                source = expression.toFirSourceElement()
                condition = expression.condition.toFirExpression("No condition in while loop")
                // break/continue in the while loop condition will refer to an outer loop if any.
                // So, prepare the loop target after building the condition.
                target = prepareTarget(expression)
            }.configure(target) { expression.body.toFirBlock() }
        }

        override fun visitForExpression(expression: KtForExpression, data: FirElement?): FirElement {
            val rangeExpression = expression.loopRange.toFirExpression("No range in for loop")
            val ktParameter = expression.loopParameter
            val fakeSource = expression.toKtPsiSourceElement(KtFakeSourceElementKind.DesugaredForLoop)
            val rangeSource = expression.loopRange?.toFirSourceElement(KtFakeSourceElementKind.DesugaredForLoop) ?: fakeSource

            val target: FirLoopTarget
            // NB: FirForLoopChecker relies on this block existence and structure
            return buildBlock {
                source = fakeSource
                val iteratorVal = generateTemporaryVariable(
                    baseModuleData, rangeSource, SpecialNames.ITERATOR,
                    buildFunctionCall {
                        source = rangeSource
                        calleeReference = buildSimpleNamedReference {
                            source = rangeSource
                            name = OperatorNameConventions.ITERATOR
                        }
                        explicitReceiver = rangeExpression
                        origin = FirFunctionCallOrigin.Operator
                    },
                )
                statements += iteratorVal
                statements += FirWhileLoopBuilder().apply {
                    source = expression.toFirSourceElement()
                    condition = buildFunctionCall {
                        source = rangeSource
                        calleeReference = buildSimpleNamedReference {
                            source = rangeSource
                            name = OperatorNameConventions.HAS_NEXT
                        }
                        explicitReceiver = generateResolvedAccessExpression(rangeSource, iteratorVal)
                        origin = FirFunctionCallOrigin.Operator
                    }
                    // break/continue in the for loop condition will refer to an outer loop if any.
                    // So, prepare the loop target after building the condition.
                    target = prepareTarget(expression)
                }.configure(target) {
                    val blockBuilder = FirBlockBuilder().apply {
                        source = expression.toFirSourceElement()
                    }
                    if (ktParameter != null) {
                        val multiDeclaration = ktParameter.destructuringDeclaration
                        val firLoopParameter = generateTemporaryVariable(
                            moduleData = baseModuleData,
                            source = ktParameter.toFirSourceElement(),
                            name = if (multiDeclaration != null) SpecialNames.DESTRUCT else ktParameter.nameAsSafeName,
                            initializer = buildFunctionCall {
                                source = rangeSource
                                calleeReference = buildSimpleNamedReference {
                                    source = rangeSource
                                    name = OperatorNameConventions.NEXT
                                }
                                explicitReceiver = generateResolvedAccessExpression(rangeSource, iteratorVal)
                                origin = FirFunctionCallOrigin.Operator
                            },
                            typeRef = ktParameter.typeReference.toFirOrImplicitType(),
                            extractedAnnotations = ktParameter.modifierList?.annotationEntries?.map { it.convert() },
                        )
                        if (multiDeclaration != null) {
                            addDestructuringVariables(
                                blockBuilder.statements,
                                this@Visitor,
                                baseModuleData,
                                multiDeclaration = multiDeclaration,
                                container = firLoopParameter,
                                tmpVariable = true,
                                forceLocal = true,
                            )
                        } else {
                            blockBuilder.statements.add(firLoopParameter)
                        }
                    }
                    blockBuilder.statements.add(expression.body.toFirBlock())
                    blockBuilder.build()
                }
            }
        }

        override fun visitBreakExpression(expression: KtBreakExpression, data: FirElement?): FirElement {
            return FirBreakExpressionBuilder().apply {
                source = expression.toFirSourceElement()
            }.bindLabel(expression).build()
        }

        override fun visitContinueExpression(expression: KtContinueExpression, data: FirElement?): FirElement {
            return FirContinueExpressionBuilder().apply {
                source = expression.toFirSourceElement()
            }.bindLabel(expression).build()
        }

        override fun visitBinaryExpression(expression: KtBinaryExpression, data: FirElement?): FirElement {
            val operationToken = expression.operationToken

            if (operationToken == IDENTIFIER) {
                context.calleeNamesForLambda += expression.operationReference.getReferencedNameAsName()
            } else {
                context.calleeNamesForLambda += null
            }

            val leftArgument = expression.left.toFirExpression("No left operand")
            val rightArgument = expression.right.toFirExpression("No right operand")

            // No need for the callee name since arguments are already generated
            context.calleeNamesForLambda.removeLast()

            val source = expression.toFirSourceElement()

            when (operationToken) {
                ELVIS ->
                    return leftArgument.generateNotNullOrOther(rightArgument, source)
                ANDAND, OROR ->
                    return leftArgument.generateLazyLogicalOperation(rightArgument, operationToken == ANDAND, source)
                in OperatorConventions.IN_OPERATIONS ->
                    return rightArgument.generateContainsOperation(
                        leftArgument, operationToken == NOT_IN, source,
                        expression.operationReference.toFirSourceElement(),
                    )
                in OperatorConventions.COMPARISON_OPERATIONS ->
                    return leftArgument.generateComparisonExpression(
                        rightArgument, operationToken, source,
                        expression.operationReference.toFirSourceElement(),
                    )
            }
            val conventionCallName = operationToken.toBinaryName()
            return if (conventionCallName != null || operationToken == IDENTIFIER) {
                buildFunctionCall {
                    this.source = source
                    calleeReference = buildSimpleNamedReference {
                        this.source = expression.operationReference.toFirSourceElement()
                        name = conventionCallName ?: expression.operationReference.getReferencedNameAsName()
                    }
                    explicitReceiver = leftArgument
                    argumentList = buildUnaryArgumentList(rightArgument)
                    origin = if (conventionCallName != null) FirFunctionCallOrigin.Operator else FirFunctionCallOrigin.Infix
                }
            } else {
                val firOperation = operationToken.toFirOperation()
                if (firOperation in FirOperation.ASSIGNMENTS) {
                    return expression.left.generateAssignment(
                        source,
                        expression.left?.toFirSourceElement(),
                        rightArgument,
                        firOperation,
                        leftArgument.annotations,
                        expression.right,
                    ) {
                        (this as KtExpression).toFirExpression(
                            sourceWhenInvalidExpression = expression,
                            isValidExpression = { !it.isStatementLikeExpression || it.isArraySet },
                        ) {
                            ConeSimpleDiagnostic(
                                "Incorrect expression in assignment: ${expression.text}",
                                DiagnosticKind.ExpressionExpected
                            )
                        }
                    }
                } else {
                    buildEqualityOperatorCall {
                        this.source = source
                        operation = firOperation
                        argumentList = buildBinaryArgumentList(leftArgument, rightArgument)
                    }
                }
            }
        }

        override fun visitBinaryWithTypeRHSExpression(expression: KtBinaryExpressionWithTypeRHS, data: FirElement?): FirElement {
            return buildTypeOperatorCall {
                source = expression.toFirSourceElement()
                operation = expression.operationReference.getReferencedNameElementType().toFirOperation()
                conversionTypeRef = expression.right.toFirOrErrorType()
                argumentList = buildUnaryArgumentList(expression.left.toFirExpression("No left operand"))
            }
        }

        override fun visitIsExpression(expression: KtIsExpression, data: FirElement?): FirElement {
            return buildTypeOperatorCall {
                source = expression.toFirSourceElement()
                operation = if (expression.isNegated) FirOperation.NOT_IS else FirOperation.IS
                conversionTypeRef = expression.typeReference.toFirOrErrorType()
                argumentList = buildUnaryArgumentList(expression.leftHandSide.toFirExpression("No left operand"))
            }
        }

        override fun visitUnaryExpression(expression: KtUnaryExpression, data: FirElement?): FirElement {
            val operationToken = expression.operationToken
            val argument = expression.baseExpression
            val conventionCallName = operationToken.toUnaryName()
            return when {
                operationToken == EXCLEXCL -> {
                    buildCheckNotNullCall {
                        source = expression.toFirSourceElement()
                        argumentList = buildUnaryArgumentList(argument.toFirExpression("No operand"))
                    }
                }
                conventionCallName != null -> {
                    if (operationToken in OperatorConventions.INCREMENT_OPERATIONS) {
                        return generateIncrementOrDecrementBlock(
                            expression, expression.operationReference, argument,
                            callName = conventionCallName,
                            prefix = expression is KtPrefixExpression,
                        ) { (this as KtExpression).toFirExpression("Incorrect expression inside inc/dec") }
                    }

                    val receiver = argument.toFirExpression("No operand")

                    convertUnaryPlusMinusCallOnIntegerLiteralIfNecessary(expression, receiver, operationToken)?.let { return it }

                    buildFunctionCall {
                        source = expression.toFirSourceElement()
                        calleeReference = buildSimpleNamedReference {
                            source = expression.operationReference.toFirSourceElement()
                            name = conventionCallName
                        }
                        explicitReceiver = receiver
                        origin = FirFunctionCallOrigin.Operator
                    }
                }
                else -> throw IllegalStateException("Unexpected expression: ${expression.text}")
            }
        }

        private fun splitToCalleeAndReceiver(
            calleeExpression: KtExpression?,
            defaultSource: KtPsiSourceElement,
        ): CalleeAndReceiver {
            val parenthesizedArgument = (calleeExpression as? KtParenthesizedExpression)?.expression
                ?.toFirExpression("Incorrect invoke receiver")

            return when {
                calleeExpression is KtSimpleNameExpression ->
                    CalleeAndReceiver(
                        buildSimpleNamedReference {
                            source = calleeExpression.toFirSourceElement()
                            name = calleeExpression.getReferencedNameAsName()
                        }
                    )

                calleeExpression is KtSuperExpression || (parenthesizedArgument as? FirResolvable)?.calleeReference is FirSuperReference -> {
                    CalleeAndReceiver(
                        buildErrorNamedReference {
                            source = (calleeExpression as? KtSuperExpression)?.toFirSourceElement()
                                ?: (parenthesizedArgument as? FirResolvable)?.calleeReference?.source
                            diagnostic = ConeSimpleDiagnostic("Super cannot be a callee", DiagnosticKind.SuperNotAllowed)
                        }
                    )
                }

                parenthesizedArgument != null -> {
                    CalleeAndReceiver(
                        buildSimpleNamedReference {
                            source = defaultSource.fakeElement(KtFakeSourceElementKind.ImplicitInvokeCall)
                            name = OperatorNameConventions.INVOKE
                        },
                        receiverForInvoke = parenthesizedArgument,
                    )
                }

                calleeExpression == null -> {
                    CalleeAndReceiver(
                        buildErrorNamedReference {
                            source = defaultSource
                            diagnostic = ConeSyntaxDiagnostic("Call has no callee")
                        }
                    )
                }

                else -> {
                    CalleeAndReceiver(
                        buildSimpleNamedReference {
                            source = defaultSource.fakeElement(KtFakeSourceElementKind.ImplicitInvokeCall)
                            name = OperatorNameConventions.INVOKE
                        },
                        receiverForInvoke = calleeExpression.toFirExpression("Incorrect invoke receiver"),
                    )
                }
            }
        }

        // In non-erroneous code, it's either `f()` (without explicit receiver) or `(expr)()` which is transformed to `expr.invoke()`
        override fun visitCallExpression(expression: KtCallExpression, data: FirElement?): FirElement {
            val source = expression.toFirSourceElement()
            val (calleeReference, receiverForInvoke) = splitToCalleeAndReceiver(expression.calleeExpression, source)

            val result: FirQualifiedAccessExpressionBuilder =
                if (expression.valueArgumentList == null && expression.lambdaArguments.isEmpty()) {
                    FirPropertyAccessExpressionBuilder().apply {
                        this.source = source
                        this.calleeReference = calleeReference
                    }
                } else {
                    val builder = if (receiverForInvoke != null) FirImplicitInvokeCallBuilder() else FirFunctionCallBuilder()
                    builder.apply {
                        this.source = source
                        this.calleeReference = calleeReference
                        context.calleeNamesForLambda += calleeReference.name
                        expression.extractArgumentsTo(this)
                        context.calleeNamesForLambda.removeLast()
                    }
                }

            return result.apply {
                this.explicitReceiver = receiverForInvoke
                typeArguments.appendTypeArguments(expression.typeArguments)
            }.build().pullUpSafeCallIfNecessary()
        }

        override fun visitArrayAccessExpression(expression: KtArrayAccessExpression, data: FirElement?): FirElement {
            val arrayExpression = expression.arrayExpression
            val setArgument = context.arraySetArgument.remove(expression)
            return buildFunctionCall {
                val isGet = setArgument == null
                source = (if (isGet) expression else expression.parent).toFirSourceElement()
                calleeReference = buildSimpleNamedReference {
                    source = expression.toFirSourceElement().fakeElement(KtFakeSourceElementKind.ArrayAccessNameReference)
                    name = if (isGet) OperatorNameConventions.GET else OperatorNameConventions.SET
                }
                explicitReceiver = arrayExpression.toFirExpression("No array expression")
                argumentList = buildArgumentList {
                    for (indexExpression in expression.indexExpressions) {
                        arguments += indexExpression.toFirExpression("Incorrect index expression")
                    }
                    if (setArgument != null) {
                        arguments += setArgument
                    }
                }
                origin = FirFunctionCallOrigin.Operator
            }.pullUpSafeCallIfNecessary()
        }

        override fun visitQualifiedExpression(expression: KtQualifiedExpression, data: FirElement?): FirElement {
            val receiver = expression.receiverExpression.toFirExpression("Incorrect receiver expression")

            val selector = expression.selectorExpression
                ?: return buildErrorExpression {
                    source = expression.toFirSourceElement()
                    diagnostic = ConeSyntaxDiagnostic("Qualified expression without selector")

                    // if there is no selector, we still want to resolve the receiver
                    this.expression = receiver
                }

            val firSelector = selector.toFirExpression("Incorrect selector expression")
            if (firSelector is FirQualifiedAccessExpression) {
                if (expression is KtSafeQualifiedExpression) {
                    @OptIn(FirImplementationDetail::class)
                    firSelector.replaceSource(expression.toFirSourceElement(KtFakeSourceElementKind.DesugaredSafeCallExpression))
                    return firSelector.createSafeCall(
                        receiver,
                        expression.toFirSourceElement()
                    )
                }

                return convertFirSelector(firSelector, expression.toFirSourceElement(), receiver)
            }
            if (firSelector is FirErrorExpression) {
                return buildQualifiedErrorAccessExpression {
                    this.receiver = receiver
                    this.selector = firSelector
                    source = expression.toFirSourceElement()
                    diagnostic = ConeSyntaxDiagnostic("Qualified expression with unexpected selector")
                }
            }
            return firSelector
        }

        override fun visitThisExpression(expression: KtThisExpression, data: FirElement?): FirElement {
            return buildThisReceiverExpression {
                val sourceElement = expression.toFirSourceElement()
                source = sourceElement
                calleeReference = buildExplicitThisReference {
                    source = sourceElement.fakeElement(KtFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
                    labelName = expression.getLabelName()
                }
            }
        }

        override fun visitSuperExpression(expression: KtSuperExpression, data: FirElement?): FirElement {
            val superType = expression.superTypeQualifier
            val theSource = expression.toFirSourceElement()
            return buildPropertyAccessExpression {
                this.source = theSource
                calleeReference = buildExplicitSuperReference {
                    source = theSource.fakeElement(KtFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
                    labelName = expression.getLabelName()
                    superTypeRef = superType.toFirOrImplicitType()
                }
            }
        }

        override fun visitParenthesizedExpression(expression: KtParenthesizedExpression, data: FirElement?): FirElement {
            context.forwardLabelUsagePermission(expression, expression.expression)
            return expression.expression.toFirExpression("Empty parentheses")
        }

        override fun visitLabeledExpression(expression: KtLabeledExpression, data: FirElement?): FirElement {
            val label = expression.getTargetLabel()
            var labelSource: KtSourceElement? = null
            var forbiddenLabelKind: ForbiddenLabelKind? = null

            val isRepetitiveLabel = expression.baseExpression is KtLabeledExpression

            val result = if (label != null) {
                val name = label.getReferencedNameElement().node!!.text
                forbiddenLabelKind = getForbiddenLabelKind(name, isRepetitiveLabel)
                labelSource = label.toKtPsiSourceElement()
                val firLabel = buildLabel(name, labelSource)
                context.withNewLabel(firLabel, expression.baseExpression) {
                    expression.baseExpression?.accept(this, data)
                }
            } else {
                expression.baseExpression?.accept(this, data)
            }

            return buildExpressionHandlingErrors(result, expression.toFirSourceElement(), forbiddenLabelKind, labelSource)
        }

        override fun visitAnnotatedExpression(expression: KtAnnotatedExpression, data: FirElement?): FirElement {
            val baseExpression = expression.baseExpression
            context.forwardLabelUsagePermission(expression, baseExpression)
            val rawResult = baseExpression?.accept(this, data)
            val result = rawResult as? FirAnnotationContainer
                ?: buildErrorExpression(
                    expression.toFirSourceElement(),
                    ConeNotAnnotationContainer(rawResult?.render() ?: "???")
                )
            expression.extractAnnotationsTo(result)
            return result
        }

        override fun visitThrowExpression(expression: KtThrowExpression, data: FirElement?): FirElement {
            return buildThrowExpression {
                source = expression.toFirSourceElement()
                exception = expression.thrownExpression.toFirExpression("Nothing to throw")
            }
        }

        override fun visitDestructuringDeclaration(multiDeclaration: KtDestructuringDeclaration, data: FirElement?): FirElement {
            val baseVariable = generateTemporaryVariable(
                baseModuleData,
                multiDeclaration.toFirSourceElement(),
                "destruct",
                multiDeclaration.initializer.toFirExpression { ConeSyntaxDiagnostic("Initializer required for destructuring declaration") },
                extractAnnotationsTo = { extractAnnotationsTo(it) }
            )
            return generateDestructuringBlock(
                this@Visitor,
                baseModuleData,
                multiDeclaration,
                baseVariable,
                tmpVariable = true,
            )
        }

        override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, data: FirElement?): FirElement {
            return buildGetClassCall {
                source = expression.toFirSourceElement()
                argumentList = buildUnaryArgumentList(
                    expression.receiverExpression.toFirExpression(sourceWhenThisIsNull = expression) {
                        ConeUnsupportedClassLiteralsWithEmptyLhs
                    }
                )
            }
        }

        override fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression, data: FirElement?): FirElement {
            return buildCallableReferenceAccess {
                source = expression.toFirSourceElement()
                calleeReference = buildSimpleNamedReference {
                    source = expression.callableReference.toFirSourceElement()
                    name = expression.callableReference.getReferencedNameAsName()
                }
                explicitReceiver = expression.receiverExpression?.toFirExpression("Incorrect receiver expression")
                hasQuestionMarkAtLHS = expression.hasQuestionMarks
            }
        }

        override fun visitCollectionLiteralExpression(expression: KtCollectionLiteralExpression, data: FirElement?): FirElement {
            return buildArrayLiteral {
                source = expression.toFirSourceElement()
                argumentList = buildArgumentList {
                    for (innerExpression in expression.getInnerExpressions()) {
                        arguments += innerExpression.toFirExpression("Incorrect collection literal argument")
                    }
                }
            }
        }

        override fun visitExpression(expression: KtExpression, data: FirElement?): FirElement {
            return buildExpressionStub {
                source = expression.toFirSourceElement()
            }
        }

        private fun MutableList.appendTypeArguments(args: List) {
            for (typeArgument in args) {
                this += typeArgument.convert()
            }
        }

        private fun buildErrorTopLevelDeclarationForDanglingModifierList(modifierList: KtModifierList) = buildDanglingModifierList {
            this.source = modifierList.toFirSourceElement(KtFakeSourceElementKind.DanglingModifierList)
            moduleData = baseModuleData
            origin = FirDeclarationOrigin.Source
            diagnostic = ConeDanglingModifierOnTopLevel
            symbol = FirDanglingModifierSymbol()
            withContainerSymbol(symbol) {
                for (annotationEntry in modifierList.getAnnotationEntries()) {
                    annotations += annotationEntry.convert()
                }
            }
        }
    }
}

enum class BodyBuildingMode {
    /**
     * Build every expression and every body
     */
    NORMAL,

    /**
     * Build [FirLazyBlock] for function bodies, constructors & getters/setters
     * Build [FirLazyExpression] for property initializers
     */
    LAZY_BODIES;

    companion object {
        fun lazyBodies(lazyBodies: Boolean): BodyBuildingMode = if (lazyBodies) LAZY_BODIES else NORMAL
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy