override fun check()

in compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirValueClassDeclarationChecker.kt [62:271]


    override fun check(declaration: FirRegularClass) {
        if (!declaration.symbol.isInlineOrValueClass()) {
            return
        }

        if (declaration.isInner || declaration.isLocal) {
            reporter.reportOn(declaration.source, FirErrors.VALUE_CLASS_NOT_TOP_LEVEL)
        }

        if (declaration.modality != Modality.FINAL) {
            reporter.reportOn(declaration.source, FirErrors.VALUE_CLASS_NOT_FINAL)
        }

        if (declaration.contextParameters.isNotEmpty() && LanguageFeature.ContextReceivers.isEnabled()) {
            reporter.reportOn(declaration.source, FirErrors.VALUE_CLASS_CANNOT_HAVE_CONTEXT_RECEIVERS)
        }


        for (supertypeEntry in declaration.superTypeRefs) {
            if (supertypeEntry is FirImplicitAnyTypeRef || supertypeEntry is FirErrorTypeRef) continue
            if (supertypeEntry.toRegularClassSymbol(context.session)?.isInterface == true) continue
            reporter.reportOn(supertypeEntry.source, FirErrors.VALUE_CLASS_CANNOT_EXTEND_CLASSES)
        }

        if (declaration.isSubtypeOfCloneable(context.session)) {
            reporter.reportOn(declaration.source, FirErrors.VALUE_CLASS_CANNOT_BE_CLONEABLE)
        }

        var primaryConstructor: FirConstructorSymbol? = null
        var primaryConstructorParametersByName = mapOf<Name, FirValueParameterSymbol>()
        val primaryConstructorPropertiesByName = hashMapOf<Name, FirPropertySymbol>()
        var primaryConstructorParametersSymbolsSet = setOf<FirValueParameterSymbol>()
        val isCustomEqualsSupported = LanguageFeature.CustomEqualsInValueClasses.isEnabled()

        declaration.constructors(context.session).forEach { innerDeclaration ->
            when {
                innerDeclaration.isPrimary -> {
                    primaryConstructor = innerDeclaration
                    primaryConstructorParametersByName = innerDeclaration.valueParameterSymbols.associateBy { it.name }
                    primaryConstructorParametersSymbolsSet = primaryConstructorParametersByName.values.toSet()
                }

                innerDeclaration.hasBody && !context.languageVersionSettings.supportsFeature(
                    LanguageFeature.ValueClassesSecondaryConstructorWithBody
                ) -> {
                    reporter.reportOn(
                        innerDeclaration.bodySource!!, FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS
                    )
                }
            }
        }
        declaration.processAllDeclarations(context.session) { innerDeclaration ->
            when (innerDeclaration) {
                is FirRegularClassSymbol -> {
                    if (innerDeclaration.isInner) {
                        reporter.reportOn(innerDeclaration.source, FirErrors.INNER_CLASS_INSIDE_VALUE_CLASS)
                    }
                }

                is FirPropertySymbol -> {
                    if (innerDeclaration.isRelatedToParameter(primaryConstructorParametersByName[innerDeclaration.name])) {
                        primaryConstructorPropertiesByName[innerDeclaration.name] = innerDeclaration
                    } else {
                        when {
                            innerDeclaration.delegate != null ->
                                reporter.reportOn(
                                    innerDeclaration.delegate!!.source,
                                    FirErrors.DELEGATED_PROPERTY_INSIDE_VALUE_CLASS
                                )

                            innerDeclaration.hasBackingField &&
                                    innerDeclaration.source?.kind !is KtFakeSourceElementKind -> {
                                reporter.reportOn(
                                    innerDeclaration.source, FirErrors.PROPERTY_WITH_BACKING_FIELD_INSIDE_VALUE_CLASS
                                )
                            }
                        }
                    }
                }

                else -> {}
            }
        }
        // Separate handling of delegate fields
        @OptIn(DirectDeclarationsAccess::class)
        declaration.declarations.forEach { innerDeclaration ->
            if (innerDeclaration !is FirField || !innerDeclaration.isSynthetic) return@forEach
            val symbol = innerDeclaration.initializer?.toResolvedCallableSymbol(context.session)
            if (symbol != null && symbol in primaryConstructorParametersSymbolsSet) {
                return@forEach
            }
            val delegatedTypeRefSource = (innerDeclaration.returnTypeRef as FirResolvedTypeRef).delegatedTypeRef?.source
            reporter.reportOn(
                delegatedTypeRefSource,
                FirErrors.VALUE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION
            )
        }

        val reservedNames = boxAndUnboxNames + if (isCustomEqualsSupported) emptySet() else equalsAndHashCodeNames
        val classScope = declaration.unsubstitutedScope()
        for (reservedName in reservedNames) {
            classScope.processFunctionsByName(Name.identifier(reservedName)) {
                val functionSymbol = it.unwrapFakeOverrides()
                if (functionSymbol.isAbstract) return@processFunctionsByName
                val containingClassSymbol = functionSymbol.getContainingClassSymbol() ?: return@processFunctionsByName
                if (containingClassSymbol == declaration.symbol) {
                    if (functionSymbol.source?.kind is KtRealSourceElementKind) {
                        reporter.reportOn(
                            functionSymbol.source,
                            FirErrors.RESERVED_MEMBER_INSIDE_VALUE_CLASS,
                            reservedName
                        )
                    }
                } else if (containingClassSymbol.classKind == ClassKind.INTERFACE) {
                    reporter.reportOn(
                        declaration.source,
                        FirErrors.RESERVED_MEMBER_FROM_INTERFACE_INSIDE_VALUE_CLASS,
                        containingClassSymbol.name.asString(),
                        reservedName
                    )
                }
            }
        }

        if (primaryConstructor?.source?.kind !is KtRealSourceElementKind) {
            reporter.reportOn(declaration.source, FirErrors.ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_VALUE_CLASS)
            return
        }

        if (LanguageFeature.ValueClasses.isEnabled()) {
            if (primaryConstructorParametersByName.isEmpty()) {
                reporter.reportOn(primaryConstructor.source, FirErrors.VALUE_CLASS_EMPTY_CONSTRUCTOR)
                return
            }
        } else if (primaryConstructorParametersByName.size != 1) {
            reporter.reportOn(primaryConstructor.source, FirErrors.INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE)
            return
        }

        for ((name, primaryConstructorParameter) in primaryConstructorParametersByName) {
            val parameterTypeRef = primaryConstructorParameter.resolvedReturnTypeRef
            when {
                primaryConstructorParameter.isNotFinalReadOnly(primaryConstructorPropertiesByName[name]) ->
                    reporter.reportOn(
                        primaryConstructorParameter.source,
                        FirErrors.VALUE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER
                    )

                parameterTypeRef.isInapplicableParameterType(context.session) -> {
                    reporter.reportOn(
                        parameterTypeRef.source,
                        FirErrors.VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE,
                        parameterTypeRef.coneType
                    )
                }

                parameterTypeRef.coneType.isRecursiveValueClassType(context.session) -> {
                    reporter.reportOn(
                        parameterTypeRef.source, FirErrors.VALUE_CLASS_CANNOT_BE_RECURSIVE
                    )
                }

                declaration.multiFieldValueClassRepresentation != null -> {
                    val defaultValue = primaryConstructorParameter.resolvedDefaultValue
                    if (defaultValue != null) {
                        // TODO, KT-50113: Fix when inline arguments are supported.
                        reporter.reportOn(
                            defaultValue.source,
                            FirErrors.MULTI_FIELD_VALUE_CLASS_PRIMARY_CONSTRUCTOR_DEFAULT_PARAMETER
                        )
                    }
                }
            }
        }

        if (isCustomEqualsSupported) {
            val (equalsFromAnyOverriding, typedEquals) = run {
                var equalsFromAnyOverriding: FirNamedFunctionSymbol? = null
                var typedEquals: FirNamedFunctionSymbol? = null
                declaration.processAllDeclarations(context.session) {
                    if (it !is FirNamedFunctionSymbol) {
                        return@processAllDeclarations
                    }
                    if (it.isEquals(context.session)) equalsFromAnyOverriding = it
                    if (it.isTypedEqualsInValueClass(context.session)) typedEquals = it
                }
                equalsFromAnyOverriding to typedEquals
            }
            if (typedEquals != null) {
                if (typedEquals.typeParameterSymbols.isNotEmpty()) {
                    reporter.reportOn(
                        typedEquals.source,
                        FirErrors.TYPE_PARAMETERS_NOT_ALLOWED
                    )
                }
                val singleParameterReturnTypeRef = typedEquals.valueParameterSymbols.single().resolvedReturnTypeRef
                if (singleParameterReturnTypeRef.coneType.typeArguments.any { !it.isStarProjection }) {
                    reporter.reportOn(singleParameterReturnTypeRef.source, FirErrors.TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS)
                }
            }

            if (equalsFromAnyOverriding != null && typedEquals == null) {
                reporter.reportOn(
                    equalsFromAnyOverriding.source,
                    FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS,
                    declaration.defaultType().replaceArgumentsWithStarProjections()
                )
            }
        }
    }