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

org.jetbrains.kotlin.fir.lightTree.converter.LightTreeRawFirDeclarationBuilder.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
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.lightTree.converter

import com.intellij.lang.LighterASTNode
import com.intellij.psi.TokenType
import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.ElementTypeUtils.isExpression
import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.isCallTheFirstStatement
import org.jetbrains.kotlin.fir.builder.*
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.DanglingTypeConstraint
import org.jetbrains.kotlin.fir.declarations.utils.addDeclarations
import org.jetbrains.kotlin.fir.declarations.utils.addDefaultBoundIfNecessary
import org.jetbrains.kotlin.fir.declarations.utils.danglingTypeConstraints
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.lightTree.fir.*
import org.jetbrains.kotlin.fir.lightTree.fir.modifier.Modifier
import org.jetbrains.kotlin.fir.lightTree.fir.modifier.TypeParameterModifier
import org.jetbrains.kotlin.fir.lightTree.fir.modifier.TypeProjectionModifier
import org.jetbrains.kotlin.fir.references.builder.buildExplicitSuperReference
import org.jetbrains.kotlin.fir.references.builder.buildExplicitThisReference
import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference
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.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import org.jetbrains.kotlin.util.getChildren
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled

class LightTreeRawFirDeclarationBuilder(
    session: FirSession,
    internal val baseScopeProvider: FirScopeProvider,
    tree: FlyweightCapableTreeStructure,
    context: Context = Context(),
) : AbstractLightTreeRawFirBuilder(session, tree, context) {

    private val expressionConverter = LightTreeRawFirExpressionBuilder(session, tree, this, context)

    /**
     * [org.jetbrains.kotlin.parsing.KotlinParsing.parseFile]
     * [org.jetbrains.kotlin.parsing.KotlinParsing.parsePreamble]
     */
    fun convertFile(file: LighterASTNode, sourceFile: KtSourceFile, linesMapping: KtSourceFileLinesMapping): FirFile {
        if (file.tokenType != KT_FILE) {
            //TODO throw error
            throw Exception()
        }

        val fileSymbol = FirFileSymbol()
        var fileAnnotations = mutableListOf()
        val importList = mutableListOf()
        val firDeclarationList = mutableListOf()
        val modifierList = mutableListOf()
        context.packageFqName = FqName.ROOT
        var packageDirective: FirPackageDirective? = null
        file.forEachChildren { child ->
            when (child.tokenType) {
                FILE_ANNOTATION_LIST -> {
                    withContainerSymbol(fileSymbol) {
                        fileAnnotations += convertAnnotationList(child)
                    }
                }
                PACKAGE_DIRECTIVE -> {
                    packageDirective = convertPackageDirective(child).also { context.packageFqName = it.packageFqName }
                }
                IMPORT_LIST -> importList += convertImportDirectives(child)
                CLASS -> firDeclarationList += convertClass(child)
                FUN -> firDeclarationList += convertFunctionDeclaration(child) as FirDeclaration
                KtNodeTypes.PROPERTY -> firDeclarationList += convertPropertyDeclaration(child)
                TYPEALIAS -> firDeclarationList += convertTypeAlias(child)
                OBJECT_DECLARATION -> firDeclarationList += convertClass(child)
                DESTRUCTURING_DECLARATION -> firDeclarationList += buildErrorTopLevelDestructuringDeclaration(child.toFirSourceElement())
                SCRIPT -> {
                    // TODO: scripts aren't supported yet
                }
                MODIFIER_LIST -> modifierList += child
            }
        }

        modifierList.forEach {
            firDeclarationList += buildErrorTopLevelDeclarationForDanglingModifierList(it)
        }

        return buildFile {
            symbol = fileSymbol
            source = file.toFirSourceElement()
            origin = FirDeclarationOrigin.Source
            moduleData = baseModuleData
            name = sourceFile.name
            this.sourceFile = sourceFile
            this.sourceFileLinesMapping = linesMapping
            this.packageDirective = packageDirective ?: buildPackageDirective { packageFqName = context.packageFqName }
            annotations += fileAnnotations
            imports += importList
            declarations += firDeclarationList
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseBlockExpression
     */
    fun convertBlockExpression(block: LighterASTNode): FirBlock {
        return convertBlockExpressionWithoutBuilding(block).build()
    }

    fun convertBlockExpressionWithoutBuilding(block: LighterASTNode, kind: KtFakeSourceElementKind? = null): FirBlockBuilder {
        val firStatements = block.forEachChildrenReturnList { node, container ->
            @Suppress("RemoveRedundantQualifierName")
            when (node.tokenType) {
                CLASS, OBJECT_DECLARATION -> container += convertClass(node) as FirStatement
                FUN -> container += convertFunctionDeclaration(node)
                KtNodeTypes.PROPERTY -> container += convertPropertyDeclaration(node) as FirStatement
                DESTRUCTURING_DECLARATION -> container += convertDestructingDeclaration(node).toFirDestructingDeclaration(this, baseModuleData)
                TYPEALIAS -> container += convertTypeAlias(node) as FirStatement
                CLASS_INITIALIZER -> shouldNotBeCalled("CLASS_INITIALIZER expected to be processed during class body conversion")
                else -> if (node.isExpression()) container += expressionConverter.getAsFirStatement(node)
            }
        }
        return FirBlockBuilder().apply {
            source = block.toFirSourceElement(kind)
            firStatements.forEach { firStatement ->
                val isForLoopBlock = firStatement is FirBlock && firStatement.source?.kind == KtFakeSourceElementKind.DesugaredForLoop
                if (firStatement !is FirBlock || isForLoopBlock || firStatement.annotations.isNotEmpty()) {
                    statements += firStatement
                } else {
                    statements += firStatement.statements
                }
            }
        }
    }

    /*****    PREAMBLE    *****/
    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parsePackageName
     */
    private fun convertPackageDirective(packageNode: LighterASTNode): FirPackageDirective {
        var packageName: FqName = FqName.ROOT
        packageNode.forEachChildren {
            when (it.tokenType) {
                //TODO separate logic for both expression types
                DOT_QUALIFIED_EXPRESSION, REFERENCE_EXPRESSION -> packageName = parsePackageName(it)
            }
        }
        return buildPackageDirective {
            packageFqName = packageName
            source = packageNode.toFirSourceElement()
        }
    }

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

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

        return packageName
    }

    private fun parsePackageParts(node: LighterASTNode): List {
        fun parse(node: LighterASTNode): MutableList {
            if (node.tokenType == DOT_QUALIFIED_EXPRESSION) {
                val children = node.getChildren(tree)

                if (children.size == 3) {
                    return parse(children.first()).apply {
                        add(children.last().getAsStringWithoutBacktick())
                    }
                }
            }

            if (node.tokenType == REFERENCE_EXPRESSION) {
                return mutableListOf(node.getAsStringWithoutBacktick())
            }

            return mutableListOf()
        }

        return parse(node)
    }

    private fun convertImportAlias(importAlias: LighterASTNode): Pair? {
        importAlias.forEachChildren {
            when (it.tokenType) {
                IDENTIFIER -> return Pair(it.asText, it.toFirSourceElement())
            }
        }

        return null
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseImportDirective
     */
    private fun convertImportDirective(importDirective: LighterASTNode): FirImport {
        var importedFqName: FqName? = null
        var isAllUnder = false
        var aliasName: String? = null
        var aliasSource: KtSourceElement? = null
        importDirective.forEachChildren {
            when (it.tokenType) {
                REFERENCE_EXPRESSION, DOT_QUALIFIED_EXPRESSION -> {
                    importedFqName = mutableListOf()
                        .apply { collectSegments(it) }
                        .joinToString(".")
                        .let { FqName(it) }
                }
                MUL -> isAllUnder = true
                IMPORT_ALIAS -> {
                    val importAlias = convertImportAlias(it)
                    if (importAlias != null) {
                        aliasName = importAlias.first
                        aliasSource = importAlias.second
                    }
                }
            }
        }

        return buildImport {
            source = importDirective.toFirSourceElement()
            this.importedFqName = importedFqName
            this.isAllUnder = isAllUnder
            this.aliasName = aliasName?.let { Name.identifier(it) }
            this.aliasSource = aliasSource
        }
    }

    private fun MutableList.collectSegments(expression: LighterASTNode) {
        when (expression.tokenType) {
            REFERENCE_EXPRESSION -> add(expression.getAsStringWithoutBacktick())
            DOT_QUALIFIED_EXPRESSION -> {
                expression.forEachChildren {
                    collectSegments(it)
                }
            }
            else -> {}
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseImportDirectives
     */
    private fun convertImportDirectives(importList: LighterASTNode): List {
        return importList.forEachChildrenReturnList { node, container ->
            when (node.tokenType) {
                IMPORT_DIRECTIVE -> container += convertImportDirective(node)
            }
        }
    }

    /*****    MODIFIERS    *****/
    /**
     * Convert only modifiers
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseModifierList
     */
    private fun convertModifierList(modifiers: LighterASTNode, isInClass: Boolean = false): Modifier {
        val modifier = Modifier()
        modifiers.forEachChildren {
            if (it.tokenType is KtModifierKeywordToken) {
                modifier.addModifier(it, isInClass)
            }
        }

        return modifier
    }

    /**
     * Convert only annotations
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseModifierList
     */
    private fun convertAnnotationList(annotations: LighterASTNode): List {
        return annotations.forEachChildrenReturnList { node, list ->
            when (node.tokenType) {
                ANNOTATION -> list += convertAnnotation(node)
                ANNOTATION_ENTRY -> list += convertAnnotationEntry(node)
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeModifierList
     */
    private fun convertTypeModifierList(modifiers: LighterASTNode): Modifier {
        val typeModifier = Modifier()
        modifiers.forEachChildren {
            when (it.tokenType) {
                ANNOTATION -> typeModifier.annotations += convertAnnotation(it)
                ANNOTATION_ENTRY -> typeModifier.annotations += convertAnnotationEntry(it)
                is KtModifierKeywordToken -> typeModifier.addModifier(it)
            }
        }
        return typeModifier
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeArgumentModifierList
     */
    private fun convertTypeArgumentModifierList(modifiers: LighterASTNode): TypeProjectionModifier {
        val typeArgumentModifier = TypeProjectionModifier()
        modifiers.forEachChildren {
            when (it.tokenType) {
                ANNOTATION -> typeArgumentModifier.annotations += convertAnnotation(it)
                ANNOTATION_ENTRY -> typeArgumentModifier.annotations += convertAnnotationEntry(it)
                is KtModifierKeywordToken -> typeArgumentModifier.addModifier(it)
            }
        }
        return typeArgumentModifier
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeArgumentModifierList
     */
    private fun convertTypeParameterModifiers(modifiers: LighterASTNode): TypeParameterModifier {
        val modifier = TypeParameterModifier()
        modifiers.forEachChildren {
            when (it.tokenType) {
                ANNOTATION -> modifier.annotations += convertAnnotation(it)
                ANNOTATION_ENTRY -> modifier.annotations += convertAnnotationEntry(it)
                is KtModifierKeywordToken -> modifier.addModifier(it)
            }
        }
        return modifier
    }

    /*****    ANNOTATIONS    *****/

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseAnnotationOrList
     */
    fun convertAnnotation(annotationNode: LighterASTNode): List {
        var annotationTarget: AnnotationUseSiteTarget? = null
        return annotationNode.forEachChildrenReturnList { node, container ->
            when (node.tokenType) {
                ANNOTATION_TARGET -> annotationTarget = convertAnnotationTarget(node)
                ANNOTATION_ENTRY -> container += convertAnnotationEntry(node, annotationTarget)
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseAnnotationTarget
     */
    private fun convertAnnotationTarget(annotationUseSiteTarget: LighterASTNode): AnnotationUseSiteTarget {
        lateinit var annotationTarget: AnnotationUseSiteTarget
        annotationUseSiteTarget.forEachChildren {
            when (it.tokenType) {
                FIELD_KEYWORD -> annotationTarget = FIELD
                FILE_KEYWORD -> annotationTarget = FILE
                PROPERTY_KEYWORD -> annotationTarget = AnnotationUseSiteTarget.PROPERTY
                GET_KEYWORD -> annotationTarget = PROPERTY_GETTER
                SET_KEYWORD -> annotationTarget = PROPERTY_SETTER
                RECEIVER_KEYWORD -> annotationTarget = RECEIVER
                PARAM_KEYWORD -> annotationTarget = CONSTRUCTOR_PARAMETER
                SETPARAM_KEYWORD -> annotationTarget = SETTER_PARAMETER
                DELEGATE_KEYWORD -> annotationTarget = PROPERTY_DELEGATE_FIELD
            }
        }

        return annotationTarget
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseAnnotation
     * can be treated as unescapedAnnotation
     */
    fun convertAnnotationEntry(
        unescapedAnnotation: LighterASTNode,
        defaultAnnotationUseSiteTarget: AnnotationUseSiteTarget? = null,
        diagnostic: ConeDiagnostic? = null,
    ): FirAnnotationCall {
        var annotationUseSiteTarget: AnnotationUseSiteTarget? = null
        lateinit var constructorCalleePair: Pair>
        unescapedAnnotation.forEachChildren {
            when (it.tokenType) {
                ANNOTATION_TARGET -> annotationUseSiteTarget = convertAnnotationTarget(it)
                CONSTRUCTOR_CALLEE -> constructorCalleePair = convertConstructorInvocation(unescapedAnnotation)
            }
        }
        val qualifier = (constructorCalleePair.first as? FirUserTypeRef)?.qualifier?.last()
        val name = qualifier?.name ?: Name.special("")
        val theCalleeReference = buildSimpleNamedReference {
            source = unescapedAnnotation
                .getChildNodeByType(CONSTRUCTOR_CALLEE)
                ?.getChildNodeByType(TYPE_REFERENCE)
                ?.getChildNodeByType(USER_TYPE)
                ?.getChildNodeByType(REFERENCE_EXPRESSION)
                ?.toFirSourceElement()
            this.name = name
        }
        return if (diagnostic == null) {
            buildAnnotationCall {
                source = unescapedAnnotation.toFirSourceElement()
                useSiteTarget = annotationUseSiteTarget ?: defaultAnnotationUseSiteTarget
                annotationTypeRef = constructorCalleePair.first
                calleeReference = theCalleeReference
                extractArgumentsFrom(constructorCalleePair.second)
                typeArguments += qualifier?.typeArgumentList?.typeArguments ?: listOf()
                containingDeclarationSymbol = context.containerSymbol
            }
        } else {
            buildErrorAnnotationCall {
                source = unescapedAnnotation.toFirSourceElement()
                useSiteTarget = annotationUseSiteTarget ?: defaultAnnotationUseSiteTarget
                annotationTypeRef = constructorCalleePair.first
                this.diagnostic = diagnostic
                calleeReference = theCalleeReference
                extractArgumentsFrom(constructorCalleePair.second)
                typeArguments += qualifier?.typeArgumentList?.typeArguments ?: listOf()
                containingDeclarationSymbol = context.containerSymbol
            }
        }
    }

    private fun LighterASTNode.hasValueParameters(): Boolean {
        return getChildNodesByType(VALUE_PARAMETER_LIST).let {
            it.isNotEmpty() && it.first().getChildNodesByType(VALUE_PARAMETER).isNotEmpty()
        }
    }

    /*****    DECLARATIONS    *****/
    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseClassOrObject
     */
    private fun convertClass(classNode: LighterASTNode): FirDeclaration {
        var modifiers: Modifier? = null
        var classKind: ClassKind = ClassKind.CLASS
        var identifier: String? = null
        val firTypeParameters = mutableListOf()
        var primaryConstructor: LighterASTNode? = null
        val typeConstraints = mutableListOf()
        val classAnnotations = mutableListOf()
        var classBody: LighterASTNode? = null
        var superTypeList: LighterASTNode? = null
        var typeParameterList: LighterASTNode? = null
        classNode.forEachChildren {
            when (it.tokenType) {
                MODIFIER_LIST -> modifiers = convertModifierList(it, isInClass = true)
                IDENTIFIER -> identifier = it.asText
            }
        }

        val calculatedModifiers = modifiers ?: Modifier()
        val className = identifier.nameAsSafeName(if (calculatedModifiers.isCompanion()) "Companion" else "")
        val isLocalWithinParent = classNode.getParent()?.elementType != CLASS_BODY && isClassLocal(classNode) { getParent() }
        val classIsExpect = calculatedModifiers.hasExpect() || context.containerIsExpect

        return withChildClassName(className, isExpect = classIsExpect, isLocalWithinParent) {
            val classSymbol = FirRegularClassSymbol(context.currentClassId)
            withContainerSymbol(classSymbol) {
                classNode.forEachChildren {
                    when (it.tokenType) {
                        MODIFIER_LIST -> {
                            classAnnotations += convertAnnotationList(it)
                        }
                        CLASS_KEYWORD -> classKind = ClassKind.CLASS
                        INTERFACE_KEYWORD -> classKind = ClassKind.INTERFACE
                        OBJECT_KEYWORD -> classKind = ClassKind.OBJECT
                        TYPE_PARAMETER_LIST -> typeParameterList = it
                        PRIMARY_CONSTRUCTOR -> primaryConstructor = it
                        SUPER_TYPE_LIST -> superTypeList = it
                        TYPE_CONSTRAINT_LIST -> typeConstraints += convertTypeConstraints(it)
                        CLASS_BODY -> classBody = it
                    }
                }

                if (classKind == ClassKind.CLASS) {
                    classKind = when {
                        calculatedModifiers.isEnum() -> ClassKind.ENUM_CLASS
                        calculatedModifiers.isAnnotation() -> ClassKind.ANNOTATION_CLASS
                        else -> classKind
                    }
                }

                val isLocal = context.inLocalContext
                val status = FirDeclarationStatusImpl(
                    if (isLocal) Visibilities.Local else calculatedModifiers.getVisibility(publicByDefault = true),
                    calculatedModifiers.getModality(isClassOrObject = true)
                ).apply {
                    isExpect = classIsExpect
                    isActual = calculatedModifiers.hasActual()
                    isInner = calculatedModifiers.isInner()
                    isCompanion = calculatedModifiers.isCompanion() && classKind == ClassKind.OBJECT
                    isData = calculatedModifiers.isDataClass()
                    isInline = calculatedModifiers.isInlineClass()
                    isFun = calculatedModifiers.isFunctionalInterface()
                    isExternal = calculatedModifiers.hasExternal()
                }


                typeParameterList?.let { firTypeParameters += convertTypeParameters(it, typeConstraints, classSymbol) }

                withCapturedTypeParameters(status.isInner || isLocal, classNode.toFirSourceElement(), firTypeParameters) {
                    var delegatedFieldsMap: Map? = null
                    buildRegularClass {
                        source = classNode.toFirSourceElement()
                        moduleData = baseModuleData
                        origin = FirDeclarationOrigin.Source
                        name = className
                        this.status = status
                        this.classKind = classKind
                        scopeProvider = baseScopeProvider
                        symbol = classSymbol
                        annotations += classAnnotations
                        typeParameters += firTypeParameters

                        context.appendOuterTypeParameters(ignoreLastLevel = true, typeParameters)

                        val selfType = classNode.toDelegatedSelfType(this)
                        registerSelfType(selfType)

                        val delegationSpecifiers = superTypeList?.let { convertDelegationSpecifiers(it) }
                        var delegatedSuperTypeRef: FirTypeRef? = delegationSpecifiers?.superTypeCalls?.lastOrNull()?.delegatedSuperTypeRef
                        val delegatedConstructorSource: KtLightSourceElement? = delegationSpecifiers?.superTypeCalls?.lastOrNull()?.source

                        val superTypeRefs = mutableListOf()

                        delegationSpecifiers?.let { superTypeRefs += it.superTypesRef }

                        when {
                            calculatedModifiers.isEnum() && (classKind == ClassKind.ENUM_CLASS) && delegatedConstructorSource == null -> {
                                delegatedSuperTypeRef = buildResolvedTypeRef {
                                    coneType = ConeClassLikeTypeImpl(
                                        implicitEnumType.coneType.lookupTag,
                                        arrayOf(selfType.coneType),
                                        isNullable = false
                                    )
                                    source =classNode.toFirSourceElement(KtFakeSourceElementKind.EnumSuperTypeRef)
                                }
                                superTypeRefs += delegatedSuperTypeRef
                            }
                            calculatedModifiers.isAnnotation() && (classKind == ClassKind.ANNOTATION_CLASS) -> {
                                superTypeRefs += implicitAnnotationType
                                delegatedSuperTypeRef = implicitAnyType
                            }
                        }

                        val classIsKotlinAny = symbol.classId == StandardClassIds.Any

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

                        this.superTypeRefs += superTypeRefs

                        val secondaryConstructors = classBody.getChildNodesByType(SECONDARY_CONSTRUCTOR)
                        val classWrapper = ClassWrapper(
                            calculatedModifiers, classKind, this, hasSecondaryConstructor = secondaryConstructors.isNotEmpty(),
                            hasDefaultConstructor = if (primaryConstructor != null) !primaryConstructor!!.hasValueParameters()
                            else secondaryConstructors.isEmpty() || secondaryConstructors.any { !it.hasValueParameters() },
                            delegatedSelfTypeRef = selfType,
                            delegatedSuperTypeRef = delegatedSuperTypeRef ?: FirImplicitTypeRefImplWithoutSource,
                            delegatedSuperCalls = delegationSpecifiers?.superTypeCalls ?: emptyList()
                        )
                        //parse primary constructor
                        val primaryConstructorWrapper = convertPrimaryConstructor(
                            classNode,
                            primaryConstructor,
                            selfType.source,
                            classWrapper,
                            delegatedConstructorSource,
                            containingClassIsExpectClass = status.isExpect,
                            isImplicitlyActual = status.isActual && (status.isInline || classKind == ClassKind.ANNOTATION_CLASS),
                            isKotlinAny = classIsKotlinAny,
                        )
                        val firPrimaryConstructor = primaryConstructorWrapper?.firConstructor
                        firPrimaryConstructor?.let { declarations += it }
                        delegationSpecifiers?.delegateFieldsMap?.values?.mapTo(declarations) { it.fir }
                        delegatedFieldsMap = delegationSpecifiers?.delegateFieldsMap?.takeIf { it.isNotEmpty() }

                        val properties = mutableListOf()
                        if (primaryConstructor != null && firPrimaryConstructor != null) {
                            //parse properties
                            properties += primaryConstructorWrapper.valueParameters
                                .filter { it.hasValOrVar() }
                                .map {
                                    it.toFirPropertyFromPrimaryConstructor(
                                        baseModuleData,
                                        callableIdForName(it.firValueParameter.name),
                                        classIsExpect,
                                        currentDispatchReceiverType(),
                                        context
                                    )
                                }
                            addDeclarations(properties)
                        }

                        //parse declarations
                        classBody?.let {
                            addDeclarations(convertClassBody(it, classWrapper))
                        }

                        //parse data class
                        if (calculatedModifiers.isDataClass() && firPrimaryConstructor != null) {
                            val zippedParameters = properties.map { it.source!!.lighterASTNode to it }
                            DataClassMembersGenerator(
                                classNode,
                                this,
                                zippedParameters,
                                context.packageFqName,
                                context.className,
                                createClassTypeRefWithSourceKind = { firPrimaryConstructor.returnTypeRef.copyWithNewSourceKind(it) },
                                createParameterTypeRefWithSourceKind = { property, kind -> property.returnTypeRef.copyWithNewSourceKind(kind) },
                                addValueParameterAnnotations = { valueParam ->
                                    valueParam.forEachChildren {
                                        if (it.tokenType == MODIFIER_LIST) convertAnnotationList(it).filterTo(annotations) {
                                            it.useSiteTarget.appliesToPrimaryConstructorParameter()
                                        }
                                    }
                                },
                            ).generate()
                        }

                        if (calculatedModifiers.isEnum()) {
                            generateValuesFunction(
                                baseModuleData,
                                context.packageFqName,
                                context.className,
                                classIsExpect
                            )
                            generateValueOfFunction(
                                baseModuleData,
                                context.packageFqName,
                                context.className,
                                classIsExpect
                            )
                            generateEntriesGetter(
                                baseModuleData,
                                context.packageFqName,
                                context.className,
                                classIsExpect
                            )
                        }
                        initCompanionObjectSymbolAttr()

                        contextReceivers.addAll(convertContextReceivers(classNode))
                    }.also {
                        it.delegateFieldsMap = delegatedFieldsMap
                    }
                }.also {
                    fillDanglingConstraintsTo(firTypeParameters, typeConstraints, it)
                }
            }
        }.also {
            if (classNode.getParent()?.elementType == KtStubElementTypes.CLASS_BODY) {
                it.initContainingClassForLocalAttr()
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseObjectLiteral
     * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitObjectLiteralExpression
     */
    fun convertObjectLiteral(objectLiteral: LighterASTNode): FirElement {
        return withChildClassName(SpecialNames.ANONYMOUS, forceLocalContext = true, isExpect = false) {
            var delegatedFieldsMap: Map? = null
            buildAnonymousObjectExpression {
                source = objectLiteral.toFirSourceElement()
                anonymousObject = buildAnonymousObject {
                    val objectDeclaration = objectLiteral.getChildNodesByType(OBJECT_DECLARATION).first()
                    source = objectDeclaration.toFirSourceElement()
                    origin = FirDeclarationOrigin.Source
                    moduleData = baseModuleData
                    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)

                    var modifiers: Modifier? = null
                    val objectAnnotations = mutableListOf()
                    var primaryConstructor: LighterASTNode? = null
                    val superTypeRefs = mutableListOf()
                    var delegatedSuperTypeRef: FirTypeRef? = null
                    var classBody: LighterASTNode? = null
                    var delegatedConstructorSource: KtLightSourceElement? = null
                    var delegatedSuperCalls: List? = null
                    var delegateFields: List? = null

                    objectDeclaration.forEachChildren {
                        when (it.tokenType) {
                            MODIFIER_LIST -> {
                                modifiers = convertModifierList(it)
                                objectAnnotations += convertAnnotationList(it)
                            }
                            PRIMARY_CONSTRUCTOR -> primaryConstructor = it
                            SUPER_TYPE_LIST -> convertDelegationSpecifiers(it).let { specifiers ->
                                delegatedSuperTypeRef = specifiers.superTypeCalls.lastOrNull()?.delegatedSuperTypeRef
                                superTypeRefs += specifiers.superTypesRef
                                delegatedConstructorSource = specifiers.superTypeCalls.lastOrNull()?.source
                                delegateFields = specifiers.delegateFieldsMap.values.map { it.fir }
                                delegatedFieldsMap = specifiers.delegateFieldsMap.takeIf { it.isNotEmpty() }
                                delegatedSuperCalls = specifiers.superTypeCalls
                            }
                            CLASS_BODY -> classBody = it
                        }
                    }

                    superTypeRefs.ifEmpty {
                        superTypeRefs += implicitAnyType
                        delegatedSuperTypeRef = implicitAnyType
                    }
                    val delegatedSuperType = delegatedSuperTypeRef ?: FirImplicitTypeRefImplWithoutSource

                    annotations += objectAnnotations
                    this.superTypeRefs += superTypeRefs

                    val classWrapper = ClassWrapper(
                        modifiers ?: Modifier(),
                        ClassKind.OBJECT,
                        this,
                        hasSecondaryConstructor = classBody.getChildNodesByType(SECONDARY_CONSTRUCTOR).isNotEmpty(),
                        hasDefaultConstructor = false,
                        delegatedSelfTypeRef = delegatedSelfType,
                        delegatedSuperTypeRef = delegatedSuperType,
                        delegatedSuperCalls = delegatedSuperCalls ?: emptyList(),
                    )
                    //parse primary constructor
                    convertPrimaryConstructor(
                        objectDeclaration,
                        primaryConstructor,
                        delegatedSelfType.source,
                        classWrapper,
                        delegatedConstructorSource,
                        containingClassIsExpectClass = false
                    )?.let { this.declarations += it.firConstructor }
                    delegateFields?.let { this.declarations += it }

                    //parse declarations
                    classBody?.let {
                        this.declarations += convertClassBody(it, classWrapper)
                    }
                }.also {
                    it.delegateFieldsMap = delegatedFieldsMap
                }
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseEnumEntry
     */
    private fun convertEnumEntry(enumEntry: LighterASTNode, classWrapper: ClassWrapper): FirEnumEntry {
        var modifiers: Modifier? = null
        val entryAnnotations = mutableListOf()
        lateinit var identifier: String
        val enumSuperTypeCallEntry = mutableListOf()
        var classBodyNode: LighterASTNode? = null
        var superTypeCallEntry: LighterASTNode? = null
        enumEntry.getChildNodeByType(IDENTIFIER)?.let {
            identifier = it.asText
        }

        val enumEntryName = identifier.nameAsSafeName()
        val containingClassIsExpectClass = classWrapper.hasExpect() || context.containerIsExpect
        return buildEnumEntry {
            symbol = FirEnumEntrySymbol(CallableId(context.currentClassId, enumEntryName))
            withContainerSymbol(symbol) {
                enumEntry.forEachChildren {
                    when (it.tokenType) {
                        MODIFIER_LIST -> {
                            modifiers = convertModifierList(it)
                            entryAnnotations += convertAnnotationList(it)
                        }
                        INITIALIZER_LIST -> {
                            enumSuperTypeCallEntry += convertInitializerList(it)
                            it.getChildNodeByType(SUPER_TYPE_CALL_ENTRY)?.let { superTypeCall ->
                                superTypeCallEntry = superTypeCall
                            }
                        }
                        CLASS_BODY -> classBodyNode = it
                    }
                }

                source = enumEntry.toFirSourceElement()
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                returnTypeRef = classWrapper.delegatedSelfTypeRef
                name = enumEntryName
                status = FirDeclarationStatusImpl(Visibilities.Public, Modality.FINAL).apply {
                    isStatic = true
                    isExpect = containingClassIsExpectClass
                }
                if (classWrapper.hasDefaultConstructor && enumEntry.getChildNodeByType(INITIALIZER_LIST) == null &&
                    entryAnnotations.isEmpty() && classBodyNode == null
                ) {
                    return@buildEnumEntry
                }
                annotations += entryAnnotations
                initializer = withChildClassName(enumEntryName, isExpect = false) {
                    buildAnonymousObjectExpression {
                        val entrySource = enumEntry.toFirSourceElement(KtFakeSourceElementKind.EnumInitializer)
                        source = entrySource
                        anonymousObject = buildAnonymousObject {
                            source = entrySource
                            moduleData = baseModuleData
                            origin = FirDeclarationOrigin.Source
                            classKind = ClassKind.ENUM_ENTRY
                            scopeProvider = baseScopeProvider
                            symbol = FirAnonymousObjectSymbol(context.packageFqName)
                            status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL)
                            val enumClassWrapper = ClassWrapper(
                                modifiers ?: Modifier(),
                                ClassKind.ENUM_ENTRY,
                                this,
                                hasSecondaryConstructor = classBodyNode.getChildNodesByType(SECONDARY_CONSTRUCTOR).isNotEmpty(),
                                hasDefaultConstructor = false,
                                delegatedSelfTypeRef = buildResolvedTypeRef {
                                    coneType = ConeClassLikeTypeImpl(
                                        [email protected](),
                                        ConeTypeProjection.EMPTY_ARRAY,
                                        isNullable = false
                                    )
                                }.also { registerSelfType(it) },
                                delegatedSuperTypeRef = classWrapper.delegatedSelfTypeRef,
                                delegatedSuperCalls = listOf(
                                    DelegatedConstructorWrapper(
                                        classWrapper.delegatedSelfTypeRef,
                                        enumSuperTypeCallEntry,
                                        superTypeCallEntry?.toFirSourceElement(),
                                    )
                                )
                            )
                            superTypeRefs += enumClassWrapper.delegatedSuperTypeRef
                            convertPrimaryConstructor(
                                enumEntry,
                                null,
                                enumEntry.toFirSourceElement(),
                                enumClassWrapper,
                                superTypeCallEntry?.toFirSourceElement(),
                                isEnumEntry = true,
                                containingClassIsExpectClass = containingClassIsExpectClass
                            )?.let { declarations += it.firConstructor }
                            classBodyNode?.also {
                                // Use ANONYMOUS_OBJECT_NAME for the owner class id of enum entry declarations
                                withChildClassName(SpecialNames.ANONYMOUS, forceLocalContext = true, isExpect = false) {
                                    declarations += convertClassBody(it, enumClassWrapper)
                                }
                            }
                        }
                    }
                }
            }
        }.also {
            it.containingClassForStaticMemberAttr = currentDispatchReceiverType()!!.lookupTag
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseEnumEntry
     */
    private fun convertInitializerList(initializerList: LighterASTNode): List {
        val firValueArguments = mutableListOf()
        initializerList.forEachChildren {
            when (it.tokenType) {
                SUPER_TYPE_CALL_ENTRY -> convertConstructorInvocation(it).apply {
                    firValueArguments += second
                }
            }
        }

        return firValueArguments
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseClassBody
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseEnumClassBody
     */
    private fun convertClassBody(classBody: LighterASTNode, classWrapper: ClassWrapper): List {
        val modifierLists = mutableListOf()
        var firDeclarations = classBody.forEachChildrenReturnList { node, container ->
            @Suppress("RemoveRedundantQualifierName")
            when (node.tokenType) {
                ENUM_ENTRY -> container += convertEnumEntry(node, classWrapper)
                CLASS -> container += convertClass(node)
                FUN -> container += convertFunctionDeclaration(node) as FirDeclaration
                KtNodeTypes.PROPERTY -> container += convertPropertyDeclaration(node, classWrapper)
                TYPEALIAS -> container += convertTypeAlias(node)
                OBJECT_DECLARATION -> container += convertClass(node)
                CLASS_INITIALIZER -> container += convertAnonymousInitializer(node, classWrapper) //anonymousInitializer
                SECONDARY_CONSTRUCTOR -> container += convertSecondaryConstructor(node, classWrapper)
                MODIFIER_LIST -> modifierLists += node
                DESTRUCTURING_DECLARATION -> container += buildErrorTopLevelDestructuringDeclaration(node.toFirSourceElement())
            }
        }
        for (node in modifierLists) {
            firDeclarations += buildErrorTopLevelDeclarationForDanglingModifierList(node)
        }
        return firDeclarations
    }

    private fun buildErrorTopLevelDeclarationForDanglingModifierList(node: LighterASTNode) = buildDanglingModifierList {
        this.source = node.toFirSourceElement(KtFakeSourceElementKind.DanglingModifierList)
        moduleData = baseModuleData
        origin = FirDeclarationOrigin.Source
        diagnostic = ConeDanglingModifierOnTopLevel
        symbol = FirDanglingModifierSymbol()
        withContainerSymbol(symbol) {
            annotations += convertAnnotationList(node)
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseClassOrObject
     * primaryConstructor branch
     */
    private fun convertPrimaryConstructor(
        classNode: LighterASTNode,
        primaryConstructor: LighterASTNode?,
        selfTypeSource: KtSourceElement?,
        classWrapper: ClassWrapper,
        delegatedConstructorSource: KtLightSourceElement?,
        isEnumEntry: Boolean = false,
        containingClassIsExpectClass: Boolean,
        isImplicitlyActual: Boolean = false,
        isKotlinAny: Boolean = false,
    ): PrimaryConstructor? {
        val shouldGenerateImplicitConstructor =
            (classWrapper.isEnumEntry() || !classWrapper.hasSecondaryConstructor) &&
                    !classWrapper.isInterface() &&
                    (!containingClassIsExpectClass || classWrapper.classBuilder.classKind == ClassKind.ENUM_ENTRY)
        val isErrorConstructor = primaryConstructor == null && !shouldGenerateImplicitConstructor
        if (isErrorConstructor && classWrapper.delegatedSuperCalls.isEmpty()) {
            return null
        }

        val constructorSymbol = FirConstructorSymbol(callableIdForClassConstructor())
        withContainerSymbol(constructorSymbol) {
            var modifiersIfPresent: Modifier? = null
            val constructorAnnotations = mutableListOf()
            val valueParameters = mutableListOf()
            var hasConstructorKeyword = false
            primaryConstructor?.forEachChildren {
                when (it.tokenType) {
                    MODIFIER_LIST -> {
                        modifiersIfPresent = convertModifierList(it)
                        constructorAnnotations += convertAnnotationList(it)
                    }
                    CONSTRUCTOR_KEYWORD -> hasConstructorKeyword = true
                    VALUE_PARAMETER_LIST -> valueParameters += convertValueParameters(
                        it,
                        constructorSymbol,
                        ValueParameterDeclaration.PRIMARY_CONSTRUCTOR
                    )
                }
            }

            val modifiers = modifiersIfPresent ?: Modifier()

            val generateDelegatedSuperCall = shouldGenerateDelegatedSuperCall(
                isAnySuperCall = isKotlinAny,
                isExpectClass = containingClassIsExpectClass,
                isEnumEntry = isEnumEntry,
                hasExplicitDelegatedCalls = classWrapper.delegatedSuperCalls.isNotEmpty()
            )

            val firDelegatedCall = runIf(generateDelegatedSuperCall) {
                fun createDelegatedConstructorCall(
                    delegatedConstructorSource: KtLightSourceElement?,
                    delegatedSuperTypeRef: FirTypeRef,
                    arguments: List,
                ): FirDelegatedConstructorCall {
                    return buildDelegatedConstructorCall {
                        source = delegatedConstructorSource
                            ?: primaryConstructor?.toFirSourceElement(KtFakeSourceElementKind.DelegatingConstructorCall)
                                    ?: selfTypeSource?.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall)
                        constructedTypeRef = delegatedSuperTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef)
                        isThis = false
                        calleeReference = buildExplicitSuperReference {
                            //[dirty] in case of enum classWrapper.delegatedSuperTypeRef.source is whole enum source
                            source = if (!isEnumEntry) {
                                classWrapper.delegatedSuperTypeRef.source?.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall)
                                    ?: [email protected]?.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall)
                            } else {
                                delegatedConstructorSource
                                    ?.lighterASTNode
                                    ?.getChildNodeByType(CONSTRUCTOR_CALLEE)
                                    ?.toFirSourceElement(KtFakeSourceElementKind.DelegatingConstructorCall)
                                    ?: [email protected]
                            }

                            superTypeRef = [email protected]
                        }
                        extractArgumentsFrom(arguments)
                    }
                }
                if (classWrapper.delegatedSuperCalls.size <= 1) {
                    createDelegatedConstructorCall(
                        delegatedConstructorSource,
                        classWrapper.delegatedSuperTypeRef,
                        classWrapper.delegatedSuperCalls.lastOrNull()?.arguments ?: emptyList(),
                    )
                } else {
                    buildMultiDelegatedConstructorCall {
                        classWrapper.delegatedSuperCalls.mapTo(delegatedConstructorCalls) { (delegatedSuperTypeRef, arguments, source) ->
                            createDelegatedConstructorCall(source, delegatedSuperTypeRef, arguments)
                        }
                    }
                }
            }

            val explicitVisibility = runIf(primaryConstructor != null) {
                modifiers.getVisibility().takeUnless { it == Visibilities.Unknown }
            }
            val status = FirDeclarationStatusImpl(explicitVisibility ?: classWrapper.defaultConstructorVisibility(), Modality.FINAL).apply {
                isExpect = modifiers.hasExpect() || context.containerIsExpect
                isActual = modifiers.hasActual() || isImplicitlyActual
                isInner = classWrapper.isInner()
                isFromSealedClass = classWrapper.isSealed() && explicitVisibility !== Visibilities.Private
                isFromEnumClass = classWrapper.isEnum()
            }

            val builder = when {
                isErrorConstructor -> createErrorConstructorBuilder(ConeNoConstructorError)
                else -> FirPrimaryConstructorBuilder()
            }
            builder.apply {
                source = primaryConstructor?.toFirSourceElement()
                    ?: selfTypeSource?.fakeElement(KtFakeSourceElementKind.ImplicitConstructor)
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                returnTypeRef = classWrapper.delegatedSelfTypeRef
                dispatchReceiverType = classWrapper.obtainDispatchReceiverForConstructor()
                this.status = status
                symbol = constructorSymbol
                annotations += constructorAnnotations
                typeParameters += constructorTypeParametersFromConstructedClass(classWrapper.classBuilder.typeParameters)
                this.valueParameters += valueParameters.map { it.firValueParameter }
                delegatedConstructor = firDelegatedCall
                this.body = null
                this.contextReceivers.addAll(convertContextReceivers(classNode))
            }

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

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseMemberDeclarationRest
     * at INIT keyword
     */
    private fun convertAnonymousInitializer(
        anonymousInitializer: LighterASTNode,
        classWrapper: ClassWrapper
    ): FirDeclaration {
        val initializerSymbol = FirAnonymousInitializerSymbol()
        withContainerSymbol(initializerSymbol) {
            var firBlock: FirBlock? = null
            val initializerAnnotations = mutableListOf()
            anonymousInitializer.forEachChildren {
                when (it.tokenType) {
                    MODIFIER_LIST -> initializerAnnotations += convertAnnotationList(it)
                    BLOCK -> withForcedLocalContext {
                        firBlock = convertBlock(it)
                    }
                }
            }

            return buildAnonymousInitializer {
                symbol = initializerSymbol
                source = anonymousInitializer.toFirSourceElement()
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                body = firBlock ?: buildEmptyExpressionBlock()
                containingDeclarationSymbol = classWrapper.classBuilder.ownerRegularOrAnonymousObjectSymbol
                annotations += initializerAnnotations
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseSecondaryConstructor
     */
    private fun convertSecondaryConstructor(secondaryConstructor: LighterASTNode, classWrapper: ClassWrapper): FirConstructor {
        var modifiers: Modifier? = null
        val constructorAnnotations = mutableListOf()
        val firValueParameters = mutableListOf()
        var constructorDelegationCall: FirDelegatedConstructorCall? = null
        var block: LighterASTNode? = null

        val constructorSymbol = FirConstructorSymbol(callableIdForClassConstructor())
        withContainerSymbol(constructorSymbol) {
            var delegatedConstructorNode: LighterASTNode? = null
            secondaryConstructor.forEachChildren {
                when (it.tokenType) {
                    MODIFIER_LIST -> {
                        modifiers = convertModifierList(it)
                        constructorAnnotations += convertAnnotationList(it)
                    }
                    VALUE_PARAMETER_LIST -> firValueParameters += convertValueParameters(
                        it,
                        constructorSymbol,
                        ValueParameterDeclaration.FUNCTION
                    )
                    CONSTRUCTOR_DELEGATION_CALL -> delegatedConstructorNode = it
                    BLOCK -> block = it
                }
            }

            val delegatedSelfTypeRef = classWrapper.delegatedSelfTypeRef
            val calculatedModifiers = modifiers ?: Modifier()
            val isExpect = calculatedModifiers.hasExpect() || context.containerIsExpect
            if (delegatedConstructorNode != null) {
                constructorDelegationCall = convertConstructorDelegationCall(delegatedConstructorNode, classWrapper, isExpect)
            }

            val explicitVisibility = calculatedModifiers.getVisibility().takeUnless { it == Visibilities.Unknown }
            val status = FirDeclarationStatusImpl(explicitVisibility ?: classWrapper.defaultConstructorVisibility(), Modality.FINAL).apply {
                this.isExpect = isExpect
                isActual = calculatedModifiers.hasActual()
                isInner = classWrapper.isInner()
                isFromSealedClass = classWrapper.isSealed() && explicitVisibility !== Visibilities.Private
                isFromEnumClass = classWrapper.isEnum()
            }

            val target = FirFunctionTarget(labelName = null, isLambda = false)
            return buildConstructor {
                source = secondaryConstructor.toFirSourceElement()
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                returnTypeRef = delegatedSelfTypeRef
                dispatchReceiverType = classWrapper.obtainDispatchReceiverForConstructor()
                this.status = status
                symbol = constructorSymbol
                delegatedConstructor = constructorDelegationCall

                context.firFunctionTargets += target
                annotations += constructorAnnotations
                typeParameters += constructorTypeParametersFromConstructedClass(classWrapper.classBuilder.typeParameters)
                valueParameters += firValueParameters.map { it.firValueParameter }
                val (body, contractDescription) = withForcedLocalContext {
                    convertFunctionBody(block, null, allowLegacyContractDescription = true)
                }
                this.body = body
                contractDescription?.let { this.contractDescription = it }
                context.firFunctionTargets.removeLast()
                this.contextReceivers.addAll(convertContextReceivers(secondaryConstructor.getParent()!!.getParent()!!))
            }.also {
                it.containingClassForStaticMemberAttr = currentDispatchReceiverType()!!.lookupTag
                target.bind(it)
            }
        }
    }

    private fun ClassWrapper.obtainDispatchReceiverForConstructor(): ConeClassLikeType? =
        if (isInner()) dispatchReceiverForInnerClassConstructor() else null

    /**
     * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.convert(
     * KtConstructorDelegationCall, FirTypeRef, Boolean)
     */
    private fun convertConstructorDelegationCall(
        constructorDelegationCall: LighterASTNode,
        classWrapper: ClassWrapper,
        isExpect: Boolean,
    ): FirDelegatedConstructorCall? {
        var thisKeywordPresent = false
        val firValueArguments = mutableListOf()
        constructorDelegationCall.forEachChildren {
            when (it.tokenType) {
                CONSTRUCTOR_DELEGATION_REFERENCE -> if (it.asText == "this") thisKeywordPresent = true
                VALUE_ARGUMENT_LIST -> firValueArguments += expressionConverter.convertValueArguments(it)
            }
        }

        val isImplicit = constructorDelegationCall.textLength == 0
        if (isImplicit && (classWrapper.modifiers.hasExternal() || isExpect)) {
            return null
        }
        val isThis = thisKeywordPresent
        val delegatedType =
            when {
                isThis -> classWrapper.delegatedSelfTypeRef
                else -> classWrapper.delegatedSuperTypeRef
            }

        return buildDelegatedConstructorCall {
            source = if (isImplicit) {
                constructorDelegationCall.toFirSourceElement().fakeElement(KtFakeSourceElementKind.ImplicitConstructor)
            } else {
                constructorDelegationCall.toFirSourceElement()
            }
            constructedTypeRef = delegatedType.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef)
            this.isThis = isThis
            val calleeKind =
                if (isImplicit) KtFakeSourceElementKind.ImplicitConstructor else KtFakeSourceElementKind.DelegatingConstructorCall
            val calleeSource = constructorDelegationCall.getChildNodeByType(CONSTRUCTOR_DELEGATION_REFERENCE)
                ?.toFirSourceElement(calleeKind)
                ?: [email protected]?.fakeElement(calleeKind)
            calleeReference = if (isThis) {
                buildExplicitThisReference {
                    this.source = calleeSource
                }
            } else {
                buildExplicitSuperReference {
                    source = calleeSource
                    superTypeRef = [email protected]
                }
            }
            extractArgumentsFrom(firValueArguments)
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeAlias
     */
    private fun convertTypeAlias(typeAlias: LighterASTNode): FirDeclaration {
        var modifiers: Modifier? = null
        var identifier: String? = null
        lateinit var firType: FirTypeRef
        val aliasAnnotations = mutableListOf()

        typeAlias.forEachChildren {
            when (it.tokenType) {
                MODIFIER_LIST -> modifiers = convertModifierList(it)
                IDENTIFIER -> identifier = it.asText
            }
        }

        val calculatedModifiers = modifiers ?: Modifier()
        val typeAliasName = identifier.nameAsSafeName()
        val typeAliasIsExpect = calculatedModifiers.hasExpect() || context.containerIsExpect
        return withChildClassName(typeAliasName, isExpect = typeAliasIsExpect) {
            val typeAliasSymbol = FirTypeAliasSymbol(context.currentClassId)
            withContainerSymbol(typeAliasSymbol) {
                typeAlias.forEachChildren {
                    when (it.tokenType) {
                        MODIFIER_LIST -> {
                            aliasAnnotations += convertAnnotationList(it)
                        }
                        TYPE_REFERENCE -> firType = convertType(it)
                    }
                }

                val firTypeParameters = mutableListOf()
                typeAlias.forEachChildren {
                    if (it.tokenType == TYPE_PARAMETER_LIST) {
                        firTypeParameters += convertTypeParameters(it, emptyList(), typeAliasSymbol)
                    }
                }

                buildTypeAlias {
                    source = typeAlias.toFirSourceElement()
                    moduleData = baseModuleData
                    origin = FirDeclarationOrigin.Source
                    name = typeAliasName
                    val isLocal = context.inLocalContext
                    status = FirDeclarationStatusImpl(
                        if (isLocal) Visibilities.Local else calculatedModifiers.getVisibility(publicByDefault = true),
                        Modality.FINAL,
                    ).apply {
                        isExpect = typeAliasIsExpect
                        isActual = calculatedModifiers.hasActual()
                    }

                    symbol = typeAliasSymbol
                    expandedTypeRef = firType
                    annotations += aliasAnnotations
                    typeParameters += firTypeParameters
                }
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseProperty
     */
    fun convertPropertyDeclaration(property: LighterASTNode, classWrapper: ClassWrapper? = null): FirDeclaration {
        var modifiers: Modifier? = null
        val propertyAnnotations = mutableListOf()
        var identifier: String? = null
        val firTypeParameters = mutableListOf()
        var isReturnType = false
        var delegate: LighterASTNode? = null
        var isVar = false
        var receiverType: FirTypeRef? = null
        var returnType: FirTypeRef = implicitType
        val typeConstraints = mutableListOf()
        val accessors = mutableListOf()
        var propertyInitializer: FirExpression? = null
        var typeParameterList: LighterASTNode? = null
        var fieldDeclaration: LighterASTNode? = null
        property.getChildNodeByType(IDENTIFIER)?.let {
            identifier = it.asText
        }

        val propertyName = identifier.nameAsSafeName()
        val parentNode = property.getParent()
        val isLocal = !(parentNode?.tokenType == KT_FILE || parentNode?.tokenType == CLASS_BODY)
        val propertySymbol = if (isLocal) {
            FirPropertySymbol(propertyName)
        } else {
            FirPropertySymbol(callableIdForName(propertyName))
        }

        withContainerSymbol(propertySymbol, isLocal) {
            val propertySource = property.toFirSourceElement()
            property.forEachChildren {
                when (it.tokenType) {
                    MODIFIER_LIST -> {
                        modifiers = convertModifierList(it)
                        propertyAnnotations += convertAnnotationList(it)
                    }
                    TYPE_PARAMETER_LIST -> typeParameterList = it
                    COLON -> isReturnType = true
                    TYPE_REFERENCE -> if (isReturnType) returnType = convertType(it) else receiverType = convertType(it)
                    TYPE_CONSTRAINT_LIST -> typeConstraints += convertTypeConstraints(it)
                    PROPERTY_DELEGATE -> delegate = it
                    VAR_KEYWORD -> isVar = true
                    PROPERTY_ACCESSOR -> {
                        accessors += it
                    }
                    BACKING_FIELD -> fieldDeclaration = it
                    else -> if (it.isExpression()) {
                        context.calleeNamesForLambda += null
                        propertyInitializer = withForcedLocalContext {
                            expressionConverter.getAsFirExpression(it, "Should have initializer")
                        }
                        context.calleeNamesForLambda.removeLast()
                    }
                }
            }

            val calculatedModifiers = modifiers ?: Modifier()
            return buildProperty {
                source = propertySource
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                returnTypeRef = returnType
                name = propertyName
                this.isVar = isVar

                receiverParameter = receiverType?.convertToReceiverParameter()
                initializer = propertyInitializer

                //probably can do this for delegateExpression itself
                val delegateSource = delegate?.let {
                    (it.getChildExpression() ?: it).toFirSourceElement()
                }

                symbol = propertySymbol

                typeParameterList?.let { firTypeParameters += convertTypeParameters(it, typeConstraints, symbol) }

                backingField = fieldDeclaration.convertBackingField(
                    symbol, calculatedModifiers, returnType, isVar,
                    if (isLocal) emptyList() else propertyAnnotations.filter {
                        it.useSiteTarget == FIELD || it.useSiteTarget == PROPERTY_DELEGATE_FIELD
                    },
                    property,
                )

                if (isLocal) {
                    this.isLocal = true
                    val delegateBuilder = delegate?.let {
                        FirWrappedDelegateExpressionBuilder().apply {
                            source = delegateSource?.fakeElement(KtFakeSourceElementKind.WrappedDelegate)
                            expression = expressionConverter.getAsFirExpression(it, "Incorrect delegate expression")
                        }
                    }
                    status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL).apply {
                        isLateInit = calculatedModifiers.hasLateinit()
                    }

                    typeParameters += firTypeParameters
                    generateAccessorsByDelegate(
                        delegateBuilder,
                        baseModuleData,
                        classWrapper?.classBuilder?.ownerRegularOrAnonymousObjectSymbol,
                        context = context,
                        isExtension = false
                    )
                } else {
                    this.isLocal = false

                    dispatchReceiverType = currentDispatchReceiverType()
                    withCapturedTypeParameters(true, propertySource, firTypeParameters) {
                        typeParameters += firTypeParameters

                        val delegateBuilder = delegate?.let {
                            FirWrappedDelegateExpressionBuilder().apply {
                                source = delegateSource?.fakeElement(KtFakeSourceElementKind.WrappedDelegate)
                                expression = expressionConverter.getAsFirExpression(it, "Should have delegate")
                            }
                        }

                        val propertyVisibility = calculatedModifiers.getVisibility()

                        fun defaultAccessorStatus() =
                            // Downward propagation of `inline` and `external` modifiers (from property to its accessors)
                            FirDeclarationStatusImpl(propertyVisibility, null).apply {
                                isInline = calculatedModifiers.hasInline()
                                isExternal = calculatedModifiers.hasExternal()
                            }

                        val convertedAccessors = accessors.map {
                            convertGetterOrSetter(it, returnType, propertyVisibility, symbol, calculatedModifiers, propertyAnnotations)
                        }
                        this.getter = convertedAccessors.find { it.isGetter }
                            ?: FirDefaultPropertyGetter(
                                property.toFirSourceElement(KtFakeSourceElementKind.DefaultAccessor),
                                moduleData,
                                FirDeclarationOrigin.Source,
                                returnType.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor),
                                propertyVisibility,
                                symbol,
                            ).also {
                                it.status = defaultAccessorStatus()
                                it.replaceAnnotations(propertyAnnotations.filterUseSiteTarget(PROPERTY_GETTER))
                                it.initContainingClassAttr()
                            }
                        // NOTE: We still need the setter even for a val property so we can report errors (e.g., VAL_WITH_SETTER).
                        this.setter = convertedAccessors.find { it.isSetter }
                            ?: if (isVar) {
                                FirDefaultPropertySetter(
                                    property.toFirSourceElement(KtFakeSourceElementKind.DefaultAccessor),
                                    moduleData,
                                    FirDeclarationOrigin.Source,
                                    returnType.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor),
                                    propertyVisibility,
                                    symbol,
                                    parameterAnnotations = propertyAnnotations.filterUseSiteTarget(SETTER_PARAMETER)
                                ).also {
                                    it.status = defaultAccessorStatus()
                                    it.replaceAnnotations(propertyAnnotations.filterUseSiteTarget(PROPERTY_SETTER))
                                    it.initContainingClassAttr()
                                }
                            } else null

                        status = FirDeclarationStatusImpl(propertyVisibility, calculatedModifiers.getModality(isClassOrObject = false)).apply {
                            isExpect = calculatedModifiers.hasExpect() || context.containerIsExpect
                            isActual = calculatedModifiers.hasActual()
                            isOverride = calculatedModifiers.hasOverride()
                            isConst = calculatedModifiers.isConst()
                            isLateInit = calculatedModifiers.hasLateinit()
                            isExternal = calculatedModifiers.hasExternal()
                        }

                        generateAccessorsByDelegate(
                            delegateBuilder,
                            baseModuleData,
                            classWrapper?.classBuilder?.ownerRegularOrAnonymousObjectSymbol,
                            context,
                            isExtension = receiverType != null,
                        )
                    }
                }
                annotations += when {
                    isLocal -> propertyAnnotations
                    else -> propertyAnnotations.filterStandalonePropertyRelevantAnnotations(isVar)
                }

                contextReceivers.addAll(convertContextReceivers(property))
            }.also {
                if (!isLocal) {
                    fillDanglingConstraintsTo(firTypeParameters, typeConstraints, it)
                }
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitDestructuringDeclaration
     */
    internal fun convertDestructingDeclaration(destructingDeclaration: LighterASTNode): DestructuringDeclaration {
        val annotations = mutableListOf()
        var isVar = false
        val entries = mutableListOf()
        val source = destructingDeclaration.toFirSourceElement()
        var firExpression: FirExpression? = null
        destructingDeclaration.forEachChildren {
            when (it.tokenType) {
                MODIFIER_LIST -> annotations += convertAnnotationList(it)
                VAR_KEYWORD -> isVar = true
                DESTRUCTURING_DECLARATION_ENTRY -> entries += convertDestructingDeclarationEntry(it)
                // Property delegates should be ignored as they aren't a valid initializers
                PROPERTY_DELEGATE -> {}
                else -> if (it.isExpression()) firExpression =
                    expressionConverter.getAsFirExpression(it, "Initializer required for destructuring declaration")
            }
        }

        return DestructuringDeclaration(
            isVar,
            entries,
            firExpression ?: buildErrorExpression(
                null,
                ConeSyntaxDiagnostic("Initializer required for destructuring declaration")
            ),
            source,
            annotations
        )
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseMultiDeclarationName
     */
    private fun convertDestructingDeclarationEntry(entry: LighterASTNode): DestructuringEntry {
        val annotations = mutableListOf()
        var identifier: String? = null
        var firType: FirTypeRef? = null
        entry.forEachChildren {
            when (it.tokenType) {
                MODIFIER_LIST -> annotations += convertAnnotationList(it)
                IDENTIFIER -> identifier = it.asText
                TYPE_REFERENCE -> firType = convertType(it)
            }
        }

        val name = if (identifier == "_") {
            SpecialNames.UNDERSCORE_FOR_UNUSED_VAR
        } else {
            identifier.nameAsSafeName()
        }

        return DestructuringEntry(
            source = entry.toFirSourceElement(),
            returnTypeRef = firType ?: implicitType,
            name = name,
            annotations = annotations,
        )
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parsePropertyComponent
     */
    private fun convertGetterOrSetter(
        getterOrSetter: LighterASTNode,
        propertyTypeRef: FirTypeRef,
        propertyVisibility: Visibility,
        propertySymbol: FirPropertySymbol,
        propertyModifiers: Modifier,
        propertyAnnotations: List,
    ): FirPropertyAccessor {
        var modifiers: Modifier? = null
        val accessorAnnotations = mutableListOf()
        var isGetter = true
        var returnType: FirTypeRef? = null
        val propertyTypeRefToUse = propertyTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef)
        val accessorSymbol = FirPropertyAccessorSymbol()
        var firValueParameters: FirValueParameter = buildDefaultSetterValueParameter {
            moduleData = baseModuleData
            containingFunctionSymbol = accessorSymbol
            origin = FirDeclarationOrigin.Source
            returnTypeRef = propertyTypeRefToUse
            symbol = FirValueParameterSymbol(StandardNames.DEFAULT_VALUE_PARAMETER)
        }
        var block: LighterASTNode? = null
        var expression: LighterASTNode? = null
        var outerContractDescription: FirContractDescription? = null


        getterOrSetter.forEachChildren {
            if (it.asText == "set") isGetter = false
            when (it.tokenType) {
                SET_KEYWORD -> isGetter = false
                MODIFIER_LIST -> {
                    modifiers = convertModifierList(it)
                    accessorAnnotations += convertAnnotationList(it)
                }
                TYPE_REFERENCE -> returnType = convertType(it)
                VALUE_PARAMETER_LIST -> firValueParameters = convertSetterParameter(
                    it, accessorSymbol, propertyTypeRefToUse, propertyAnnotations.filterUseSiteTarget(SETTER_PARAMETER)
                )
                CONTRACT_EFFECT_LIST -> outerContractDescription = obtainContractDescription(it)
                BLOCK -> block = it
                else -> if (it.isExpression()) expression = it
            }
        }

        val calculatedModifiers = modifiers ?: Modifier()
        var accessorVisibility = calculatedModifiers.getVisibility()
        if (accessorVisibility == Visibilities.Unknown) {
            accessorVisibility = propertyVisibility
        }
        val status =
            // Downward propagation of `inline`, `external` and `expect` modifiers (from property to its accessors)
            FirDeclarationStatusImpl(accessorVisibility, calculatedModifiers.getModality(isClassOrObject = false)).apply {
                isInline = propertyModifiers.hasInline() || calculatedModifiers.hasInline()
                isExternal = propertyModifiers.hasExternal() || calculatedModifiers.hasExternal()
                isExpect = propertyModifiers.hasExpect() || calculatedModifiers.hasExpect()
            }
        val sourceElement = getterOrSetter.toFirSourceElement()
        val accessorAdditionalAnnotations = propertyAnnotations.filterUseSiteTarget(
            if (isGetter) PROPERTY_GETTER
            else PROPERTY_SETTER
        )
        if (block == null && expression == null) {
            return FirDefaultPropertyAccessor
                .createGetterOrSetter(
                    sourceElement,
                    baseModuleData,
                    FirDeclarationOrigin.Source,
                    propertyTypeRefToUse,
                    accessorVisibility,
                    propertySymbol,
                    isGetter,
                    parameterSource = firValueParameters.source,
                )
                .also { accessor ->
                    accessor.replaceAnnotations(accessorAnnotations + accessorAdditionalAnnotations)
                    accessor.status = status
                    accessor.initContainingClassAttr()
                    accessor.valueParameters.firstOrNull()?.replaceReturnTypeRef(firValueParameters.returnTypeRef)
                }
        }
        val target = FirFunctionTarget(labelName = null, isLambda = false)
        return buildPropertyAccessor {
            source = sourceElement
            moduleData = baseModuleData
            origin = FirDeclarationOrigin.Source
            returnTypeRef = returnType ?: if (isGetter) propertyTypeRefToUse else implicitUnitType
            symbol = accessorSymbol
            this.isGetter = isGetter
            this.status = status
            context.firFunctionTargets += target
            annotations += accessorAdditionalAnnotations
            annotations += accessorAnnotations

            if (!isGetter) {
                valueParameters += firValueParameters
            }
            val allowLegacyContractDescription = outerContractDescription == null
            val bodyWithContractDescription = withForcedLocalContext {
                convertFunctionBody(block, expression, allowLegacyContractDescription)
            }
            this.body = bodyWithContractDescription.first
            val contractDescription = outerContractDescription ?: bodyWithContractDescription.second
            contractDescription?.let {
                this.contractDescription = it
            }
            context.firFunctionTargets.removeLast()
            this.propertySymbol = propertySymbol
        }.also {
            target.bind(it)
            it.initContainingClassAttr()
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parsePropertyComponent
     */
    private fun LighterASTNode?.convertBackingField(
        propertySymbol: FirPropertySymbol,
        propertyModifiers: Modifier,
        propertyReturnType: FirTypeRef,
        isVar: Boolean,
        annotationsFromProperty: List,
        property: LighterASTNode,
    ): FirBackingField {
        var modifiers: Modifier? = null
        val fieldAnnotations = mutableListOf()
        var returnType: FirTypeRef = implicitType
        var backingFieldInitializer: FirExpression? = null
        this?.forEachChildren {
            when {
                it.tokenType == MODIFIER_LIST -> {
                    modifiers = convertModifierList(it)
                    fieldAnnotations += convertAnnotationList(it)
                }
                it.tokenType == TYPE_REFERENCE -> returnType = convertType(it)
                it.isExpression() -> {
                    backingFieldInitializer = expressionConverter.getAsFirExpression(it, "Should have initializer")
                }
            }
        }
        val calculatedModifiers = modifiers ?: Modifier()
        var componentVisibility = calculatedModifiers.getVisibility()
        if (componentVisibility == Visibilities.Unknown) {
            componentVisibility = Visibilities.Private
        }
        val status = obtainPropertyComponentStatus(componentVisibility, calculatedModifiers, propertyModifiers)
        val sourceElement = this?.toFirSourceElement()
        return if (this != null) {
            buildBackingField {
                source = sourceElement
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                returnTypeRef = returnType
                name = StandardNames.BACKING_FIELD
                symbol = FirBackingFieldSymbol(CallableId(name))
                this.status = status
                annotations += fieldAnnotations
                annotations += annotationsFromProperty
                this.propertySymbol = propertySymbol
                this.initializer = backingFieldInitializer
                this.isVar = isVar
                this.isVal = !isVar
            }
        } else {
            FirDefaultPropertyBackingField(
                moduleData = baseModuleData,
                origin = FirDeclarationOrigin.Source,
                source = property.toFirSourceElement(KtFakeSourceElementKind.DefaultAccessor),
                annotations = annotationsFromProperty.toMutableList(),
                returnTypeRef = propertyReturnType.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor),
                isVar = isVar,
                propertySymbol = propertySymbol,
                status = status,
            )
        }
    }

    private fun obtainPropertyComponentStatus(
        componentVisibility: Visibility,
        modifiers: Modifier,
        propertyModifiers: Modifier,
    ): FirDeclarationStatus {
        // Downward propagation of `inline` and `external` modifiers (from property to its accessors)
        return FirDeclarationStatusImpl(componentVisibility, modifiers.getModality(isClassOrObject = false)).apply {
            isInline = propertyModifiers.hasInline() || modifiers.hasInline()
            isExternal = propertyModifiers.hasExternal() || modifiers.hasExternal()
            isLateInit = modifiers.hasLateinit()
        }
    }

    private fun obtainContractDescription(rawContractDescription: LighterASTNode): FirContractDescription =
        buildRawContractDescription {
            source = rawContractDescription.toFirSourceElement()
            extractRawEffects(rawContractDescription, rawEffects)
        }

    private fun extractRawEffects(rawContractDescription: LighterASTNode, destination: MutableList) {
        rawContractDescription.forEachChildren {
            val errorReason = "The contract effect is not an expression"
            when (it.tokenType) {
                CONTRACT_EFFECT -> {
                    val effect = it.getFirstChild()
                    if (effect == null) {
                        val errorExpression =
                            buildErrorExpression(null, ConeSimpleDiagnostic(errorReason, DiagnosticKind.ExpressionExpected))
                        destination.add(errorExpression)
                    } else {
                        val expression = expressionConverter.convertExpression(effect, errorReason)
                        destination.add(expression as FirExpression)
                    }
                }
                else -> Unit
            }
        }
    }

    /**
     * this is just a VALUE_PARAMETER_LIST
     *
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parsePropertyComponent
     * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.toFirValueParameter
     */
    private fun convertSetterParameter(
        setterParameter: LighterASTNode,
        functionSymbol: FirFunctionSymbol<*>,
        propertyTypeRef: FirTypeRef,
        additionalAnnotations: List
    ): FirValueParameter {
        var modifiers: Modifier? = null
        lateinit var firValueParameter: FirValueParameter
        setterParameter.forEachChildren {
            when (it.tokenType) {
                MODIFIER_LIST -> modifiers = convertModifierList(it)
                VALUE_PARAMETER -> firValueParameter = convertValueParameter(it, functionSymbol, ValueParameterDeclaration.SETTER).firValueParameter
            }
        }

        val calculatedModifiers = modifiers ?: Modifier()
        return buildValueParameter {
            source = firValueParameter.source
            containingFunctionSymbol = functionSymbol
            moduleData = baseModuleData
            origin = FirDeclarationOrigin.Source
            returnTypeRef = if (firValueParameter.returnTypeRef == implicitType) propertyTypeRef else firValueParameter.returnTypeRef
            name = firValueParameter.name
            symbol = FirValueParameterSymbol(firValueParameter.name)
            defaultValue = firValueParameter.defaultValue
            isCrossinline = calculatedModifiers.hasCrossinline() || firValueParameter.isCrossinline
            isNoinline = calculatedModifiers.hasNoinline() || firValueParameter.isNoinline
            isVararg = calculatedModifiers.hasVararg() || firValueParameter.isVararg
            annotations += firValueParameter.annotations
            annotations += additionalAnnotations
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseFunction
     */
    fun convertFunctionDeclaration(functionDeclaration: LighterASTNode): FirStatement {
        var modifiers: Modifier? = null
        val functionAnnotations = mutableListOf()
        var identifier: String? = null
        var valueParametersList: LighterASTNode? = null
        var isReturnType = false
        var receiverType: FirTypeRef? = null
        var returnType: FirTypeRef? = null
        val typeConstraints = mutableListOf()
        var block: LighterASTNode? = null
        var expression: LighterASTNode? = null
        var hasEqToken = false
        var typeParameterList: LighterASTNode? = null
        var outerContractDescription: FirContractDescription? = null
        functionDeclaration.getChildNodeByType(IDENTIFIER)?.let {
            identifier = it.asText
        }

        val parentNode = functionDeclaration.getParent()
        val isLocal = !(parentNode?.tokenType == KT_FILE || parentNode?.tokenType == CLASS_BODY)
        val functionSource = functionDeclaration.toFirSourceElement()
        val isAnonymousFunction = identifier == null && isLocal
        val functionName = identifier.nameAsSafeName()
        val functionSymbol: FirFunctionSymbol<*> = if (isAnonymousFunction) {
            FirAnonymousFunctionSymbol()
        } else {
            FirNamedFunctionSymbol(callableIdForName(functionName))
        }

        withContainerSymbol(functionSymbol, isLocal) {
            val target: FirFunctionTarget
            functionDeclaration.forEachChildren {
                when (it.tokenType) {
                    MODIFIER_LIST -> {
                        modifiers = convertModifierList(it)
                        functionAnnotations += convertAnnotationList(it)
                    }
                    TYPE_PARAMETER_LIST -> typeParameterList = it
                    VALUE_PARAMETER_LIST -> valueParametersList = it //must convert later, because it can contains "return"
                    COLON -> isReturnType = true
                    TYPE_REFERENCE -> if (isReturnType) returnType = convertType(it) else receiverType = convertType(it)
                    TYPE_CONSTRAINT_LIST -> typeConstraints += convertTypeConstraints(it)
                    CONTRACT_EFFECT_LIST -> outerContractDescription = obtainContractDescription(it)
                    BLOCK -> block = it
                    EQ -> hasEqToken = true
                    else -> if (it.isExpression()) expression = it
                }
            }

            val calculatedModifiers = modifiers ?: Modifier()

            if (returnType == null) {
                returnType =
                    if (block != null || !hasEqToken) implicitUnitType
                    else implicitType
            }

            val functionBuilder = if (isAnonymousFunction) {
                FirAnonymousFunctionBuilder().apply {
                    source = functionSource
                    receiverParameter = receiverType?.convertToReceiverParameter()
                    symbol = functionSymbol as FirAnonymousFunctionSymbol
                    isLambda = false
                    hasExplicitParameterList = true
                    label = context.getLastLabel(functionDeclaration)
                    val labelName = label?.name ?: context.calleeNamesForLambda.lastOrNull()?.identifier
                    target = FirFunctionTarget(labelName = labelName, isLambda = false)
                    if (calculatedModifiers.hasSuspend()) {
                        status = FirResolvedDeclarationStatusImpl.DEFAULT_STATUS_FOR_SUSPEND_FUNCTION_EXPRESSION
                    }
                }
            } else {
                val labelName =
                    context.getLastLabel(functionDeclaration)?.name ?: runIf(!functionName.isSpecial) { functionName.identifier }
                target = FirFunctionTarget(labelName, isLambda = false)
                FirSimpleFunctionBuilder().apply {
                    source = functionSource
                    receiverParameter = receiverType?.convertToReceiverParameter()
                    name = functionName
                    status = FirDeclarationStatusImpl(
                        if (isLocal) Visibilities.Local else calculatedModifiers.getVisibility(),
                        calculatedModifiers.getModality(isClassOrObject = false)
                    ).apply {
                        isExpect = calculatedModifiers.hasExpect() || context.containerIsExpect
                        isActual = calculatedModifiers.hasActual()
                        isOverride = calculatedModifiers.hasOverride()
                        isOperator = calculatedModifiers.hasOperator()
                        isInfix = calculatedModifiers.hasInfix()
                        isInline = calculatedModifiers.hasInline()
                        isTailRec = calculatedModifiers.hasTailrec()
                        isExternal = calculatedModifiers.hasExternal()
                        isSuspend = calculatedModifiers.hasSuspend()
                    }

                    symbol = functionSymbol as FirNamedFunctionSymbol
                    dispatchReceiverType = runIf(!isLocal) { currentDispatchReceiverType() }
                    contextReceivers.addAll(convertContextReceivers(functionDeclaration))
                }
            }

            val firTypeParameters = mutableListOf()
            typeParameterList?.let { firTypeParameters += convertTypeParameters(it, typeConstraints, functionSymbol) }

            val function = functionBuilder.apply {
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Source
                returnTypeRef = returnType!!

                context.firFunctionTargets += target
                annotations += functionAnnotations

                val actualTypeParameters = if (this is FirSimpleFunctionBuilder) {
                    typeParameters += firTypeParameters
                    typeParameters
                } else {
                    listOf()
                }

                withCapturedTypeParameters(true, functionSource, actualTypeParameters) {
                    valueParametersList?.let { list ->
                        valueParameters += convertValueParameters(
                            list,
                            functionSymbol,
                            if (isAnonymousFunction) ValueParameterDeclaration.LAMBDA else ValueParameterDeclaration.FUNCTION
                        ).map { it.firValueParameter }
                    }

                    val allowLegacyContractDescription = outerContractDescription == null
                    val bodyWithContractDescription = withForcedLocalContext {
                        convertFunctionBody(block, expression, allowLegacyContractDescription)
                    }
                    this.body = bodyWithContractDescription.first
                    val contractDescription = outerContractDescription ?: bodyWithContractDescription.second
                    contractDescription?.let {
                        if (this is FirSimpleFunctionBuilder) {
                            this.contractDescription = it
                        } else if (this is FirAnonymousFunctionBuilder) {
                            this.contractDescription = it
                        }
                    }
                }
                context.firFunctionTargets.removeLast()
            }.build().also {
                target.bind(it)
                if (it is FirSimpleFunction) {
                    fillDanglingConstraintsTo(firTypeParameters, typeConstraints, it)
                }
            }

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

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseFunctionBody
     * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.buildFirBody
     */
    private fun convertFunctionBody(
        blockNode: LighterASTNode?,
        expression: LighterASTNode?,
        allowLegacyContractDescription: Boolean
    ): Pair {
        return when {
            blockNode != null -> {
                val block = convertBlock(blockNode)
                val contractDescription = runIf(allowLegacyContractDescription) {
                    val blockSource = block.source
                    val diagnostic = when {
                        blockSource == null || !isCallTheFirstStatement(blockSource) -> ConeContractShouldBeFirstStatement
                        else -> null
                    }
                    processLegacyContractDescription(block, diagnostic)
                }
                block to contractDescription
            }
            expression != null -> FirSingleExpressionBlock(
                expressionConverter.getAsFirExpression(expression, "Function has no body (but should)").toReturn()
            ) to null
            else -> null to null
        }
    }

    private fun isCallTheFirstStatement(sourceElement: KtSourceElement): Boolean =
        isCallTheFirstStatement(sourceElement.lighterASTNode, { it.elementType }, { it.getChildren(sourceElement.treeStructure) })

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseBlock
     */
    fun convertBlock(block: LighterASTNode?): FirBlock {
        if (block == null) return buildEmptyExpressionBlock()
        if (block.tokenType != BLOCK) {
            return FirSingleExpressionBlock(
                expressionConverter.getAsFirStatement(block)
            )
        }

        return convertBlockExpression(block)
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseDelegationSpecifierList
     * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.extractSuperTypeListEntriesTo
     *
     * SUPER_TYPE_ENTRY             - userType
     * SUPER_TYPE_CALL_ENTRY        - constructorInvocation
     * DELEGATED_SUPER_TYPE_ENTRY   - explicitDelegation
     */
    //TODO make wrapper for result?
    private data class DelegationSpecifiers(
        val superTypeCalls: List,
        val superTypesRef: List,
        val delegateFieldsMap: Map,
    )

    private fun convertDelegationSpecifiers(delegationSpecifiers: LighterASTNode): DelegationSpecifiers {
        val superTypeRefs = mutableListOf()
        val superTypeCalls = mutableListOf()
        val delegateFieldsMap = mutableMapOf()
        var index = 0
        delegationSpecifiers.forEachChildren {
            when (it.tokenType) {
                SUPER_TYPE_ENTRY -> {
                    superTypeRefs += convertType(it)
                    index++
                }
                SUPER_TYPE_CALL_ENTRY -> convertConstructorInvocation(it).apply {
                    superTypeCalls += DelegatedConstructorWrapper(first, second, it.toFirSourceElement())
                    superTypeRefs += first
                    index++
                }
                DELEGATED_SUPER_TYPE_ENTRY -> {
                    superTypeRefs += convertExplicitDelegation(it, delegateFieldsMap, index)
                    index++
                }
            }
        }
        return DelegationSpecifiers(superTypeCalls, superTypeRefs, delegateFieldsMap)
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseDelegationSpecifier
     *
     * constructorInvocation
     *   : userType valueArguments
     *   ;
     */
    private fun convertConstructorInvocation(constructorInvocation: LighterASTNode): Pair> {
        var firTypeRef: FirTypeRef = implicitType
        val firValueArguments = mutableListOf()
        constructorInvocation.forEachChildren {
            when (it.tokenType) {
                CONSTRUCTOR_CALLEE -> firTypeRef = convertType(it)
                VALUE_ARGUMENT_LIST -> firValueArguments += expressionConverter.convertValueArguments(it)
            }
        }
        return Pair(firTypeRef, firValueArguments)
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseDelegationSpecifier
     *
     * explicitDelegation
     *   : userType "by" element
     *   ;
     */
    private fun convertExplicitDelegation(
        explicitDelegation: LighterASTNode,
        delegateFieldsMap: MutableMap,
        index: Int
    ): FirTypeRef {
        lateinit var firTypeRef: FirTypeRef
        var firExpression: FirExpression? = null
        explicitDelegation.forEachChildren {
            when (it.tokenType) {
                TYPE_REFERENCE -> firTypeRef = convertType(it)
                else -> if (it.isExpression()) firExpression = expressionConverter.getAsFirExpression(it, "Should have delegate")
            }
        }

        val calculatedFirExpression = firExpression ?: buildErrorExpression(
            explicitDelegation.toFirSourceElement(), ConeSyntaxDiagnostic("Should have delegate")
        )

        delegateFieldsMap.put(
            index,
            buildField {
                source = explicitDelegation.toFirSourceElement().fakeElement(KtFakeSourceElementKind.ClassDelegationField)
                moduleData = baseModuleData
                origin = FirDeclarationOrigin.Synthetic.DelegateField
                name = NameUtils.delegateFieldName(delegateFieldsMap.size)
                returnTypeRef = firTypeRef
                symbol = FirFieldSymbol(CallableId(context.currentClassId, name))
                isVar = false
                status = FirDeclarationStatusImpl(Visibilities.Private, Modality.FINAL)
                initializer = calculatedFirExpression
                dispatchReceiverType = currentDispatchReceiverType()
            }.symbol
        )
        return firTypeRef
    }

    /*****    TYPES    *****/
    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeParameterList
     */
    private fun convertTypeParameters(
        typeParameterList: LighterASTNode,
        typeConstraints: List,
        containingDeclarationSymbol: FirBasedSymbol<*>
    ): List {
        return typeParameterList.forEachChildrenReturnList { node, container ->
            when (node.tokenType) {
                TYPE_PARAMETER -> container += convertTypeParameter(node, typeConstraints, containingDeclarationSymbol)
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeConstraintList
     */
    private fun convertTypeConstraints(typeConstraints: LighterASTNode): List {
        return typeConstraints.forEachChildrenReturnList { node, container ->
            when (node.tokenType) {
                TYPE_CONSTRAINT -> container += convertTypeConstraint(node)
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeConstraint
     */
    private fun convertTypeConstraint(typeConstraint: LighterASTNode): TypeConstraint {
        var identifier: String? = null
        var firType: FirTypeRef? = null
        var referenceExpression: LighterASTNode? = null

        val annotations = mutableListOf()
        typeConstraint.forEachChildren {
            when (it.tokenType) {
                ANNOTATION_ENTRY -> {
                    annotations +=
                        convertAnnotationEntry(
                            it,
                            diagnostic = ConeSimpleDiagnostic(
                                "Type parameter annotations are not allowed inside where clauses", DiagnosticKind.AnnotationInWhereClause,
                            )
                        )
                }
                REFERENCE_EXPRESSION -> {
                    identifier = it.asText
                    referenceExpression = it
                }
                TYPE_REFERENCE -> firType = convertType(it)
            }
        }

        return TypeConstraint(
            annotations,
            identifier,
            firType ?: buildErrorTypeRef { },
            (referenceExpression ?: typeConstraint).toFirSourceElement()
        )
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeParameter
     */
    private fun convertTypeParameter(
        typeParameter: LighterASTNode,
        typeConstraints: List,
        containingSymbol: FirBasedSymbol<*>
    ): FirTypeParameter {
        var typeParameterModifiers: TypeParameterModifier? = null
        var identifier: String? = null
        var firType: FirTypeRef? = null
        typeParameter.forEachChildren {
            when (it.tokenType) {
                MODIFIER_LIST -> typeParameterModifiers = convertTypeParameterModifiers(it)
                IDENTIFIER -> identifier = it.asText
                TYPE_REFERENCE -> firType = convertType(it)
            }
        }

        val calculatedTypeParameterModifiers = typeParameterModifiers ?: TypeParameterModifier()
        return buildTypeParameter {
            source = typeParameter.toFirSourceElement()
            moduleData = baseModuleData
            origin = FirDeclarationOrigin.Source
            name = identifier.nameAsSafeName()
            symbol = FirTypeParameterSymbol()
            containingDeclarationSymbol = containingSymbol
            variance = calculatedTypeParameterModifiers.getVariance()
            isReified = calculatedTypeParameterModifiers.hasReified()
            annotations += calculatedTypeParameterModifiers.annotations
            firType?.let { bounds += it }
            for (typeConstraint in typeConstraints) {
                if (typeConstraint.identifier == identifier) {
                    bounds += typeConstraint.firTypeRef
                    annotations += typeConstraint.annotations
                }
            }
            addDefaultBoundIfNecessary()
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeRef
     */
    fun convertType(type: LighterASTNode): FirTypeRef {
        val typeRefSource = type.toFirSourceElement()

        // There can be MODIFIER_LIST children on the TYPE_REFERENCE node AND the descendant NULLABLE_TYPE nodes.
        // 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) in TYPE_REFERENCE
        //   `(@A() (@B Int))? -> No modifier list on TYPE_REFERENCE, but 2 modifier lists (@A and @B) on child NULLABLE_TYPE
        //   `@A() (@B Int)?   -> Has 1 modifier list (@A) on TYPE_REFERENCE, and 1 modifier list (@B) on child NULLABLE_TYPE
        //   `@A (@B() (@C() (@Bar D)?)?)?` -> Has 1 modifier list (@A) on B and 1 modifier list on each of the
        //                                     3 descendant NULLABLE_TYPE (@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 NULLABLE_TYPE
        // 2. `(suspend @A () -> Int)?` is a nullable suspend function type but the modifier list is on the child NULLABLE_TYPE
        //
        // TODO: Report MODIFIER_LIST_NOT_ALLOWED error when there are multiple modifier lists. How do we report on each of them?
        val allTypeModifiers = mutableListOf()

        var firType: FirTypeRef? = null
        type.forEachChildren {
            when (it.tokenType) {
                TYPE_REFERENCE -> firType = convertType(it)
                MODIFIER_LIST -> allTypeModifiers += convertTypeModifierList(it)
                USER_TYPE -> firType = convertUserType(typeRefSource, it)
                NULLABLE_TYPE -> firType = convertNullableType(typeRefSource, it, allTypeModifiers)
                FUNCTION_TYPE -> firType = convertFunctionType(typeRefSource, it, isSuspend = allTypeModifiers.hasSuspend())
                DYNAMIC_TYPE -> firType = buildDynamicTypeRef {
                    source = typeRefSource
                    isMarkedNullable = false
                }
                INTERSECTION_TYPE -> firType = convertIntersectionType(typeRefSource, it, false)
                CONTEXT_RECEIVER_LIST, TokenType.ERROR_ELEMENT -> firType =
                    buildErrorTypeRef {
                        source = typeRefSource
                        diagnostic = ConeSyntaxDiagnostic("Unwrapped type is null")
                    }
            }
        }

        val calculatedFirType = firType ?: buildErrorTypeRef {
            source = typeRefSource
            diagnostic = ConeSyntaxDiagnostic("Incomplete code")
        }

        for (modifierList in allTypeModifiers) {
            calculatedFirType.replaceAnnotations(calculatedFirType.annotations.smartPlus(modifierList.annotations))
        }
        return calculatedFirType
    }

    private fun Collection.hasSuspend() = any { it.hasSuspend() }

    private fun convertIntersectionType(typeRefSource: KtSourceElement, intersectionType: LighterASTNode, isNullable: Boolean): FirTypeRef {
        val children = arrayListOf()
        intersectionType.forEachChildren {
            if (it.tokenType != AND) { //skip in forEachChildren?
                children.add(convertType(it))
            }
        }

        if (children.size != 2) {
            return buildErrorTypeRef {
                source = typeRefSource
                diagnostic = ConeSyntaxDiagnostic("Wrong code")
            }
        }

        return buildIntersectionTypeRef {
            source = typeRefSource
            isMarkedNullable = isNullable
            leftType = children[0]
            rightType = children[1]
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeRefContents
     */
    private fun convertReceiverType(receiverType: LighterASTNode): FirTypeRef {
        receiverType.forEachChildren {
            when (it.tokenType) {
                TYPE_REFERENCE -> return convertType(it)
            }
        }

        //TODO specify error
        throw Exception()
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseNullableTypeSuffix
     */
    private fun convertNullableType(
        typeRefSource: KtSourceElement,
        nullableType: LighterASTNode,
        allTypeModifiers: MutableList,
        isNullable: Boolean = true
    ): FirTypeRef {
        lateinit var firType: FirTypeRef
        nullableType.forEachChildren {
            when (it.tokenType) {
                MODIFIER_LIST -> allTypeModifiers += convertTypeModifierList(it)
                USER_TYPE -> firType = convertUserType(typeRefSource, it, isNullable)
                FUNCTION_TYPE -> firType = convertFunctionType(typeRefSource, it, isNullable, isSuspend = allTypeModifiers.hasSuspend())
                NULLABLE_TYPE -> firType = convertNullableType(typeRefSource, it, allTypeModifiers)
                DYNAMIC_TYPE -> firType = buildDynamicTypeRef {
                    source = typeRefSource
                    isMarkedNullable = true
                }
                INTERSECTION_TYPE -> firType = convertIntersectionType(typeRefSource, it, isNullable)
            }
        }

        return firType
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseUserType
     */
    private fun convertUserType(
        typeRefSource: KtSourceElement,
        userType: LighterASTNode,
        isNullable: Boolean = false
    ): FirTypeRef {
        var simpleFirUserType: FirUserTypeRef? = null
        var identifier: String? = null
        var identifierSource: KtSourceElement? = null
        val firTypeArguments = mutableListOf()
        var typeArgumentsSource: KtSourceElement? = null
        userType.forEachChildren {
            when (it.tokenType) {
                USER_TYPE -> simpleFirUserType = convertUserType(typeRefSource, it) as? FirUserTypeRef //simple user type
                REFERENCE_EXPRESSION -> {
                    identifierSource = it.toFirSourceElement()
                    identifier = it.asText
                }
                TYPE_ARGUMENT_LIST -> {
                    typeArgumentsSource = it.toFirSourceElement()
                    firTypeArguments += convertTypeArguments(it, allowedUnderscoredTypeArgument = false)
                }
            }
        }

        if (identifier == null) {
            return buildErrorTypeRef {
                source = typeRefSource
                diagnostic = ConeSyntaxDiagnostic("Incomplete user type")
                simpleFirUserType?.let { qualifierPart ->
                    if (qualifierPart.qualifier.isNotEmpty()) {
                        partiallyResolvedTypeRef = buildUserTypeRef {
                            source = qualifierPart.qualifier.last().source
                            isMarkedNullable = false
                            this.qualifier.addAll(qualifierPart.qualifier)
                        }
                    }
                }
            }
        }

        val qualifierPart = FirQualifierPartImpl(
            identifierSource!!,
            identifier.nameAsSafeName(),
            FirTypeArgumentListImpl(typeArgumentsSource ?: typeRefSource).apply {
                typeArguments += firTypeArguments
            }
        )

        return buildUserTypeRef {
            source = typeRefSource
            isMarkedNullable = isNullable
            qualifier.add(qualifierPart)
            simpleFirUserType?.qualifier?.let { this.qualifier.addAll(0, it) }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeArgumentList
     */
    fun convertTypeArguments(typeArguments: LighterASTNode, allowedUnderscoredTypeArgument: Boolean): List {
        return typeArguments.forEachChildrenReturnList { node, container ->
            when (node.tokenType) {
                TYPE_PROJECTION -> container += convertTypeProjection(node, allowedUnderscoredTypeArgument)
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.tryParseTypeArgumentList
     */
    private fun convertTypeProjection(typeProjection: LighterASTNode, allowedUnderscoredTypeArgument: Boolean): FirTypeProjection {
        var modifiers: TypeProjectionModifier? = null
        lateinit var firType: FirTypeRef
        var isStarProjection = false
        typeProjection.forEachChildren {
            when (it.tokenType) {
                MODIFIER_LIST -> modifiers = convertTypeArgumentModifierList(it)
                TYPE_REFERENCE -> firType = convertType(it)
                MUL -> isStarProjection = true
            }
        }

        //annotations from modifiers must be ignored
        return when {
            isStarProjection -> buildStarProjection { source = typeProjection.toFirSourceElement() }
            allowedUnderscoredTypeArgument && (firType as? FirUserTypeRef)?.isUnderscored == true -> buildPlaceholderProjection {
                source = typeProjection.toFirSourceElement()
            }
            else -> buildTypeProjectionWithVariance {
                source = typeProjection.toFirSourceElement()
                typeRef = firType
                variance = (modifiers ?: TypeProjectionModifier()).getVariance()
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseFunctionType
     */
    private fun convertFunctionType(
        typeRefSource: KtSourceElement,
        functionType: LighterASTNode,
        isNullable: Boolean = false,
        isSuspend: Boolean = false
    ): FirTypeRef {
        var receiverTypeReference: FirTypeRef? = null
        lateinit var returnTypeReference: FirTypeRef
        val parameters = mutableListOf()
        functionType.forEachChildren {
            when (it.tokenType) {
                FUNCTION_TYPE_RECEIVER -> receiverTypeReference = convertReceiverType(it)
                VALUE_PARAMETER_LIST -> parameters += convertFunctionTypeParameters(it)
                TYPE_REFERENCE -> returnTypeReference = convertType(it)
            }
        }

        return buildFunctionTypeRef {
            source = typeRefSource
            isMarkedNullable = isNullable
            receiverTypeRef = receiverTypeReference
            returnTypeRef = returnTypeReference
            this.parameters += parameters
            this.isSuspend = isSuspend
            this.contextReceiverTypeRefs.addAll(
                functionType.getChildNodeByType(CONTEXT_RECEIVER_LIST)?.getChildNodesByType(CONTEXT_RECEIVER)?.mapNotNull {
                    it.getChildNodeByType(TYPE_REFERENCE)?.let(::convertType)
                }.orEmpty()
            )
        }
    }

    private fun convertFunctionTypeParameters(
        parameters: LighterASTNode,
    ): List {
        return parameters.forEachChildrenReturnList { node, container ->
            when (node.tokenType) {
                VALUE_PARAMETER -> {
                    var name: Name? = null
                    var typeRef: FirTypeRef? = null
                    node.forEachChildren {
                        when (it.tokenType) {
                            IDENTIFIER -> name = it.asText.nameAsSafeName()
                            TYPE_REFERENCE -> typeRef = convertType(it)
                        }
                    }
                    container += buildFunctionTypeParameter {
                        val parameterSource = node.toFirSourceElement()
                        source = parameterSource
                        this.name = name
                        this.returnTypeRef = typeRef ?: createNoTypeForParameterTypeRef(parameterSource)
                    }
                }
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseValueParameterList
     */
    fun convertValueParameters(
        valueParameters: LighterASTNode,
        functionSymbol: FirFunctionSymbol<*>,
        valueParameterDeclaration: ValueParameterDeclaration,
        additionalAnnotations: List = emptyList()
    ): List {
        return valueParameters.forEachChildrenReturnList { node, container ->
            when (node.tokenType) {
                VALUE_PARAMETER -> container += convertValueParameter(node, functionSymbol, valueParameterDeclaration, additionalAnnotations)
            }
        }
    }

    /**
     * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseValueParameter
     */
    fun convertValueParameter(
        valueParameter: LighterASTNode,
        functionSymbol: FirFunctionSymbol<*>?,
        valueParameterDeclaration: ValueParameterDeclaration,
        additionalAnnotations: List = emptyList()
    ): ValueParameter {
        var modifiers: Modifier? = null
        val valueAnnotations = mutableListOf()
        var isVal = false
        var isVar = false
        var identifier: String? = null
        var firType: FirTypeRef? = null
        var firExpression: FirExpression? = null
        var destructuringDeclaration: DestructuringDeclaration? = null
        valueParameter.forEachChildren {
            when (it.tokenType) {
                MODIFIER_LIST -> modifiers = convertModifierList(it)
                VAL_KEYWORD -> isVal = true
                VAR_KEYWORD -> isVar = true
                IDENTIFIER -> identifier = it.asText
                TYPE_REFERENCE -> {}
                DESTRUCTURING_DECLARATION -> destructuringDeclaration = convertDestructingDeclaration(it)
                else -> if (it.isExpression()) firExpression = expressionConverter.getAsFirExpression(it, "Should have default value")
            }
        }

        val name = convertValueParameterName(identifier.nameAsSafeName(), valueParameterDeclaration) { identifier }
        val valueParameterSymbol = FirValueParameterSymbol(name)
        withContainerSymbol(valueParameterSymbol, isLocal = valueParameterDeclaration != ValueParameterDeclaration.FUNCTION) {
            valueParameter.forEachChildren {
                when (it.tokenType) {
                    MODIFIER_LIST -> valueAnnotations += convertAnnotationList(it)
                    TYPE_REFERENCE -> firType = convertType(it)
                }
            }
        }

        val valueParameterSource = valueParameter.toFirSourceElement()
        return ValueParameter(
            valueParameterSymbol = valueParameterSymbol,
            isVal = isVal,
            isVar = isVar,
            modifiers = modifiers ?: Modifier(),
            valueParameterAnnotations = valueAnnotations,
            returnTypeRef = firType
                ?: when {
                    valueParameterDeclaration.shouldExplicitParameterTypeBePresent -> createNoTypeForParameterTypeRef(valueParameterSource)
                    else -> implicitType
                },
            source = valueParameterSource,
            moduleData = baseModuleData,
            isFromPrimaryConstructor = valueParameterDeclaration == ValueParameterDeclaration.PRIMARY_CONSTRUCTOR,
            additionalAnnotations = additionalAnnotations,
            name = name,
            defaultValue = firExpression,
            containingFunctionSymbol = functionSymbol,
            destructuringDeclaration = destructuringDeclaration
        )
    }

    private fun  fillDanglingConstraintsTo(
        typeParameters: List,
        typeConstraints: List,
        to: T
    ) where T : FirDeclaration, T : FirTypeParameterRefsOwner {
        val typeParamNames = typeParameters.map { it.name }.toSet()
        val result = typeConstraints.mapNotNull { constraint ->
            val name = constraint.identifier?.nameAsSafeName()
            if (name != null && !typeParamNames.contains(name)) {
                DanglingTypeConstraint(name, constraint.source)
            } else {
                null
            }

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

    private fun convertContextReceivers(container: LighterASTNode): List {
        val receivers = container.getChildNodeByType(CONTEXT_RECEIVER_LIST)?.getChildNodesByType(CONTEXT_RECEIVER) ?: emptyList()
        return receivers.map { contextReceiverElement ->
            buildContextReceiver {
                this.source = contextReceiverElement.toFirSourceElement()
                this.customLabelName =
                    contextReceiverElement
                        .getChildNodeByType(LABEL_QUALIFIER)
                        ?.getChildNodeByType(LABEL)
                        ?.getChildNodeByType(IDENTIFIER)
                        ?.getReferencedNameAsName()

                val typeReference = contextReceiverElement.getChildNodeByType(TYPE_REFERENCE)

                this.labelNameFromTypeRef = typeReference?.getChildNodeByType(USER_TYPE)
                    ?.getChildNodeByType(REFERENCE_EXPRESSION)
                    ?.getReferencedNameAsName()

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy