diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt index 01d59d76c1d..58b98c7c080 100755 --- a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.asJava.findFacadeClass import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.java.model.elements.JeTypeElement +import org.jetbrains.kotlin.java.model.internal.getAnnotationsWithInherited import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.BindingContext @@ -190,14 +191,9 @@ internal class AnalysisContext(annotationsMap: MutableMap getAnnotation(annotationClass: Class): A? { return getAnnotationsByType(annotationClass, onlyFirst = true).firstOrNull() @@ -49,11 +50,11 @@ interface JeAnnotationOwner : Element { val annotationFqName = annotationClass.canonicalName - val allAnnotations = annotationOwner?.annotations?.filter { it.qualifiedName == annotationFqName } ?: return emptyList() + val allAnnotations = psi.getAnnotationsWithInherited().filter { it.qualifiedName == annotationFqName } if (allAnnotations.isEmpty()) return emptyList() val annotations = if (onlyFirst) listOf(allAnnotations.first()) else allAnnotations - + val annotationDeclarations = annotations.map { it to it.nameReferenceElement?.resolve() as? PsiClass }.filter { it.second != null } @Suppress("UNCHECKED_CAST") diff --git a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/JeElement.kt b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/JeElement.kt index 8ddcbac797f..9d576ffb521 100644 --- a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/JeElement.kt +++ b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/JeElement.kt @@ -19,6 +19,6 @@ package org.jetbrains.kotlin.java.model import com.intellij.psi.PsiElement import javax.lang.model.element.Element -abstract class JeElement : Element { - abstract val psi: PsiElement +interface JeElement : Element { + val psi: PsiElement } \ No newline at end of file diff --git a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeClassInitializerExecutableElement.kt b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeClassInitializerExecutableElement.kt index 9b9563b57dd..79146f5decb 100644 --- a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeClassInitializerExecutableElement.kt +++ b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeClassInitializerExecutableElement.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.java.model.types.JeNoneType import javax.lang.model.element.* import javax.lang.model.type.TypeMirror -class JeClassInitializerExecutableElement(override val psi: PsiClassInitializer) : JeElement(), +class JeClassInitializerExecutableElement(override val psi: PsiClassInitializer) : JeElement, ExecutableElement, JeNoAnnotations, JeModifierListOwner { val isStaticInitializer = psi.isStatic diff --git a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeMethodExecutableElement.kt b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeMethodExecutableElement.kt index 062177a5e50..80d3fbc2fd1 100644 --- a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeMethodExecutableElement.kt +++ b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeMethodExecutableElement.kt @@ -16,10 +16,7 @@ package org.jetbrains.kotlin.java.model.elements -import com.intellij.psi.PsiAnnotationMethod -import com.intellij.psi.PsiAnnotationOwner -import com.intellij.psi.PsiMethod -import com.intellij.psi.PsiModifier +import com.intellij.psi.* import com.intellij.psi.util.PsiTypesUtil import org.jetbrains.kotlin.java.model.* import org.jetbrains.kotlin.java.model.internal.isStatic @@ -29,8 +26,8 @@ import org.jetbrains.kotlin.java.model.types.toJeType import javax.lang.model.element.* import javax.lang.model.type.TypeMirror -class JeMethodExecutableElement(override val psi: PsiMethod) : JeElement(), ExecutableElement, JeModifierListOwner, JeAnnotationOwner { - override fun getEnclosingElement() = psi.containingClass?.let { JeTypeElement(it) } +class JeMethodExecutableElement(override val psi: PsiMethod) : JeElement, ExecutableElement, JeModifierListOwner, JeAnnotationOwner { + override fun getEnclosingElement() = psi.containingClass?.let(::JeTypeElement) override fun getSimpleName(): JeName { if (psi.isConstructor) return JeName.INIT @@ -41,7 +38,7 @@ class JeMethodExecutableElement(override val psi: PsiMethod) : JeElement(), Exec override fun getTypeParameters() = psi.typeParameters.map { JeTypeParameterElement(it, this) } - override fun getParameters() = psi.parameterList.parameters.map { JeVariableElement(it) } + override fun getParameters() = psi.parameterList.parameters.map(::JeVariableElement) override fun getDefaultValue(): AnnotationValue? { val annotationMethod = psi as? PsiAnnotationMethod ?: return null @@ -68,9 +65,6 @@ class JeMethodExecutableElement(override val psi: PsiMethod) : JeElement(), Exec override fun getEnclosedElements() = emptyList() - override val annotationOwner: PsiAnnotationOwner? - get() = psi.modifierList - override fun equals(other: Any?): Boolean { if (this === other) return true if (other?.javaClass != javaClass) return false diff --git a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JePackageElement.kt b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JePackageElement.kt index e4f98c6eeee..6734b974f09 100644 --- a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JePackageElement.kt +++ b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JePackageElement.kt @@ -26,7 +26,7 @@ import javax.lang.model.element.ElementKind import javax.lang.model.element.ElementVisitor import javax.lang.model.element.PackageElement -class JePackageElement(override val psi: PsiPackage) : JeElement(), PackageElement, JeModifierListOwner, JeNoAnnotations { +class JePackageElement(override val psi: PsiPackage) : JeElement, PackageElement, JeModifierListOwner, JeNoAnnotations { override fun getEnclosingElement() = null override fun getSimpleName() = JeName(psi.name) @@ -39,7 +39,7 @@ class JePackageElement(override val psi: PsiPackage) : JeElement(), PackageEleme override fun getQualifiedName() = JeName(psi.qualifiedName) - override fun getEnclosedElements() = psi.classes.map { JeTypeElement(it) } + override fun getEnclosedElements() = psi.classes.map(::JeTypeElement) override fun asType() = JePackageTypeMirror diff --git a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeTypeElement.kt b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeTypeElement.kt index 7888ad2b782..cde73172f0c 100644 --- a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeTypeElement.kt +++ b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeTypeElement.kt @@ -25,16 +25,13 @@ import org.jetbrains.kotlin.java.model.types.toJeType import javax.lang.model.element.* import javax.lang.model.type.TypeMirror -class JeTypeElement(override val psi: PsiClass) : JeElement(), TypeElement, JeAnnotationOwner, JeModifierListOwner { +class JeTypeElement(override val psi: PsiClass) : JeElement, TypeElement, JeAnnotationOwner, JeModifierListOwner { override fun getEnclosingElement(): Element? { psi.containingClass?.let { return JeTypeElement(it) } val javaFile = psi.containingFile as? PsiJavaFile ?: return null - return JavaPsiFacade.getInstance(psi.project).findPackage(javaFile.packageName)?.let { JePackageElement(it) } + return JavaPsiFacade.getInstance(psi.project).findPackage(javaFile.packageName)?.let(::JePackageElement) } - override val annotationOwner: PsiAnnotationOwner? - get() = psi.modifierList - override fun getSimpleName() = JeName(psi.name) override fun getQualifiedName() = JeName(psi.qualifiedName) diff --git a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeTypeParameterElement.kt b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeTypeParameterElement.kt index 79e9003bdb9..2cdfee1de8e 100644 --- a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeTypeParameterElement.kt +++ b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeTypeParameterElement.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.java.model.elements import com.intellij.psi.PsiAnnotationOwner +import com.intellij.psi.PsiModifierListOwner import com.intellij.psi.PsiTypeParameter import com.intellij.psi.util.PsiTypesUtil import org.jetbrains.kotlin.java.model.JeAnnotationOwner @@ -32,7 +33,7 @@ import javax.lang.model.element.TypeParameterElement class JeTypeParameterElement( override val psi: PsiTypeParameter, val parent: JeElement? -) : JeElement(), TypeParameterElement, JeAnnotationOwner, JeModifierListOwner { +) : JeElement, TypeParameterElement, JeAnnotationOwner, JeModifierListOwner { override fun getSimpleName() = JeName(psi.name) override fun getEnclosingElement() = parent @@ -49,9 +50,6 @@ class JeTypeParameterElement( override fun getGenericElement() = parent - override val annotationOwner: PsiAnnotationOwner? - get() = psi.modifierList - override fun equals(other: Any?): Boolean { if (this === other) return true if (other?.javaClass != javaClass) return false diff --git a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeVariableElement.kt b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeVariableElement.kt index 153eef1f9ed..05cc9e00fbf 100644 --- a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeVariableElement.kt +++ b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeVariableElement.kt @@ -26,7 +26,7 @@ import javax.lang.model.element.ElementKind import javax.lang.model.element.ElementVisitor import javax.lang.model.element.VariableElement -class JeVariableElement(override val psi: PsiVariable) : JeElement(), VariableElement, JeModifierListOwner, JeAnnotationOwner { +class JeVariableElement(override val psi: PsiVariable) : JeElement, VariableElement, JeModifierListOwner, JeAnnotationOwner { override fun getSimpleName() = JeName(psi.name) override fun getEnclosingElement(): JeElement? { @@ -35,7 +35,7 @@ class JeVariableElement(override val psi: PsiVariable) : JeElement(), VariableEl } val containingClass = (psi as? PsiMember)?.containingClass ?: PsiTreeUtil.getParentOfType(psi, PsiClass::class.java) - return containingClass?.let { JeTypeElement(it) } + return containingClass?.let(::JeTypeElement) } override fun getConstantValue() = psi.initializer?.calcConstantValue() @@ -52,9 +52,6 @@ class JeVariableElement(override val psi: PsiVariable) : JeElement(), VariableEl override fun getEnclosedElements() = emptyList() - override val annotationOwner: PsiAnnotationOwner? - get() = psi.modifierList - override fun equals(other: Any?): Boolean { if (this === other) return true if (other?.javaClass != javaClass) return false diff --git a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/internal/internalUtil.kt b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/internal/internalUtil.kt index 02ed09d4c72..b26a66b21fb 100644 --- a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/internal/internalUtil.kt +++ b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/internal/internalUtil.kt @@ -66,4 +66,33 @@ internal val PsiModifierListOwner.isStatic: Boolean internal val PsiModifierListOwner.isFinal: Boolean get() = hasModifierProperty(PsiModifier.FINAL) -fun PsiModifierListOwner.getJavaModifiers() = modifierList?.getJavaModifiers() ?: emptySet() \ No newline at end of file +fun PsiModifierListOwner.getJavaModifiers() = modifierList?.getJavaModifiers() ?: emptySet() + +fun PsiModifierListOwner.getAnnotationsWithInherited(): List { + val annotations = modifierList?.annotations?.toMutableList() ?: mutableListOf() + + if (this is PsiClass) { + var superClass = superClass + while (superClass != null) { + superClass.modifierList?.annotations?.let { superClassAnnotations -> + for (annotation in superClassAnnotations) { + // Do not add the inherited annotation from the superclass + // if the current class has an annotation with the same qualified name + if (!annotation.isInherited() + || annotations.any { it.qualifiedName == annotation.qualifiedName }) continue + annotations += annotation + } + } + + superClass = superClass.superClass + } + } + + return annotations +} + +private fun PsiAnnotation.isInherited(): Boolean { + val annotationClass = nameReferenceElement?.resolve() as? PsiClass ?: return false + val annotations = annotationClass.modifierList?.annotations ?: return false + return annotations.any { it.qualifiedName == "java.lang.annotation.Inherited" } +} \ No newline at end of file diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/ProcessorTests.kt b/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/ProcessorTests.kt index b5d4ea1d366..29d3329d782 100644 --- a/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/ProcessorTests.kt +++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/ProcessorTests.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.annotation.processing.test.processor +import org.jetbrains.kotlin.java.model.elements.JeAnnotationMirror import org.jetbrains.kotlin.java.model.elements.JeMethodExecutableElement import org.jetbrains.kotlin.java.model.elements.JeTypeElement import org.jetbrains.kotlin.java.model.elements.JeVariableElement @@ -70,6 +71,31 @@ class ProcessorTests : AbstractProcessorTest() { fun testDoesNotRun() = testShouldNotRun("DoesNotRun", "Anno") fun testDoesNotRun2() = testShouldNotRun("DoesNotRun2", "Anno") -} + fun testInheritedAnnotations() { + val handledClasses = mutableListOf() + test("InheritedAnnotations", "Anno") { set, roundEnv, env -> + assertEquals(1, set.size) + val annotatedElements = roundEnv.getElementsAnnotatedWith(set.first()) + for (element in annotatedElements) { + handledClasses += element.simpleName.toString() + } + val implAnnotations = annotatedElements.first { it.simpleName.toString() == "Impl" }.annotationMirrors + // Should contain the inherited annotation + assertEquals(1, implAnnotations.size) + assertEquals("Anno", (implAnnotations.first() as JeAnnotationMirror).psi.qualifiedName) + } + // IntfImpl should not be here. Annotations can be inherited only from superclasses (not interfaces). + assertEquals(listOf("Base", "Impl", "Intf"), handledClasses.sorted()) + } + + fun testInheritedAnnotationsOverridden() = test("InheritedAnnotationsOverridden", "Anno") { set, roundEnv, env -> + assertEquals(1, set.size) + val annotatedElements = roundEnv.getElementsAnnotatedWith(set.first()) + assertEquals(2, annotatedElements.size) + val implAnnotations = annotatedElements.first { it.simpleName.toString() == "Impl" }.annotationMirrors + assertEquals(1, implAnnotations.size) + assertEquals("Tom", implAnnotations.first().elementValues.values.first().value) + } +} \ No newline at end of file