KtLightElements: make light annotations lazier

Allow to get annotation list and to invoke `findAnnotation` without building delegate
Introduce KtLightNullabilityAnnotation which holds nullability information and is built
    before delegate is built
This commit is contained in:
Pavel V. Talanov
2017-04-24 17:37:58 +03:00
parent 03b68666e4
commit 0571c62943
25 changed files with 411 additions and 179 deletions
@@ -40,7 +40,7 @@ class KtLightClassForInterfaceDefaultImpls(classOrObject: KtClassOrObject)
override fun getTypeParameterList(): PsiTypeParameterList? = null
override fun getTypeParameters(): Array<PsiTypeParameter> = emptyArray()
override fun computeModifiers(): Array<String> = arrayOf(PsiModifier.PUBLIC, PsiModifier.STATIC, PsiModifier.FINAL)
override fun computeModifiers() = publicStaticFinal
override fun isInterface(): Boolean = false
override fun isDeprecated(): Boolean = false
@@ -58,3 +58,5 @@ class KtLightClassForInterfaceDefaultImpls(classOrObject: KtClassOrObject)
override fun getOwnInnerClasses() = emptyList<PsiClass>()
}
private val publicStaticFinal = setOf(PsiModifier.PUBLIC, PsiModifier.STATIC, PsiModifier.FINAL)
@@ -44,8 +44,8 @@ import org.jetbrains.kotlin.asJava.builder.LightClassData
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassDataProviderForClassOrObject
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightSimpleModifierList
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
import org.jetbrains.kotlin.asJava.elements.KtLightModifierListWithExplicitModifiers
import org.jetbrains.kotlin.asJava.elements.KtLightPsiReferenceList
import org.jetbrains.kotlin.asJava.hasInterfaceDefaultImpls
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
@@ -180,11 +180,11 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC
override fun getName(): String? = classOrObject.nameAsName?.asString()
private val _modifierList: PsiModifierList by lazyPub { KtLightModifierListWithExplicitModifiers(this@KtLightClassForSourceDeclaration, computeModifiers()) }
private val _modifierList: PsiModifierList by lazyPub { KtLightSimpleModifierList(this@KtLightClassForSourceDeclaration, computeModifiers()) }
override fun getModifierList(): PsiModifierList? = _modifierList
protected open fun computeModifiers(): Array<String> {
protected open fun computeModifiers(): Set<String> {
val psiModifiers = hashSetOf<String>()
// PUBLIC, PROTECTED, PRIVATE, ABSTRACT, FINAL
@@ -218,7 +218,7 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC
psiModifiers.add(PsiModifier.STATIC)
}
return psiModifiers.toTypedArray()
return psiModifiers
}
private fun isAbstract(): Boolean = classOrObject.hasModifier(ABSTRACT_KEYWORD) || isInterface
@@ -41,7 +41,7 @@ abstract class KtLightMemberImpl<out D : PsiMember>(
private val _modifierList by lazyPub {
if (lightMemberOrigin is LightMemberOriginForDeclaration)
KtLightModifierList(this, dummyDelegate?.modifierList)
KtLightMemberModifierList(this, dummyDelegate?.modifierList)
else clsDelegate.modifierList!!
}
@@ -64,8 +64,6 @@ abstract class KtLightMemberImpl<out D : PsiMember>(
override fun isDeprecated() = (clsDelegate as PsiDocCommentOwner).isDeprecated
}
private val visibilityModifiers = arrayOf(PsiModifier.PRIVATE, PsiModifier.PACKAGE_LOCAL, PsiModifier.PROTECTED, PsiModifier.PUBLIC)
internal fun getMemberOrigin(member: PsiMember): LightMemberOriginForDeclaration? {
if (member !is ClsRepositoryPsiElement<*>) return null
@@ -74,14 +72,11 @@ internal fun getMemberOrigin(member: PsiMember): LightMemberOriginForDeclaration
return stubElement.getUserData<LightElementOrigin>(ORIGIN) as? LightMemberOriginForDeclaration ?: return null
}
private val visibilityModifiers = arrayOf(PsiModifier.PRIVATE, PsiModifier.PACKAGE_LOCAL, PsiModifier.PROTECTED, PsiModifier.PUBLIC)
class KtLightModifierList(
private val owner: KtLightMember<*>,
private val dummyDelegate: PsiModifierList?
) : LightElement(owner.manager, KotlinLanguage.INSTANCE), PsiModifierList {
private val clsDelegate by lazyPub { owner.clsDelegate.modifierList!! }
private val _annotations by lazyPub { computeAnnotations(this, clsDelegate) }
private class KtLightMemberModifierList(
owner: KtLightMember<*>, private val dummyDelegate: PsiModifierList?
) : KtLightModifierList<KtLightMember<*>>(owner) {
override fun hasModifierProperty(name: String) = when {
name == PsiModifier.ABSTRACT && isImplementationInInterface() -> false
name == PsiModifier.DEFAULT && isImplementationInInterface() -> true
@@ -100,21 +95,6 @@ class KtLightModifierList(
private fun isImplementationInInterface()
= owner.containingClass.isInterface && owner is KtLightMethod && owner.kotlinOrigin?.hasBody() ?: false
override fun hasExplicitModifier(name: String) = hasModifierProperty(name)
override fun setModifierProperty(name: String, value: Boolean) = clsDelegate.setModifierProperty(name, value)
override fun checkSetModifierProperty(name: String, value: Boolean) = clsDelegate.checkSetModifierProperty(name, value)
override fun addAnnotation(qualifiedName: String) = clsDelegate.addAnnotation(qualifiedName)
override fun getApplicableAnnotations(): Array<out PsiAnnotation> = annotations
override fun getAnnotations(): Array<out PsiAnnotation> = _annotations.value
override fun findAnnotation(qualifiedName: String) = annotations.firstOrNull { it.qualifiedName == qualifiedName }
override fun getParent() = owner
override fun getText(): String? = ""
override fun getTextRange() = TextRange.EMPTY_RANGE
override fun copy(): PsiElement = KtLightModifierList(owner, dummyDelegate)
override fun getReferences() = PsiReference.EMPTY_ARRAY
override fun isEquivalentTo(another: PsiElement?) =
another is KtLightModifierList && owner == another.owner
override fun toString() = "Light modifier list of $owner"
override fun copy() = KtLightMemberModifierList(owner, dummyDelegate)
}
@@ -0,0 +1,150 @@
/*
* Copyright 2010-2016 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.asJava.elements
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightElement
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.descriptors.ClassDescriptor
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.AnnotationWithTarget
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.AnnotationChecker
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.source.getPsi
abstract class KtLightModifierList<out T : KtLightElement<KtModifierListOwner, PsiModifierListOwner>>(protected val owner: T)
: LightElement(owner.manager, KotlinLanguage.INSTANCE), PsiModifierList, KtLightElement<KtModifierList, PsiModifierList> {
override val clsDelegate by lazyPub { owner.clsDelegate.modifierList!! }
private val _annotations by lazyPub { computeAnnotations(this) }
override val kotlinOrigin: KtModifierList?
get() = owner.kotlinOrigin?.modifierList
override fun hasExplicitModifier(name: String) = hasModifierProperty(name)
override fun setModifierProperty(name: String, value: Boolean) = clsDelegate.setModifierProperty(name, value)
override fun checkSetModifierProperty(name: String, value: Boolean) = clsDelegate.checkSetModifierProperty(name, value)
override fun addAnnotation(qualifiedName: String) = clsDelegate.addAnnotation(qualifiedName)
override fun getApplicableAnnotations(): Array<out PsiAnnotation> = annotations
override fun getAnnotations(): Array<out PsiAnnotation> = _annotations.toTypedArray()
override fun findAnnotation(qualifiedName: String) = annotations.firstOrNull { it.qualifiedName == qualifiedName }
override fun getParent() = owner
override fun getText(): String? = ""
override fun getTextRange() = TextRange.EMPTY_RANGE
override fun getReferences() = PsiReference.EMPTY_ARRAY
override fun isEquivalentTo(another: PsiElement?) =
another is KtLightModifierList<*> && owner == another.owner
override fun toString() = "Light modifier list of $owner"
}
class KtLightSimpleModifierList(
owner: KtLightElement<KtModifierListOwner, PsiModifierListOwner>, private val modifiers: Set<String>
) : KtLightModifierList<KtLightElement<KtModifierListOwner, PsiModifierListOwner>>(owner) {
override fun hasModifierProperty(name: String) = name in modifiers
override fun copy() = KtLightSimpleModifierList(owner, modifiers)
}
private fun computeAnnotations(lightModifierList: KtLightModifierList<*>): List<PsiAnnotation> {
val annotationsForEntries = lightAnnotationsForEntries(lightModifierList)
val modifierListOwner = lightModifierList.parent
if (modifierListOwner is KtLightClassForSourceDeclaration && modifierListOwner.isAnnotationType) {
val sourceAnnotationNames = annotationsForEntries.mapTo(mutableSetOf()) { it.qualifiedName }
val specialAnnotationsOnAnnotationClass = modifierListOwner.clsDelegate.modifierList?.annotations.orEmpty().filter {
it.qualifiedName !in sourceAnnotationNames
}.map { KtLightNonSourceAnnotation(lightModifierList, it) }
return annotationsForEntries + specialAnnotationsOnAnnotationClass
}
if ((modifierListOwner is KtLightMember<*> && modifierListOwner !is KtLightFieldImpl.KtLightEnumConstant)
|| modifierListOwner is KtLightParameter) {
return annotationsForEntries +
@Suppress("UNCHECKED_CAST")
listOf(KtLightNullabilityAnnotation(modifierListOwner as KtLightElement<*, PsiModifierListOwner>, lightModifierList))
}
return annotationsForEntries
}
private fun lightAnnotationsForEntries(lightModifierList: KtLightModifierList<*>): List<KtLightAnnotationForSourceEntry> {
val lightModifierListOwner = lightModifierList.parent
val annotatedKtDeclaration = lightModifierListOwner.kotlinOrigin as? KtDeclaration
if (annotatedKtDeclaration == null || !annotatedKtDeclaration.isValid || !hasAnnotationsInSource(annotatedKtDeclaration)) {
return emptyList()
}
return getAnnotationDescriptors(annotatedKtDeclaration, lightModifierListOwner)
.mapNotNull { descriptor ->
val fqName = descriptor.type.constructor.declarationDescriptor?.fqNameUnsafe?.asString() ?: return@mapNotNull null
val entry = descriptor.source.getPsi() as? KtAnnotationEntry ?: return@mapNotNull null
Pair(fqName, entry)
}
.groupBy({ it.first }) { it.second }
.flatMap {
(fqName, entries) ->
entries.mapIndexed { index, entry ->
KtLightAnnotationForSourceEntry(fqName, entry, lightModifierList) {
lightModifierList.clsDelegate.annotations.filter { it.qualifiedName == fqName }.getOrNull(index)
?: KtLightNonExistentAnnotation(lightModifierList)
}
}
}
}
private fun getAnnotationDescriptors(declaration: KtDeclaration?, annotatedLightElement: KtLightElement<*, *>): List<AnnotationDescriptor> {
val descriptor = declaration?.let { LightClassGenerationSupport.getInstance(it.project).resolveToDescriptor(it) }
val annotatedDescriptor = when {
descriptor is ClassDescriptor && annotatedLightElement is KtLightMethod && annotatedLightElement.isConstructor -> descriptor.unsubstitutedPrimaryConstructor
descriptor !is PropertyDescriptor || annotatedLightElement !is KtLightMethod -> descriptor
annotatedLightElement.isGetter -> descriptor.getter
annotatedLightElement.isSetter -> descriptor.setter
else -> descriptor
} ?: return emptyList()
return annotatedDescriptor.annotations.getAllAnnotations().
filter { it.matches(annotatedLightElement) }.
map { it.annotation }
}
private fun hasAnnotationsInSource(declaration: KtDeclaration): Boolean {
if (declaration.annotationEntries.isNotEmpty()) {
return true
}
if (declaration is KtProperty) {
return declaration.accessors.any { hasAnnotationsInSource(it) }
}
return false
}
private fun AnnotationWithTarget.matches(annotated: KtLightElement<*, *>): Boolean {
if (annotated !is KtLightFieldImpl.KtLightFieldForDeclaration) return true
if (target == AnnotationUseSiteTarget.FIELD) return true
if (target != null) return false
val declarationSiteTargets = AnnotationChecker.applicableTargetSet(annotation)
return KotlinTarget.FIELD in declarationSiteTargets && KotlinTarget.PROPERTY !in declarationSiteTargets
}
@@ -1,96 +0,0 @@
/*
* Copyright 2010-2016 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.asJava.elements
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiAnnotationOwner
import com.intellij.psi.PsiModifierList
import com.intellij.psi.impl.light.LightModifierList
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.utils.indexOfFirst
abstract class KtLightModifierListWithExplicitModifiers(
private val owner: KtLightElement<*, *>,
modifiers: Array<String>
) : LightModifierList(owner.manager, KotlinLanguage.INSTANCE, *modifiers) {
abstract val delegate: PsiAnnotationOwner
private val _annotations by lazyPub { computeAnnotations(this, delegate) }
override fun getParent() = owner
override fun getAnnotations(): Array<out PsiAnnotation> = _annotations.value
override fun getApplicableAnnotations() = delegate.applicableAnnotations
override fun findAnnotation(@NonNls qualifiedName: String) = annotations.firstOrNull { it.qualifiedName == qualifiedName }
override fun addAnnotation(@NonNls qualifiedName: String) = delegate.addAnnotation(qualifiedName)
}
internal fun computeAnnotations(lightElement: PsiModifierList,
delegate: PsiAnnotationOwner): CachedValue<Array<out PsiAnnotation>> {
fun doCompute(): Array<PsiAnnotation> {
val delegateAnnotations = delegate.annotations
if (delegateAnnotations.isEmpty()) return emptyArray()
val lightOwner = lightElement.parent as? KtLightElement<*, *>
val declaration = lightOwner?.kotlinOrigin as? KtDeclaration
if (declaration != null && !declaration.isValid) return PsiAnnotation.EMPTY_ARRAY
val descriptor = declaration?.let { LightClassGenerationSupport.getInstance(lightElement.project).resolveToDescriptor(it) }
val annotatedDescriptor = when {
descriptor !is PropertyDescriptor || lightOwner !is KtLightMethod -> descriptor
lightOwner.isGetter -> descriptor.getter
lightOwner.isSetter -> descriptor.setter
else -> descriptor
}
val ktAnnotations = annotatedDescriptor?.annotations?.getAllAnnotations() ?: emptyList()
var nextIndex = 0
val result = delegateAnnotations
.map { clsAnnotation ->
val currentIndex = ktAnnotations.indexOfFirst(nextIndex) {
it.annotation.type.constructor.declarationDescriptor?.fqNameUnsafe?.asString() == clsAnnotation.qualifiedName
}
if (currentIndex >= 0) {
nextIndex = currentIndex + 1
val ktAnnotation = ktAnnotations[currentIndex]
val entry = ktAnnotation.annotation.source.getPsi() as? KtAnnotationEntry ?: return@map clsAnnotation
KtLightAnnotation(clsAnnotation, entry, lightElement)
}
else clsAnnotation
}
.toTypedArray()
return result
}
return CachedValuesManager.getManager(lightElement.project).createCachedValue<Array<out PsiAnnotation>>(
{ CachedValueProvider.Result.create(doCompute(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) },
false
)
}
@@ -19,10 +19,8 @@ package org.jetbrains.kotlin.asJava.elements;
import com.intellij.lang.Language;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -32,6 +30,7 @@ import org.jetbrains.kotlin.idea.KotlinLanguage;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
import java.util.Collections;
import java.util.List;
public class KtLightParameter extends LightParameter implements KtLightDeclaration<KtParameter, PsiParameter> {
@@ -54,12 +53,7 @@ public class KtLightParameter extends LightParameter implements KtLightDeclarati
this.method = method;
if (method.getLightMemberOrigin() instanceof LightMemberOriginForDeclaration) {
this.modifierList = new KtLightModifierListWithExplicitModifiers(this, ArrayUtil.EMPTY_STRING_ARRAY) {
@Override
public PsiAnnotationOwner getDelegate() {
return delegate.getModifierList();
}
};
this.modifierList = new KtLightSimpleModifierList(this, Collections.emptySet());
}
else {
this.modifierList = super.getModifierList();
@@ -19,7 +19,8 @@ package org.jetbrains.kotlin.asJava.elements
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
import com.intellij.util.IncorrectOperationException
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.idea.KotlinLanguage
@@ -33,18 +34,35 @@ import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.TypeUtils
class KtLightAnnotation(
override val clsDelegate: PsiAnnotation,
abstract class KtLightAbstractAnnotation(parent: PsiElement, computeDelegate: () -> PsiAnnotation) :
KtLightElementBase(parent), PsiAnnotation, KtLightElement<KtAnnotationEntry, PsiAnnotation> {
override val clsDelegate by lazyPub(computeDelegate)
override fun getNameReferenceElement() = clsDelegate.nameReferenceElement
override fun getOwner() = parent as? PsiAnnotationOwner
override fun getMetaData() = clsDelegate.metaData
override fun getParameterList() = clsDelegate.parameterList
}
class KtLightAnnotationForSourceEntry(
private val qualifiedName: String,
override val kotlinOrigin: KtAnnotationEntry,
private val owner: PsiAnnotationOwner
) : PsiAnnotation by clsDelegate, KtLightElement<KtAnnotationEntry, PsiAnnotation> {
parent: KtLightElement<*, *>,
computeDelegate: () -> PsiAnnotation
) : KtLightAbstractAnnotation(parent, computeDelegate) {
override fun getQualifiedName() = qualifiedName
open inner class LightExpressionValue<out D : PsiExpression>(
val delegate: D,
private val parent: PsiElement
) : PsiAnnotationMemberValue, PsiExpression by delegate {
val originalExpression: PsiElement? by lazyPub {
val nameAndValue = delegate.getStrictParentOfType<PsiNameValuePair>() ?: return@lazyPub null
val annotationEntry = this@KtLightAnnotation.kotlinOrigin
val annotationEntry = this@KtLightAnnotationForSourceEntry.kotlinOrigin
val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry)
val resolvedCall = annotationEntry.getResolvedCall(context) ?: return@lazyPub null
val annotationConstructor = resolvedCall.resultingDescriptor
@@ -73,10 +91,10 @@ class KtLightAnnotation(
else -> null
}
}
fun getConstantValue(): Any? {
val expression = originalExpression as? KtExpression ?: return null
val annotationEntry = this@KtLightAnnotation.kotlinOrigin
val annotationEntry = this@KtLightAnnotationForSourceEntry.kotlinOrigin
val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry)
return context[BindingContext.COMPILE_TIME_VALUE, expression]?.getValue(TypeUtils.NO_EXPECTED_TYPE)
}
@@ -102,7 +120,8 @@ class KtLightAnnotation(
val exprToReplace =
if (origin is KtCallExpression /*arrayOf*/) {
unwrapArray(origin.valueArguments)
} else {
}
else {
origin as? KtExpression
} ?: return this
exprToReplace.replace(KtPsiFactory(this).createExpression("\"${StringUtil.escapeStringCharacters(value)}\""))
@@ -114,7 +133,7 @@ class KtLightAnnotation(
inner class LightStringLiteral(
delegate: PsiLiteralExpression,
parent: PsiElement
): LightExpressionValue<PsiLiteralExpression>(delegate, parent), PsiLiteralExpression {
) : LightExpressionValue<PsiLiteralExpression>(delegate, parent), PsiLiteralExpression {
override fun getValue() = delegate.value
}
@@ -150,31 +169,75 @@ class KtLightAnnotation(
override fun isPhysical() = true
override fun getName() = null
override fun setName(newName: String) = throw IncorrectOperationException()
override fun getOwner() = owner
override fun findAttributeValue(name: String?) = clsDelegate.findAttributeValue(name)?.let { wrapAnnotationValue(it, this) }
override fun findDeclaredAttributeValue(name: String?) = clsDelegate.findDeclaredAttributeValue(name)?.let { wrapAnnotationValue(it, this) }
override fun getText() = kotlinOrigin.text ?: ""
override fun getTextRange() = kotlinOrigin.textRange ?: TextRange.EMPTY_RANGE
override fun getParent() = owner as? PsiElement
override fun getLanguage() = KotlinLanguage.INSTANCE
override fun delete() {
kotlinOrigin.delete()
}
override fun delete() = kotlinOrigin.delete()
override fun toString() = "@$qualifiedName"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || other::class.java != this::class.java) return false
return kotlinOrigin == (other as KtLightAnnotation).kotlinOrigin
return kotlinOrigin == (other as KtLightAnnotationForSourceEntry).kotlinOrigin
}
override fun hashCode() = kotlinOrigin.hashCode()
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?): T
= clsDelegate.setDeclaredAttributeValue(attributeName, value)
}
class KtLightNonSourceAnnotation(
parent: PsiElement, clsDelegate: PsiAnnotation
): KtLightAbstractAnnotation(parent, { clsDelegate }) {
override val kotlinOrigin: KtAnnotationEntry? get() = null
override fun getQualifiedName() = clsDelegate.qualifiedName
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?)
= clsDelegate.setDeclaredAttributeValue(attributeName, value)
override fun findAttributeValue(attributeName: String?) = clsDelegate.findAttributeValue(attributeName)
override fun findDeclaredAttributeValue(attributeName: String?) = clsDelegate.findDeclaredAttributeValue(attributeName)
}
class KtLightNonExistentAnnotation(parent: KtLightElement<*, *>) : KtLightElementBase(parent), PsiAnnotation {
override val kotlinOrigin get() = null
override fun toString() = this.javaClass.name
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
override fun getNameReferenceElement() = null
override fun findAttributeValue(attributeName: String?) = null
override fun getQualifiedName() = null
override fun getOwner() = parent as? PsiAnnotationOwner
override fun findDeclaredAttributeValue(attributeName: String?) = null
override fun getMetaData() = null
override fun getParameterList() = KtLightEmptyAnnotationParameterList(this)
}
class KtLightEmptyAnnotationParameterList(parent: PsiElement) : KtLightElementBase(parent), PsiAnnotationParameterList {
override val kotlinOrigin get() = null
override fun getAttributes(): Array<PsiNameValuePair> = emptyArray()
}
class KtLightNullabilityAnnotation(member: KtLightElement<*, PsiModifierListOwner>, parent: PsiElement) : KtLightAbstractAnnotation(parent, {
// searching for last because nullability annotations are generated after backend generates source annotations
member.clsDelegate.modifierList?.annotations?.findLast {
isNullabilityAnnotation(it.qualifiedName)
} ?: KtLightNonExistentAnnotation(member)
}) {
override val kotlinOrigin get() = null
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
override fun findAttributeValue(attributeName: String?) = null
override fun getQualifiedName(): String? = clsDelegate.qualifiedName
override fun findDeclaredAttributeValue(attributeName: String?) = null
}
private fun cannotModify(): Nothing = error("Cannot modify") // TODO: meaningful message?
internal fun isNullabilityAnnotation(qualifiedName: String?) = qualifiedName in backendNullabilityAnnotations
private val backendNullabilityAnnotations = arrayOf(Nullable::class.java.name, NotNull::class.java.name)
@@ -20,7 +20,7 @@ import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
@@ -33,7 +33,6 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration
import java.util.*
fun KtClassOrObject.toLightClass(): KtLightClass? = LightClassGenerationSupport.getInstance(project).getLightClass(this)
@@ -125,7 +124,7 @@ val PsiElement.unwrapped: PsiElement?
get() = when {
this is KtLightElement<*, *> -> kotlinOrigin
this is KtLightIdentifier -> origin
this is KtLightAnnotation.LightExpressionValue<*> -> originalExpression
this is KtLightAnnotationForSourceEntry.LightExpressionValue<*> -> originalExpression
else -> this
}
@@ -153,7 +152,7 @@ fun KtAnnotationEntry.toLightAnnotation(): PsiAnnotation? {
val ktDeclaration = getStrictParentOfType<KtModifierList>()?.parent as? KtDeclaration ?: return null
for (lightElement in ktDeclaration.toLightElements()) {
if (lightElement !is PsiModifierListOwner) continue
lightElement.modifierList?.annotations?.firstOrNull { it is KtLightAnnotation && it.kotlinOrigin == this }?.let { return it }
lightElement.modifierList?.annotations?.firstOrNull { it is KtLightAnnotationForSourceEntry && it.kotlinOrigin == this }?.let { return it }
}
return null
}
@@ -0,0 +1,10 @@
@kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE)
@kotlin.annotation.Target(allowedTargets = {kotlin.annotation.AnnotationTarget.TYPE_PARAMETER})
@kotlin.annotation.MustBeDocumented
@kotlin.annotation.Repeatable
@java.lang.annotation.Documented
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
@java.lang.annotation.Target({})
public @interface Anno {
int i();
}
@@ -0,0 +1,7 @@
// Anno
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.TYPE_PARAMETER)
@MustBeDocumented
@Repeatable
annotation class Anno(val i: Int)
@@ -0,0 +1,18 @@
public final class Annotations {
@p.R(s = "a")
@p.R(s = "b")
@p.R(s = "c")
public final void repeatables1() { /* compiled code */ }
@p.R(s = "a")
public final void repeatables2() { /* compiled code */ }
@p.R(s = "a")
@p.S(g = "b")
@p.R(s = "c")
@p.S(g = "D")
@p.R(s = "f")
public final void repeatables3() { /* compiled code */ }
public Annotations() { /* compiled code */ }
}
@@ -0,0 +1,30 @@
// p.Annotations
package p
class Annotations {
@R("a") @R("b") @R("c")
fun repeatables1() {
}
@R("a")
fun repeatables2() {
}
@R("a") @S("b") @R("c") @S("D") @R("f")
fun repeatables3() {
}
}
@Repeatable
@Retention(AnnotationRetention.SOURCE)
annotation class S(val g: String)
@Repeatable
@Retention(AnnotationRetention.SOURCE)
annotation class R(val s: String)
@@ -138,6 +138,12 @@ public class CompilerLightClassTestGenerated extends AbstractCompilerLightClassT
doTest(fileName);
}
@TestMetadata("SpecialAnnotationsOnAnnotationClass.kt")
public void testSpecialAnnotationsOnAnnotationClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/SpecialAnnotationsOnAnnotationClass.kt");
doTest(fileName);
}
@TestMetadata("VarArgs.kt")
public void testVarArgs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/VarArgs.kt");
@@ -164,6 +170,12 @@ public class CompilerLightClassTestGenerated extends AbstractCompilerLightClassT
doTest(fileName);
}
@TestMetadata("RepetableAnnotations.kt")
public void testRepetableAnnotations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/compilationErrors/RepetableAnnotations.kt");
doTest(fileName);
}
@TestMetadata("SameName.kt")
public void testSameName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/compilationErrors/SameName.kt");
@@ -21,7 +21,7 @@ import com.intellij.psi.PsiConstantEvaluationHelper
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiExpression
import com.intellij.psi.impl.ConstantExpressionEvaluator
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.psi.KtExpression
@@ -49,7 +49,7 @@ class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator {
throwExceptionOnOverflow: Boolean,
auxEvaluator: PsiConstantEvaluationHelper.AuxEvaluator?
): Any? {
if (expression !is KtLightAnnotation.LightExpressionValue<*>) return null
if (expression !is KtLightAnnotationForSourceEntry.LightExpressionValue<*>) return null
val expressionToCompute = expression.originalExpression ?: return null
return when (expressionToCompute) {
is KtExpression -> {
@@ -471,7 +471,7 @@ private fun copyModifierListItems(from: PsiModifierList, to: PsiModifierList, wi
}
}
for (annotation in from.annotations) {
val annotationName = annotation.qualifiedName!!
val annotationName = annotation.qualifiedName ?: continue
if (Retention::class.java.name != annotationName) {
to.addAnnotation(annotationName)
@@ -23,13 +23,15 @@ import com.intellij.psi.*
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.jetbrains.kotlin.asJava.LightClassTestCommon
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
import org.jetbrains.kotlin.asJava.builder.StubComputationTracker
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.idea.KotlinDaemonAnalyzerTestCase
import org.jetbrains.kotlin.idea.caches.resolve.LightClassLazinessChecker.Tracker.Level.*
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.IDELightClassConstructionContext
@@ -42,9 +44,12 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.MockLibraryUtil
import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.plugins.groovy.lang.psi.impl.stringValue
import org.junit.Assert
import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
abstract class AbstractIdeLightClassTest : KotlinLightCodeInsightFixtureTestCase() {
fun doTest(testDataPath: String) {
@@ -219,18 +224,54 @@ object LightClassLazinessChecker {
for ((field, lightFieldInfo) in fieldsToInfo) {
val delegate = (field as KtLightField).clsDelegate
assertEquals(fieldInfo(delegate), lightFieldInfo)
checkAnnotationConsistency(field)
}
for ((method, lightMethodInfo) in methodsToInfo) {
val delegate = (method as KtLightMethod).clsDelegate
assertEquals(methodInfo(delegate, lazinessMode), lightMethodInfo)
checkAnnotationConsistency(method)
method.parameterList.parameters.forEach {
checkAnnotationConsistency(it as KtLightParameter)
}
}
assertEquals(classInfo(lightClass.clsDelegate), classInfo)
checkAnnotationConsistency(lightClass)
innerClasses.forEach(LazinessInfo::checkConsistency)
}
}
private fun checkAnnotationConsistency(modifierListOwner: KtLightElement<*, PsiModifierListOwner>) {
if (modifierListOwner is KtLightClassForFacade) return
modifierListOwner.clsDelegate.modifierList!!.annotations.groupBy { delegateAnnotation ->
delegateAnnotation.qualifiedName!!
}.map {
(fqName, clsAnnotations) ->
val lightAnnotations = (modifierListOwner as? PsiModifierListOwner)?.modifierList?.annotations?.filter { it.qualifiedName == fqName }.orEmpty()
if (fqName != Nullable::class.java.name && fqName != NotNull::class.java.name) {
assertEquals(clsAnnotations.size, lightAnnotations.size, "Missing $fqName annotation")
}
else {
// having duplicating nullability annotations is fine
// see KtLightNullabilityAnnotation
assertTrue(lightAnnotations.isNotEmpty(), "Missing $fqName annotation")
}
clsAnnotations.zip(lightAnnotations).forEach {
(clsAnnotation, lightAnnotation) ->
assertNotNull(lightAnnotation!!.nameReferenceElement)
if (lightAnnotation is KtLightAbstractAnnotation) {
assertEquals(clsAnnotation.values(), lightAnnotation.values())
assertEquals(clsAnnotation, lightAnnotation.clsDelegate)
}
}
}
}
private fun PsiAnnotation.values() = parameterList.attributes.map { it.value.stringValue() }
private data class ClassInfo(
val fieldNames: Collection<String>,
val methodNames: Collection<String>,
@@ -247,6 +288,8 @@ object LightClassLazinessChecker {
)
private fun fieldInfo(field: PsiField) = with(field) {
modifierList?.annotations // check getting annotations list doesn't trigger exact resolve
FieldInfo(
name!!, PsiModifier.MODIFIERS.asList().filter { modifierList!!.hasModifierProperty(it) }
)
@@ -261,6 +304,8 @@ object LightClassLazinessChecker {
)
private fun methodInfo(method: PsiMethod, lazinessMode: Mode) = with(method) {
modifierList.annotations // check getting annotations list doesn't trigger exact resolve
MethodInfo(
name, relevantModifiers(lazinessMode),
isConstructor, method.parameterList.parametersCount, isVarArgs
@@ -138,6 +138,12 @@ public class IdeCompiledLightClassTestGenerated extends AbstractIdeCompiledLight
doTest(fileName);
}
@TestMetadata("SpecialAnnotationsOnAnnotationClass.kt")
public void testSpecialAnnotationsOnAnnotationClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/SpecialAnnotationsOnAnnotationClass.kt");
doTest(fileName);
}
@TestMetadata("VarArgs.kt")
public void testVarArgs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/VarArgs.kt");
@@ -138,6 +138,12 @@ public class IdeLightClassTestGenerated extends AbstractIdeLightClassTest {
doTest(fileName);
}
@TestMetadata("SpecialAnnotationsOnAnnotationClass.kt")
public void testSpecialAnnotationsOnAnnotationClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/SpecialAnnotationsOnAnnotationClass.kt");
doTest(fileName);
}
@TestMetadata("VarArgs.kt")
public void testVarArgs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/VarArgs.kt");
@@ -164,6 +170,12 @@ public class IdeLightClassTestGenerated extends AbstractIdeLightClassTest {
doTest(fileName);
}
@TestMetadata("RepetableAnnotations.kt")
public void testRepetableAnnotations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/compilationErrors/RepetableAnnotations.kt");
doTest(fileName);
}
@TestMetadata("SameName.kt")
public void testSameName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/compilationErrors/SameName.kt");
@@ -40,7 +40,7 @@ object PsiElementChecker {
}
private fun checkPsiElement(element: PsiElement) {
if (element !is KtLightElement<*, *> && element !is KtLightModifierList) return
if (element !is KtLightElement<*, *> && element !is KtLightModifierList<*>) return
if (element is PsiModifierListOwner) {
val modifierList = element.modifierList
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.annotation.processing
import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation
import org.jetbrains.kotlin.asJava.findFacadeClass
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
@@ -6,6 +6,7 @@ public final class Test {
@org.jetbrains.annotations.NotNull
public final java.lang.String getF()
@org.jetbrains.annotations.NotNull
@org.jetbrains.annotations.NotNull
public final void a()
@@ -20,7 +20,7 @@ import com.intellij.psi.*
import com.intellij.psi.PsiModifier.*
import com.intellij.psi.impl.PsiSubstitutorImpl
import com.intellij.psi.util.PsiTypesUtil
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry
import javax.lang.model.element.Modifier
private val HAS_DEFAULT by lazy {
@@ -54,7 +54,7 @@ private fun PsiModifierList.getJavaModifiers(): Set<Modifier> {
internal fun PsiExpression.calcConstantValue(evaluator: PsiConstantEvaluationHelper? = null): Any? {
return when (this) {
is PsiLiteral -> value
is KtLightAnnotation.LightExpressionValue<*> -> getConstantValue() ?: delegate.calcConstantValue(evaluator)
is KtLightAnnotationForSourceEntry.LightExpressionValue<*> -> getConstantValue() ?: delegate.calcConstantValue(evaluator)
is PsiExpression -> (evaluator ?: getConstantEvaluator(this)).computeConstantExpression(this)
else -> null
}
@@ -71,7 +71,7 @@ internal val PsiModifierListOwner.isFinal: Boolean
fun PsiModifierListOwner.getJavaModifiers() = modifierList?.getJavaModifiers() ?: emptySet()
fun PsiModifierListOwner.getAnnotationsWithInherited(): List<PsiAnnotation> {
val annotations = modifierList?.annotations?.toMutableList() ?: mutableListOf()
val annotations = modifierList?.annotations?.filter { it.qualifiedName != null }?.toMutableList() ?: mutableListOf()
if (this is PsiClass) {
var superClass = superClass
@@ -229,7 +229,7 @@ internal object KotlinConverter {
is KtContainerNode -> element.getExpression()?.let {
KotlinConverter.convertExpression(it, parentCallback, requiredType)
} ?: el<UExpression> { UastEmptyExpression }
is KtLightAnnotation.LightExpressionValue<*> -> {
is KtLightAnnotationForSourceEntry.LightExpressionValue<*> -> {
val expression = element.originalExpression
when (expression) {
is KtExpression -> KotlinConverter.convertExpression(expression, parentCallback, requiredType)
@@ -26,7 +26,7 @@ import com.intellij.psi.PsiNameIdentifierOwner
import com.intellij.spring.gutter.SpringClassAnnotator
import com.intellij.util.Function
import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
@@ -78,7 +78,7 @@ class KotlinSpringClassAnnotator : SpringClassAnnotator() {
}
// Workaround for SpringClassAnnotator
(getElementToProcess(psiElement) as? KtLightAnnotation)?.let { return super.collectNavigationMarkers(it, result) }
(getElementToProcess(psiElement) as? KtLightAnnotationForSourceEntry)?.let { return super.collectNavigationMarkers(it, result) }
super.collectNavigationMarkers(psiElement, result)
}
@@ -30,7 +30,7 @@ import com.intellij.spring.model.xml.beans.SpringPropertyDefinition
import com.intellij.testFramework.UsefulTestCase.assertSameElements
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.xml.DomUtil
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry
import org.jetbrains.kotlin.idea.completion.test.assertInstanceOf
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
@@ -47,7 +47,7 @@ private fun nameBean(element: PsiElement): String {
private fun nameProperty(element: PsiElement) = DomUtil.getDomElement(element).assertInstanceOf<SpringPropertyDefinition>().propertyName!!
private fun namePsi(element: PsiElement): String {
if (element is KtLightAnnotation) return namePsi(element.kotlinOrigin.getStrictParentOfType<KtModifierListOwner>()!!)
if (element is KtLightAnnotationForSourceEntry) return namePsi(element.kotlinOrigin.getStrictParentOfType<KtModifierListOwner>()!!)
return SymbolPresentationUtil.getSymbolPresentableText(element)
}