Kapt: Support inherited annotations

(cherry picked from commit 02a3e6b)
This commit is contained in:
Yan Zhulanow
2016-08-09 21:24:52 +03:00
committed by Yan Zhulanow
parent 9d340e1b83
commit 8c4fb0a709
14 changed files with 105 additions and 46 deletions
@@ -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<String, MutableList<Ps
fun analyzeDeclaration(declaration: PsiElement) {
if (declaration !is PsiModifierListOwner) return
//TODO support inherited annotations
val annotations = declaration.modifierList?.annotations
if (annotations != null) {
for (annotation in annotations) {
val fqName = annotation.qualifiedName ?: continue
mutableAnnotationsMap.getOrPut(fqName, { mutableListOf() }).add(declaration)
}
for (annotation in declaration.getAnnotationsWithInherited()) {
val fqName = annotation.qualifiedName ?: return
mutableAnnotationsMap.getOrPut(fqName, { mutableListOf() }).add(declaration)
}
if (declaration is PsiClass) {
@@ -74,7 +74,7 @@ class KotlinElements(val javaPsiFacade: JavaPsiFacade, val scope: GlobalSearchSc
override fun isDeprecated(e: Element?): Boolean {
val deprecated = ((e as? JeElement)?.psi as? PsiDocCommentOwner)?.isDeprecated ?: false
if (deprecated) return true
return (e as? JeAnnotationOwner)?.annotationOwner?.findAnnotation("java.lang.Deprecated") != null
return (e as? JeAnnotationOwner)?.psi?.modifierList?.findAnnotation("java.lang.Deprecated") != null
}
override fun getAllMembers(type: TypeElement) = (type as? JeTypeElement)?.getAllMembers() ?: emptyList()
@@ -0,0 +1,12 @@
@java.lang.annotation.Inherited
annotation class Anno
@Anno
open class Base
class Impl : Base()
@Anno
interface Intf
class IntfImpl : Intf
@@ -0,0 +1,9 @@
@Repeatable
@java.lang.annotation.Inherited
annotation class Anno(val name: String)
@Anno("Mary")
open class Base
@Anno("Tom")
class Impl : Base()
@@ -16,18 +16,19 @@
package org.jetbrains.kotlin.java.model
import com.intellij.psi.PsiAnnotationOwner
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiModifierListOwner
import org.jetbrains.kotlin.java.model.elements.JeAnnotationMirror
import org.jetbrains.kotlin.java.model.internal.KotlinAnnotationProxyMaker
import org.jetbrains.kotlin.java.model.internal.getAnnotationsWithInherited
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.Element
import java.lang.reflect.Array as RArray
interface JeAnnotationOwner : Element {
val annotationOwner: PsiAnnotationOwner?
interface JeAnnotationOwner : JeElement {
override val psi: PsiModifierListOwner
override fun getAnnotationMirrors() = annotationOwner?.annotations?.map { JeAnnotationMirror(it) } ?: emptyList()
override fun getAnnotationMirrors() = psi.getAnnotationsWithInherited().map(::JeAnnotationMirror)
override fun <A : Annotation> getAnnotation(annotationClass: Class<A>): 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")
@@ -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
}
@@ -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
@@ -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<Element>()
override val annotationOwner: PsiAnnotationOwner?
get() = psi.modifierList
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other?.javaClass != javaClass) return false
@@ -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
@@ -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)
@@ -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
@@ -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<Element>()
override val annotationOwner: PsiAnnotationOwner?
get() = psi.modifierList
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other?.javaClass != javaClass) return false
@@ -66,4 +66,33 @@ internal val PsiModifierListOwner.isStatic: Boolean
internal val PsiModifierListOwner.isFinal: Boolean
get() = hasModifierProperty(PsiModifier.FINAL)
fun PsiModifierListOwner.getJavaModifiers() = modifierList?.getJavaModifiers() ?: emptySet()
fun PsiModifierListOwner.getJavaModifiers() = modifierList?.getJavaModifiers() ?: emptySet()
fun PsiModifierListOwner.getAnnotationsWithInherited(): List<PsiAnnotation> {
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" }
}
@@ -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<String>()
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)
}
}