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

org.jetbrains.kotlin.resolve.jvm.checkers.JvmFieldApplicabilityChecker.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.resolve.jvm.checkers

import org.jetbrains.kotlin.JvmFieldApplicabilityProblem.*
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fileClasses.isInsideJvmMultifileClassFile
import org.jetbrains.kotlin.load.java.DescriptorsJvmAbiUtil
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmFieldAnnotation
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.resolve.jvm.isValueClassThatRequiresMangling
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope

class JvmFieldApplicabilityChecker : DeclarationChecker {
    override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
        if (descriptor !is PropertyDescriptor || (declaration !is KtProperty && declaration !is KtParameter)) return

        val annotation = descriptor.findJvmFieldAnnotation()
            ?: descriptor.delegateField?.annotations?.findAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME)
            ?: return

        val problem = when {
            declaration is KtProperty && declaration.hasDelegate() -> DELEGATE
            !descriptor.hasBackingField(context.trace.bindingContext) -> return
            descriptor.isOverridable -> NOT_FINAL
            DescriptorVisibilities.isPrivate(descriptor.visibility) -> PRIVATE
            declaration is KtProperty && declaration.hasCustomAccessor() -> CUSTOM_ACCESSOR
            descriptor.overriddenDescriptors.isNotEmpty() -> OVERRIDES
            descriptor.isLateInit -> LATEINIT
            descriptor.isConst -> CONST
            descriptor.isInsideCompanionObjectOfInterface() ->
                if (!context.languageVersionSettings.supportsFeature(LanguageFeature.JvmFieldInInterface))
                    INSIDE_COMPANION_OF_INTERFACE
                else {
                    if (!isInterfaceCompanionWithPublicJvmFieldProperties(descriptor.containingDeclaration as ClassDescriptor)) {
                        NOT_PUBLIC_VAL_WITH_JVMFIELD
                    } else return
                }
            DescriptorUtils.isTopLevelDeclaration(descriptor) && declaration.isInsideJvmMultifileClassFile() ->
                TOP_LEVEL_PROPERTY_OF_MULTIFILE_FACADE
            descriptor.returnType?.isValueClassThatRequiresMangling() == true -> RETURN_TYPE_IS_VALUE_CLASS
            else -> return
        }

        val annotationEntry = DescriptorToSourceUtils.getSourceFromAnnotation(annotation) ?: return

        val factory =
            if (declaration is KtParameter && !context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitJvmFieldOnOverrideFromInterfaceInPrimaryConstructor)) {
                ErrorsJvm.INAPPLICABLE_JVM_FIELD_WARNING
            } else {
                ErrorsJvm.INAPPLICABLE_JVM_FIELD
            }
        context.trace.report(factory.on(annotationEntry, problem.errorMessage))
    }

    private fun isInterfaceCompanionWithPublicJvmFieldProperties(companionObject: ClassDescriptor): Boolean {
        for (next in companionObject.unsubstitutedMemberScope.getContributedDescriptors(
            DescriptorKindFilter.VARIABLES, MemberScope.ALL_NAME_FILTER
        )) {
            if (next !is PropertyDescriptor) continue

            if (next.visibility != DescriptorVisibilities.PUBLIC || next.isVar || next.modality != Modality.FINAL) return false

            if (!DescriptorsJvmAbiUtil.hasJvmFieldAnnotation(next)) return false
        }
        return true
    }

    // This logic has been effectively used since 1.0. It'd be nice to call [PropertyAccessorDescriptor.isDefault] here instead,
    // but that would be a breaking change because in the following case:
    //
    //     @JvmField
    //     @get:Anno
    //     val foo = 42
    //
    // we'd start considering foo as having a custom getter and disallow the JvmField annotation on it
    private fun KtProperty.hasCustomAccessor(): Boolean =
        getter != null || setter != null

    private fun PropertyDescriptor.hasBackingField(bindingContext: BindingContext) =
        bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, this) ?: false
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy