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

org.jetbrains.kotlin.resolve.AnnotationChecker.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
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

import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.*
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention
import org.jetbrains.kotlin.resolve.descriptorUtil.isRepeatableAnnotation
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils

class AnnotationChecker(private val additionalCheckers: Iterable) {

    fun check(annotated: KtAnnotated, trace: BindingTrace, descriptor: DeclarationDescriptor? = null) {
        val actualTargets = getActualTargetList(annotated, descriptor, trace)
        checkEntries(annotated.annotationEntries, actualTargets, trace)
        if (annotated is KtCallableDeclaration) {
            annotated.typeReference?.let { check(it, trace) }
            annotated.receiverTypeReference?.let { check(it, trace) }
        }
        if (annotated is KtTypeParameterListOwner && annotated is KtCallableDeclaration) {
            // TODO: support type parameter annotations for type parameters on classes and properties
            annotated.typeParameters.forEach { check(it, trace) }
        }
        if (annotated is KtTypeReference) {
            annotated.typeElement?.typeArgumentsAsTypes?.filterNotNull()?.forEach { check(it, trace) }
        }
        if (annotated is KtDeclarationWithBody) {
            // JetFunction or JetPropertyAccessor
            for (parameter in annotated.valueParameters) {
                if (!parameter.hasValOrVar()) {
                    check(parameter, trace)
                    if (annotated is KtFunctionLiteral) {
                        parameter.typeReference?.let { check(it, trace) }
                    }
                }
            }
        }
    }

    fun checkExpression(expression: KtExpression, trace: BindingTrace) {
        checkEntries(expression.getAnnotationEntries(), getActualTargetList(expression, null, trace), trace)
        if (expression is KtLambdaExpression) {
            for (parameter in expression.valueParameters) {
                parameter.typeReference?.let { check(it, trace) }
            }
        }
    }

    private fun checkEntries(entries: List, actualTargets: TargetList, trace: BindingTrace) {
        val entryTypesWithAnnotations = hashMapOf>()

        for (entry in entries) {
            checkAnnotationEntry(entry, actualTargets, trace)
            val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: continue
            val classDescriptor = TypeUtils.getClassDescriptor(descriptor.type) ?: continue

            val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget()
            val existingTargetsForAnnotation = entryTypesWithAnnotations.getOrPut(descriptor.type) { arrayListOf() }
            val duplicateAnnotation = useSiteTarget in existingTargetsForAnnotation
                                      || (existingTargetsForAnnotation.any { (it == null) != (useSiteTarget == null) })

            if (duplicateAnnotation && !classDescriptor.isRepeatableAnnotation()) {
                trace.report(Errors.REPEATED_ANNOTATION.on(entry));
            }

            existingTargetsForAnnotation.add(useSiteTarget)
        }
        additionalCheckers.forEach { it.checkEntries(entries, actualTargets.defaultTargets, trace) }
    }

    private fun checkAnnotationEntry(entry: KtAnnotationEntry, actualTargets: TargetList, trace: BindingTrace) {
        val applicableTargets = applicableTargetSet(entry, trace)
        val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget()

        fun check(targets: List) = targets.any {
            it in applicableTargets && (useSiteTarget == null || KotlinTarget.USE_SITE_MAPPING[useSiteTarget] == it)
        }

        fun checkUselessFunctionLiteralAnnotation() {
            // TODO: tests on different JetAnnotatedExpression (?!)
            if (KotlinTarget.FUNCTION !in applicableTargets) return
            val annotatedExpression = entry.parent as? KtAnnotatedExpression ?: return
            val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: return
            val retention = descriptor.type.constructor.declarationDescriptor?.getAnnotationRetention()
            if (retention == KotlinRetention.SOURCE) return

            val functionLiteralExpression = annotatedExpression.baseExpression as? KtLambdaExpression ?: return
            if (InlineUtil.isInlinedArgument(functionLiteralExpression.functionLiteral, trace.bindingContext, false)) {
                trace.report(Errors.NON_SOURCE_ANNOTATION_ON_INLINED_LAMBDA_EXPRESSION.on(entry))
            }
        }

        fun applicableWithUseSiteTarget() = useSiteTarget != null && actualTargets.onlyWithUseSiteTarget.any {
            it in applicableTargets && KotlinTarget.USE_SITE_MAPPING[useSiteTarget] == it
        }

        if (check(actualTargets.defaultTargets) || check(actualTargets.canBeSubstituted) || applicableWithUseSiteTarget()) {
            checkUselessFunctionLiteralAnnotation()
            return
        }

        if (useSiteTarget != null) {
            trace.report(Errors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET.on(
                    entry, actualTargets.defaultTargets.firstOrNull()?.description ?: "unidentified target", useSiteTarget.renderName))
        }
        else {
            trace.report(Errors.WRONG_ANNOTATION_TARGET.on(
                    entry, actualTargets.defaultTargets.firstOrNull()?.description ?: "unidentified target"))
        }
    }

    companion object {
        private fun applicableTargetSet(entry: KtAnnotationEntry, trace: BindingTrace): Set {
            val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: return KotlinTarget.DEFAULT_TARGET_SET
            // For descriptor with error type, all targets are considered as possible
            if (descriptor.type.isError) return KotlinTarget.ALL_TARGET_SET
            val classDescriptor = TypeUtils.getClassDescriptor(descriptor.type) ?: return KotlinTarget.DEFAULT_TARGET_SET
            return applicableTargetSet(classDescriptor) ?: KotlinTarget.DEFAULT_TARGET_SET
        }

        @JvmStatic fun applicableTargetSet(descriptor: AnnotationDescriptor): Set {
            val classDescriptor = descriptor.type.constructor.declarationDescriptor as? ClassDescriptor ?: return emptySet()
            return applicableTargetSet(classDescriptor) ?: KotlinTarget.DEFAULT_TARGET_SET
        }

        fun applicableTargetSet(classDescriptor: ClassDescriptor): Set? {
            val targetEntryDescriptor = classDescriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.target)
                                        ?: return null
            val valueArguments = targetEntryDescriptor.allValueArguments
            val valueArgument = valueArguments.entries.firstOrNull()?.value as? ArrayValue ?: return null
            return valueArgument.value.filterIsInstance().mapNotNull {
                KotlinTarget.valueOrNull(it.value.name.asString())
            }.toSet()
        }

        fun getDeclarationSiteActualTargetList(annotated: KtElement, descriptor: ClassDescriptor?, trace: BindingTrace):
                List {
            return getActualTargetList(annotated, descriptor, trace).defaultTargets
        }

        private fun DeclarationDescriptor?.hasBackingField(bindingTrace: BindingTrace)
                = (this as? PropertyDescriptor)?.let { bindingTrace.get(BindingContext.BACKING_FIELD_REQUIRED, it) } ?: false

        private fun getActualTargetList(annotated: KtElement, descriptor: DeclarationDescriptor?, trace: BindingTrace): TargetList {
            return when (annotated) {
                is KtClassOrObject ->
                    (descriptor as? ClassDescriptor)?.let { TargetList(KotlinTarget.classActualTargets(it)) } ?: TargetLists.T_CLASSIFIER
                is KtDestructuringDeclarationEntry -> TargetLists.T_LOCAL_VARIABLE
                is KtProperty -> {
                    if (annotated.isLocal)
                        TargetLists.T_LOCAL_VARIABLE
                    else if (annotated.parent is KtClassOrObject || annotated.parent is KtClassBody)
                        TargetLists.T_MEMBER_PROPERTY(descriptor.hasBackingField(trace), annotated.hasDelegate())
                    else
                        TargetLists.T_TOP_LEVEL_PROPERTY(descriptor.hasBackingField(trace), annotated.hasDelegate())
                }
                is KtParameter -> {
                    if (annotated.hasValOrVar())
                        TargetLists.T_VALUE_PARAMETER_WITH_VAL
                    else
                        TargetLists.T_VALUE_PARAMETER_WITHOUT_VAL
                }
                is KtConstructor<*> -> TargetLists.T_CONSTRUCTOR
                is KtFunction -> {
                    if (ExpressionTypingUtils.isFunctionExpression(descriptor))
                        TargetLists.T_FUNCTION_EXPRESSION
                    else if (annotated.isLocal)
                        TargetLists.T_LOCAL_FUNCTION
                    else if (annotated.parent is KtClassOrObject || annotated.parent is KtClassBody)
                        TargetLists.T_MEMBER_FUNCTION
                    else
                        TargetLists.T_TOP_LEVEL_FUNCTION
                }
                is KtPropertyAccessor -> if (annotated.isGetter) TargetLists.T_PROPERTY_GETTER else TargetLists.T_PROPERTY_SETTER
                is KtTypeReference -> TargetLists.T_TYPE_REFERENCE
                is KtFile -> TargetLists.T_FILE
                is KtTypeParameter -> TargetLists.T_TYPE_PARAMETER
                is KtTypeProjection ->
                    if (annotated.projectionKind == KtProjectionKind.STAR) TargetLists.T_STAR_PROJECTION else TargetLists.T_TYPE_PROJECTION
                is KtAnonymousInitializer -> TargetLists.T_INITIALIZER
                is KtDestructuringDeclaration -> TargetLists.T_DESTRUCTURING_DECLARATION
                is KtLambdaExpression -> TargetLists.T_FUNCTION_LITERAL
                is KtObjectLiteralExpression -> TargetLists.T_OBJECT_LITERAL
                is KtExpression -> TargetLists.T_EXPRESSION
                else -> TargetLists.EMPTY
            }
        }

        private object TargetLists {
            val T_CLASSIFIER = targetList(CLASS)

            val T_LOCAL_VARIABLE = targetList(LOCAL_VARIABLE) {
                onlyWithUseSiteTarget(PROPERTY_SETTER, VALUE_PARAMETER)
            }

            val T_DESTRUCTURING_DECLARATION = targetList(DESTRUCTURING_DECLARATION)

            fun TargetListBuilder.propertyTargets(backingField: Boolean, delegate: Boolean) {
                if (backingField) extraTargets(FIELD)
                if (delegate) {
                    onlyWithUseSiteTarget(VALUE_PARAMETER, PROPERTY_GETTER, PROPERTY_SETTER, FIELD)
                }
                else {
                    onlyWithUseSiteTarget(VALUE_PARAMETER, PROPERTY_GETTER, PROPERTY_SETTER)
                }
            }

            fun T_MEMBER_PROPERTY(backingField: Boolean, delegate: Boolean) =
                    targetList(if (backingField) MEMBER_PROPERTY_WITH_BACKING_FIELD
                               else if (delegate) MEMBER_PROPERTY_WITH_DELEGATE
                               else MEMBER_PROPERTY_WITHOUT_FIELD_OR_DELEGATE,
                               MEMBER_PROPERTY, PROPERTY) {
                        propertyTargets(backingField, delegate)
                    }

            fun T_TOP_LEVEL_PROPERTY(backingField: Boolean, delegate: Boolean) =
                    targetList(if (backingField) TOP_LEVEL_PROPERTY_WITH_BACKING_FIELD
                               else if (delegate) TOP_LEVEL_PROPERTY_WITH_DELEGATE
                               else TOP_LEVEL_PROPERTY_WITHOUT_FIELD_OR_DELEGATE,
                               TOP_LEVEL_PROPERTY, PROPERTY) {
                        propertyTargets(backingField, delegate)
                    }

            val T_PROPERTY_GETTER = targetList(PROPERTY_GETTER)
            val T_PROPERTY_SETTER = targetList(PROPERTY_SETTER)

            val T_VALUE_PARAMETER_WITHOUT_VAL = targetList(VALUE_PARAMETER)

            val T_VALUE_PARAMETER_WITH_VAL = targetList(VALUE_PARAMETER, PROPERTY, MEMBER_PROPERTY) {
                extraTargets(FIELD)
                onlyWithUseSiteTarget(PROPERTY_GETTER, PROPERTY_SETTER)
            }

            val T_FILE = targetList(FILE)

            val T_CONSTRUCTOR = targetList(CONSTRUCTOR)

            val T_LOCAL_FUNCTION = targetList(LOCAL_FUNCTION, FUNCTION) {
                onlyWithUseSiteTarget(VALUE_PARAMETER)
            }

            val T_MEMBER_FUNCTION = targetList(MEMBER_FUNCTION, FUNCTION) {
                onlyWithUseSiteTarget(VALUE_PARAMETER)
            }

            val T_TOP_LEVEL_FUNCTION = targetList(TOP_LEVEL_FUNCTION, FUNCTION) {
                onlyWithUseSiteTarget(VALUE_PARAMETER)
            }

            val T_EXPRESSION = targetList(EXPRESSION)

            val T_FUNCTION_LITERAL = targetList(LAMBDA_EXPRESSION, FUNCTION, EXPRESSION)

            val T_FUNCTION_EXPRESSION = targetList(ANONYMOUS_FUNCTION, FUNCTION, EXPRESSION)

            val T_OBJECT_LITERAL = targetList(OBJECT_LITERAL, CLASS, EXPRESSION)

            val T_TYPE_REFERENCE = targetList(TYPE) {
                onlyWithUseSiteTarget(VALUE_PARAMETER)
            }

            val T_TYPE_PARAMETER = targetList(TYPE_PARAMETER)

            val T_STAR_PROJECTION = targetList(STAR_PROJECTION)
            val T_TYPE_PROJECTION = targetList(TYPE_PROJECTION)

            val T_INITIALIZER = targetList(INITIALIZER)


            private fun targetList(vararg target: KotlinTarget, otherTargets: TargetListBuilder.() -> Unit = {}): TargetList {
                val builder = TargetListBuilder(*target)
                builder.otherTargets()
                return builder.build()
            }

            val EMPTY = targetList()

            private class TargetListBuilder(vararg val defaultTargets: KotlinTarget) {
                private var canBeSubstituted: List = listOf()
                private var onlyWithUseSiteTarget: List = listOf()

                fun extraTargets(vararg targets: KotlinTarget) {
                    canBeSubstituted = targets.toList()
                }

                fun onlyWithUseSiteTarget(vararg targets: KotlinTarget) {
                    onlyWithUseSiteTarget = targets.toList()
                }

                fun build() = TargetList(defaultTargets.toList(), canBeSubstituted, onlyWithUseSiteTarget)
            }
        }

        private class TargetList(
                val defaultTargets: List,
                val canBeSubstituted: List = emptyList(),
                val onlyWithUseSiteTarget: List = emptyList())
    }
}

interface AdditionalAnnotationChecker {
    fun checkEntries(entries: List, actualTargets: List, trace: BindingTrace)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy