Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.resolve.AnnotationChecker.kt Maven / Gradle / Ivy
/*
* 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)
}