override fun check()

in compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ValueClassDeclarationChecker.kt [26:188]


    override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
        if (declaration !is KtClass) return
        if (descriptor !is ClassDescriptor || !descriptor.isInline && !descriptor.isValue) return
        if (descriptor.kind != ClassKind.CLASS) return

        val trace = context.trace

        val valueKeyword = declaration.modifierList?.getModifier(KtTokens.VALUE_KEYWORD)

        // The check cannot be done in ModifierCheckerCore, since `value` keyword is enabled by one of two features, not by both of
        // them simultaneously
        if (valueKeyword != null) {
            if (!context.languageVersionSettings.supportsFeature(LanguageFeature.JvmInlineValueClasses) &&
                !context.languageVersionSettings.supportsFeature(LanguageFeature.InlineClasses)
            ) {
                trace.report(
                    Errors.UNSUPPORTED_FEATURE.on(
                        valueKeyword, LanguageFeature.JvmInlineValueClasses to context.languageVersionSettings
                    )
                )
                return
            }
        }

        val inlineOrValueKeyword = declaration.modifierList?.getModifier(KtTokens.INLINE_KEYWORD) ?: valueKeyword
        require(inlineOrValueKeyword != null) { "Declaration of inline class must have 'inline' keyword" }

        if (descriptor.isInner || DescriptorUtils.isLocal(descriptor)) {
            trace.report(Errors.VALUE_CLASS_NOT_TOP_LEVEL.on(inlineOrValueKeyword))
            return
        }

        if (declaration.contextReceivers.isNotEmpty()) {
            @Suppress("DEPRECATION")
            val contextReceiverList = declaration.modifierList?.contextReceiverList
            requireNotNull(contextReceiverList) { "Declaration cannot have context receivers with no context receiver list" }
            trace.report(Errors.VALUE_CLASS_CANNOT_HAVE_CONTEXT_RECEIVERS.on(contextReceiverList))
        }

        val modalityModifier = declaration.modalityModifier()
        if (modalityModifier != null && descriptor.modality != Modality.FINAL) {
            trace.report(Errors.VALUE_CLASS_NOT_FINAL.on(modalityModifier))
            return
        }

        val primaryConstructor = declaration.primaryConstructor
        if (primaryConstructor == null) {
            trace.report(Errors.ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_VALUE_CLASS.on(inlineOrValueKeyword))
            return
        }

        if (context.languageVersionSettings.supportsFeature(LanguageFeature.ValueClasses)) {
            if (primaryConstructor.valueParameters.isEmpty()) {
                (primaryConstructor.valueParameterList ?: declaration).let {
                    trace.report(Errors.VALUE_CLASS_EMPTY_CONSTRUCTOR.on(it))
                    return
                }
            }
        } else if (primaryConstructor.valueParameters.size != 1) {
            (primaryConstructor.valueParameterList ?: declaration).let {
                trace.report(Errors.INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE.on(it))
                return
            }
        }

        var baseParametersOk = true
        val baseParameterTypes = descriptor.defaultType.substitutedUnderlyingTypes()

        for ((baseParameter, baseParameterType) in primaryConstructor.valueParameters zip baseParameterTypes) {
            if (!isParameterAcceptableForInlineClass(baseParameter)) {
                trace.report(Errors.VALUE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER.on(baseParameter))
                baseParametersOk = false
                continue
            }

            val baseParameterTypeReference = baseParameter.typeReference
            if (baseParameterType != null && baseParameterTypeReference != null) {
                if (!context.languageVersionSettings.supportsFeature(LanguageFeature.GenericInlineClassParameter) &&
                    (baseParameterType.isTypeParameter() || baseParameterType.isGenericArrayOfTypeParameter())
                ) {
                    trace.report(
                        Errors.UNSUPPORTED_FEATURE.on(
                            baseParameterTypeReference,
                            LanguageFeature.GenericInlineClassParameter to context.languageVersionSettings
                        )
                    )
                    baseParametersOk = false
                    continue
                }

                if (baseParameterType.isInapplicableParameterType()) {
                    trace.report(Errors.VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE.on(baseParameterTypeReference, baseParameterType))
                    baseParametersOk = false
                    continue
                }

                if (baseParameterType.isRecursiveInlineOrValueClassType()) {
                    trace.report(Errors.VALUE_CLASS_CANNOT_BE_RECURSIVE.on(baseParameterTypeReference))
                    baseParametersOk = false
                    continue
                }

                if (descriptor.isMultiFieldValueClass() && baseParameter.defaultValue != null) {
                    // todo fix when inline arguments are supported
                    trace.report(Errors.MULTI_FIELD_VALUE_CLASS_PRIMARY_CONSTRUCTOR_DEFAULT_PARAMETER.on(baseParameter.defaultValue!!))
                }
            }
        }
        if (!baseParametersOk) {
            return
        }

        for (supertypeEntry in declaration.superTypeListEntries) {
            val typeReference = supertypeEntry.typeReference ?: continue
            val type = trace[BindingContext.TYPE, typeReference] ?: continue
            if (supertypeEntry is KtDelegatedSuperTypeEntry) {
                val resolvedCall = supertypeEntry.delegateExpression.getResolvedCall(trace.bindingContext) ?: continue
                if (!context.languageVersionSettings.supportsFeature(LanguageFeature.InlineClassImplementationByDelegation) ||
                    resolvedCall.resultingDescriptor !is ValueParameterDescriptor ||
                    resolvedCall.resultingDescriptor.containingDeclaration != trace.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, primaryConstructor]
                ) {
                    trace.report(Errors.VALUE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION.on(supertypeEntry))
                    return
                }
            } else {
                val typeDescriptor = type.constructor.declarationDescriptor ?: continue
                if (!DescriptorUtils.isInterface(typeDescriptor)) {
                    trace.report(Errors.VALUE_CLASS_CANNOT_EXTEND_CLASSES.on(typeReference))
                    return
                }
            }
        }

        if (descriptor.getAllSuperClassifiers().any {
                it.fqNameUnsafe == StandardNames.FqNames.cloneable || it.fqNameUnsafe == javaLangCloneable
            }
        ) {
            trace.report(Errors.VALUE_CLASS_CANNOT_BE_CLONEABLE.on(inlineOrValueKeyword))
            return
        }

        fun getFunctionDescriptor(declaration: KtNamedFunction): SimpleFunctionDescriptor? =
            context.trace.bindingContext.get(BindingContext.FUNCTION, declaration)

        fun isUntypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.overridesEqualsFromAny() ?: false
        fun isTypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.isTypedEqualsInValueClass() ?: false
        fun KtClass.namedFunctions() = declarations.filterIsInstance<KtNamedFunction>()

        if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInValueClasses)) {
            val typedEquals = declaration.namedFunctions().firstOrNull { isTypedEquals(it) }

            declaration.namedFunctions().singleOrNull { isUntypedEquals(it) }?.apply {
                if (typedEquals == null) {
                    trace.report(
                        Errors.INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS.on(
                            this@apply,
                            descriptor.defaultType.replaceArgumentsWithStarProjections()
                        )
                    )
                }
            }
        }
    }