From 9838f9c942a5dae5d7d3a23875a0bbbc24a85362 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 7 Aug 2015 16:27:59 +0300 Subject: [PATCH] Check deprecated annotations with use site targets --- .../validation/DeprecatedSymbolValidator.kt | 41 +++++++++++++++---- .../tests/deprecated/javaDeprecated.kt | 2 +- .../tests/deprecated/javaDeprecated.txt | 2 +- .../kotlin/builtins/KotlinBuiltIns.java | 14 ++++++- .../annotations/AnnotationUseSiteTarget.kt | 12 ++++++ .../descriptors/annotations/Annotations.kt | 8 ++++ 6 files changed, 69 insertions(+), 10 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/DeprecatedSymbolValidator.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/DeprecatedSymbolValidator.kt index d01f4347fdc..e9c1541b256 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/DeprecatedSymbolValidator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/DeprecatedSymbolValidator.kt @@ -22,6 +22,7 @@ import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.JetTokens @@ -29,6 +30,9 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.annotations.argumentValue +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.PROPERTY_GETTER +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.PROPERTY_SETTER +import org.jetbrains.kotlin.descriptors.annotations.Annotations public class DeprecatedSymbolValidator : SymbolUsageValidator { private val JAVA_DEPRECATED = FqName(javaClass().getName()) @@ -63,9 +67,10 @@ public class DeprecatedSymbolValidator : SymbolUsageValidator { } private fun DeclarationDescriptor.getDeprecatedAnnotation(): Pair? { - val ownAnnotation = getDeclaredDeprecatedAnnotation() + val ownAnnotation = getDeclaredDeprecatedAnnotation(AnnotationUseSiteTarget.getAssociatedUseSiteTarget(this)) if (ownAnnotation != null) return ownAnnotation to this + when (this) { is ConstructorDescriptor -> { val classDescriptor = getContainingDeclaration() @@ -74,17 +79,39 @@ public class DeprecatedSymbolValidator : SymbolUsageValidator { return classAnnotation to classDescriptor } is PropertyAccessorDescriptor -> { - val propertyDescriptor = getContainingDeclaration() - val propertyAnnotation = propertyDescriptor.getDeclaredDeprecatedAnnotation() - if (propertyAnnotation != null) - return propertyAnnotation to propertyDescriptor + val propertyDescriptor = correspondingProperty + + val target = if (this is PropertyGetterDescriptor) PROPERTY_GETTER else PROPERTY_SETTER + val accessorAnnotation = propertyDescriptor.getDeclaredDeprecatedAnnotation(target, false) + if (accessorAnnotation != null) + return accessorAnnotation to this + + val classDescriptor = containingDeclaration as? ClassDescriptor + if (classDescriptor != null && classDescriptor.isCompanionObject) { + val classAnnotation = classDescriptor.getDeclaredDeprecatedAnnotation() + if (classAnnotation != null) + return classAnnotation to classDescriptor + } } } return null } - private fun DeclarationDescriptor.getDeclaredDeprecatedAnnotation(): AnnotationDescriptor? { - return getAnnotations().findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated) ?: getAnnotations().findAnnotation(JAVA_DEPRECATED) + private fun DeclarationDescriptor.getDeclaredDeprecatedAnnotation( + target: AnnotationUseSiteTarget? = null, + findAnnotationsWithoutTarget: Boolean = true + ): AnnotationDescriptor? { + if (findAnnotationsWithoutTarget) { + val annotations = annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated) ?: annotations.findAnnotation(JAVA_DEPRECATED) + if (annotations != null) return annotations + } + + if (target != null) { + return Annotations.Companion.findUseSiteTargetedAnnotation(annotations, target, KotlinBuiltIns.FQ_NAMES.deprecated) + ?: Annotations.Companion.findUseSiteTargetedAnnotation(annotations, target, JAVA_DEPRECATED) + } + + return null } private fun createDeprecationDiagnostic(element: PsiElement, descriptor: DeclarationDescriptor, deprecated: AnnotationDescriptor): Diagnostic { diff --git a/compiler/testData/diagnostics/tests/deprecated/javaDeprecated.kt b/compiler/testData/diagnostics/tests/deprecated/javaDeprecated.kt index 9a2e45e95ed..71426e5c2df 100644 --- a/compiler/testData/diagnostics/tests/deprecated/javaDeprecated.kt +++ b/compiler/testData/diagnostics/tests/deprecated/javaDeprecated.kt @@ -11,6 +11,6 @@ public class A { // FILE: B.kt -class B(private @deprecated val foo: String) : A() { +class B(private @property:deprecated val foo: String) : A() { override fun getFoo(text: String): String = super.getFoo(text + foo) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/deprecated/javaDeprecated.txt b/compiler/testData/diagnostics/tests/deprecated/javaDeprecated.txt index bb61ae68a9e..ab8bd7bf115 100644 --- a/compiler/testData/diagnostics/tests/deprecated/javaDeprecated.txt +++ b/compiler/testData/diagnostics/tests/deprecated/javaDeprecated.txt @@ -9,7 +9,7 @@ kotlin.deprecated(value = "Deprecated in Java") public open class A { } internal final class B : A { - public constructor B(/*0*/ kotlin.deprecated() foo: kotlin.String) + public constructor B(/*0*/ foo: kotlin.String) kotlin.deprecated() private final val foo: kotlin.String public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ fun getFoo(/*0*/ text: kotlin.String): kotlin.String diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 900858c7b76..0f56a3bb650 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -1026,7 +1026,19 @@ public class KotlinBuiltIns { } private static boolean containsAnnotation(DeclarationDescriptor descriptor, FqName annotationClassFqName) { - return descriptor.getOriginal().getAnnotations().findAnnotation(annotationClassFqName) != null; + DeclarationDescriptor original = descriptor.getOriginal(); + Annotations annotations = original.getAnnotations(); + + if (annotations.findAnnotation(annotationClassFqName) != null) return true; + + AnnotationUseSiteTarget associatedUseSiteTarget = AnnotationUseSiteTarget.Companion.getAssociatedUseSiteTarget(descriptor); + if (associatedUseSiteTarget != null) { + if (annotations.findUseSiteTargetedAnnotation(associatedUseSiteTarget, annotationClassFqName) != null) { + return true; + } + } + + return false; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget.kt index 712eed83bcc..6fc0e66db8d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget.kt @@ -16,6 +16,8 @@ package org.jetbrains.kotlin.descriptors.annotations +import org.jetbrains.kotlin.descriptors.* + public enum class AnnotationUseSiteTarget(renderName: String? = null) { FIELD(), FILE(), @@ -27,4 +29,14 @@ public enum class AnnotationUseSiteTarget(renderName: String? = null) { SETTER_PARAMETER("sparam"); public val renderName: String = renderName ?: name().toLowerCase() + + public companion object { + public fun getAssociatedUseSiteTarget(descriptor: DeclarationDescriptor): AnnotationUseSiteTarget? = when (descriptor) { + is PropertyDescriptor -> PROPERTY + is ValueParameterDescriptor -> CONSTRUCTOR_PARAMETER + is PropertyGetterDescriptor -> PROPERTY_GETTER + is PropertySetterDescriptor -> PROPERTY_SETTER + else -> null + } + } } \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt index ad0528af08e..43bf3d702b4 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.descriptors.annotations +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -38,6 +39,13 @@ public interface Annotations : Iterable { } } + public fun findUseSiteTargetedAnnotation(target: AnnotationUseSiteTarget, fqName: FqName): AnnotationDescriptor? { + return getUseSiteTargetedAnnotations(target).firstOrNull { + val descriptor = it.type.constructor.declarationDescriptor + descriptor is ClassDescriptor && fqName.toUnsafe() == DescriptorUtils.getFqName(descriptor) + } + } + // Returns both targeted and annotations without target. Annotation order is preserved. public fun getAllAnnotations(): List