Kotlin meta-annotation converter for UL-annotations

This commit is contained in:
Igor Yakovlev
2019-06-25 20:14:41 +03:00
parent afe784e79e
commit eea469939e
7 changed files with 315 additions and 114 deletions
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation
import org.jetbrains.kotlin.asJava.elements.psiType
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtCallElement
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.constants.*
@@ -52,13 +53,22 @@ class KtUltraLightNullabilityAnnotation(
}
}
fun AnnotationDescriptor.toLightAnnotation(ultraLightSupport: KtUltraLightSupport, parent: PsiElement) =
KtUltraLightSimpleAnnotation(
fqName?.asString(),
allValueArguments.map { it.key.asString() to it.value },
ultraLightSupport,
parent
)
fun DeclarationDescriptor.obtainLightAnnotations(
ultraLightSupport: KtUltraLightSupport,
parent: PsiElement
): List<KtLightAbstractAnnotation> = annotations.map { KtUltraLightAnnotationForDescriptor(it, ultraLightSupport, parent) }
): List<KtLightAbstractAnnotation> = annotations.map { it.toLightAnnotation(ultraLightSupport, parent) }
class KtUltraLightAnnotationForDescriptor(
private val annotationDescriptor: AnnotationDescriptor,
class KtUltraLightSimpleAnnotation(
private val annotationFqName: String?,
private val argumentsList: List<Pair<String, ConstantValue<*>>>,
private val ultraLightSupport: KtUltraLightSupport,
parent: PsiElement
) : KtLightAbstractAnnotation(parent, computeDelegate = null) {
@@ -80,12 +90,12 @@ class KtUltraLightAnnotationForDescriptor(
override fun findDeclaredAttributeValue(attributeName: String?) =
PsiImplUtil.findDeclaredAttributeValue(this, attributeName)
override fun getQualifiedName() = annotationDescriptor.fqName?.asString()
override fun getQualifiedName() = annotationFqName
private inner class ParameterListImpl : KtLightElementBase(this@KtUltraLightAnnotationForDescriptor), PsiAnnotationParameterList {
private inner class ParameterListImpl : KtLightElementBase(this@KtUltraLightSimpleAnnotation), PsiAnnotationParameterList {
private val _attributes: Array<PsiNameValuePair> by lazyPub {
annotationDescriptor.allValueArguments.map {
PsiNameValuePairForAnnotationArgument(it.key.asString(), it.value, ultraLightSupport, this)
argumentsList.map {
PsiNameValuePairForAnnotationArgument(it.first, it.second, ultraLightSupport, this)
}.toTypedArray()
}
@@ -124,7 +134,7 @@ private fun ConstantValue<*>.toAnnotationMemberValue(
parent: PsiElement, ultraLightSupport: KtUltraLightSupport
): PsiAnnotationMemberValue? = when (this) {
is AnnotationValue -> KtUltraLightAnnotationForDescriptor(value, ultraLightSupport, parent)
is AnnotationValue -> value.toLightAnnotation(ultraLightSupport, parent)
is ArrayValue ->
KtUltraLightPsiArrayInitializerMemberValue(lightParent = parent) { arrayLiteralParent ->
@@ -43,15 +43,16 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
private class KtUltraLightClassModifierList(
private val containingClass: KtLightClassForSourceDeclaration,
private val support: KtUltraLightSupport,
private val computeModifiers: () -> Set<String>
) :
KtUltraLightModifierList<KtLightClassForSourceDeclaration>(containingClass) {
KtUltraLightModifierList<KtLightClassForSourceDeclaration>(containingClass, support) {
private val modifiers by lazyPub { computeModifiers() }
override fun hasModifierProperty(name: String): Boolean =
if (name != PsiModifier.FINAL) name in modifiers else owner.isFinal(PsiModifier.FINAL in modifiers)
override fun copy(): PsiElement = KtUltraLightClassModifierList(containingClass, computeModifiers)
override fun copy(): PsiElement = KtUltraLightClassModifierList(containingClass, support, computeModifiers)
}
@@ -90,7 +91,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
override fun getDelegate(): PsiClass = forTooComplex { super.getDelegate() }
private val _modifierList: PsiModifierList? by lazyPub {
if (tooComplex) super.getModifierList() else KtUltraLightClassModifierList(this) { computeModifiers() }
if (tooComplex) super.getModifierList() else KtUltraLightClassModifierList(this, support) { computeModifiers() }
}
override fun getModifierList(): PsiModifierList? = _modifierList
@@ -101,9 +102,12 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
private fun mapSupertype(supertype: KotlinType) =
supertype.asPsiType(support, TypeMappingMode.SUPER_TYPE, this) as? PsiClassType
override fun createExtendsList(): PsiReferenceList? =
if (tooComplex) super.createExtendsList()
else KotlinSuperTypeListBuilder(
override fun createExtendsList(): PsiReferenceList? {
if (isAnnotationType) return KotlinLightReferenceListBuilder(manager, language, PsiReferenceList.Role.EXTENDS_LIST)
if (tooComplex) return super.createExtendsList()
return KotlinSuperTypeListBuilder(
kotlinOrigin.getSuperTypeList(),
manager,
language,
@@ -114,6 +118,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
.map(this::mapSupertype)
.forEach(list::addReference)
}
}
private fun isTypeForExtendsList(supertype: KotlinType): Boolean {
// Do not add redundant "extends java.lang.Object" anywhere
@@ -128,9 +133,13 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
return !JvmCodegenUtil.isJvmInterface(supertype)
}
override fun createImplementsList(): PsiReferenceList? =
if (tooComplex) super.createImplementsList()
else KotlinSuperTypeListBuilder(
override fun createImplementsList(): PsiReferenceList? {
if (isAnnotationType) return KotlinLightReferenceListBuilder(manager, language, PsiReferenceList.Role.IMPLEMENTS_LIST)
if (tooComplex) return super.createImplementsList()
return KotlinSuperTypeListBuilder(
kotlinOrigin.getSuperTypeList(),
manager,
language,
@@ -143,6 +152,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
.forEach(list::addReference)
}
}
}
override fun buildTypeParameterList(): PsiTypeParameterList =
if (tooComplex) super.buildTypeParameterList() else buildTypeParameterList(classOrObject, this, support)
@@ -35,7 +35,7 @@ private class KtUltraLightSimpleModifierListField(
private val declaration: KtNamedDeclaration,
owner: KtLightElement<KtModifierListOwner, PsiModifierListOwner>,
private val modifiers: Set<String>
) : KtUltraLightSimpleModifierList(owner, modifiers) {
) : KtUltraLightSimpleModifierList(owner, modifiers, support) {
override fun hasModifierProperty(name: String): Boolean = when (name) {
PsiModifier.VOLATILE -> hasFieldAnnotation(VOLATILE_ANNOTATION_FQ_NAME)
PsiModifier.TRANSIENT -> hasFieldAnnotation(TRANSIENT_ANNOTATION_FQ_NAME)
@@ -11,6 +11,7 @@ import com.intellij.psi.impl.LanguageConstantExpressionEvaluator
import com.intellij.psi.impl.light.LightIdentifier
import com.intellij.psi.impl.light.LightTypeElement
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.cannotModify
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.name.FqNameUnsafe
@@ -23,7 +24,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class KtLightPsiArrayInitializerMemberValue(
override val kotlinOrigin: KtElement,
val lightParent: PsiElement,
private val lightParent: PsiElement,
private val arguments: (KtLightPsiArrayInitializerMemberValue) -> List<PsiAnnotationMemberValue>
) : KtLightElementBase(lightParent), PsiArrayInitializerMemberValue {
override fun getInitializers(): Array<PsiAnnotationMemberValue> = arguments(this).toTypedArray()
@@ -35,7 +36,7 @@ class KtLightPsiArrayInitializerMemberValue(
open class KtLightPsiLiteral(
override val kotlinOrigin: KtExpression,
val lightParent: PsiElement
private val lightParent: PsiElement
) : KtLightElementBase(lightParent), PsiLiteralExpression {
override fun getValue(): Any? =
@@ -106,31 +107,24 @@ internal fun psiType(kotlinFqName: String, context: PsiElement, boxPrimitiveType
return PsiType.getTypeByName(javaFqName, context.project, context.resolveScope)
}
class KtLightPsiNameValuePair private constructor(
class KtLightPsiNameValuePair(
override val kotlinOrigin: KtElement,
val valueArgument: KtValueArgument,
private val name: String,
lightParent: PsiElement,
private val argument: (KtLightPsiNameValuePair) -> PsiAnnotationMemberValue?
) : KtLightElementBase(lightParent),
PsiNameValuePair {
constructor(
valueArgument: KtValueArgument,
lightParent: PsiElement,
argument: (KtLightPsiNameValuePair) -> PsiAnnotationMemberValue?
) : this(valueArgument.asElement(), valueArgument, lightParent, argument)
override fun setValue(newValue: PsiAnnotationMemberValue): PsiAnnotationMemberValue = cannotModify()
override fun setValue(newValue: PsiAnnotationMemberValue): PsiAnnotationMemberValue =
throw UnsupportedOperationException("can't modify KtLightPsiNameValuePair")
override fun getNameIdentifier(): PsiIdentifier? = LightIdentifier(kotlinOrigin.manager, name)
override fun getNameIdentifier(): PsiIdentifier? = LightIdentifier(kotlinOrigin.manager, valueArgument.name)
override fun getName(): String? = valueArgument.getArgumentName()?.asName?.asString()
override fun getName(): String? = name
private val _value: PsiAnnotationMemberValue? by lazyPub { argument(this) }
override fun getValue(): PsiAnnotationMemberValue? = _value
override fun getLiteralValue(): String? = (getValue() as? PsiLiteralExpression)?.value?.toString()
override fun getLiteralValue(): String? = (value as? PsiLiteralExpression)?.value?.toString()
}
@@ -1,33 +1,15 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.asJava.elements
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiModifierList
import com.intellij.psi.PsiModifierListOwner
import com.intellij.psi.*
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.asJava.classes.KtUltraLightElementWithNullabilityAnnotation
import org.jetbrains.kotlin.asJava.classes.KtUltraLightNullabilityAnnotation
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.asJava.toLightAnnotation
import org.jetbrains.kotlin.asJava.classes.*
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
@@ -67,24 +49,29 @@ abstract class KtLightModifierList<out T : KtLightElement<KtModifierListOwner, P
override fun toString() = "Light modifier list of $owner"
protected open fun nonSourceAnnotations(sourceAnnotationNames: Set<String>): List<KtLightAbstractAnnotation> {
protected open fun nonSourceAnnotationsForAnnotationType(sourceAnnotations: List<PsiAnnotation>): List<KtLightAbstractAnnotation> {
val annotations = parent.clsDelegate.modifierList?.annotations
if (annotations.isNullOrEmpty()) return emptyList()
return annotations
.filter { it.qualifiedName !in sourceAnnotationNames }
.map { KtLightNonSourceAnnotation(this, it) }
return annotations.map { KtLightNonSourceAnnotation(this, it) }
}
private fun computeAnnotations(): List<KtLightAbstractAnnotation> {
val annotationsForEntries =
owner.givenAnnotations ?: lightAnnotationsForEntries(this)
val annotationsForEntries = owner.givenAnnotations ?: lightAnnotationsForEntries(this)
val modifierListOwner = parent
if (modifierListOwner is KtLightClassForSourceDeclaration && modifierListOwner.isAnnotationType) {
val sourceAnnotationNames = annotationsForEntries.mapNotNullTo(mutableSetOf()) { it.qualifiedName }
return annotationsForEntries + nonSourceAnnotations(sourceAnnotationNames)
val nonSourceAnnotations = nonSourceAnnotationsForAnnotationType(annotationsForEntries)
val filteredNonSourceAnnotations = nonSourceAnnotations.filter { nonSourceAnnotation ->
annotationsForEntries.all { sourceAnnotation ->
nonSourceAnnotation.qualifiedName != sourceAnnotation.qualifiedName
}
}
return annotationsForEntries + filteredNonSourceAnnotations
}
if ((modifierListOwner is KtLightMember<*> && modifierListOwner !is KtLightFieldImpl.KtLightEnumConstant)
|| modifierListOwner is LightParameter
@@ -98,19 +85,22 @@ abstract class KtLightModifierList<out T : KtLightElement<KtModifierListOwner, P
}
return annotationsForEntries
}
}
open class KtUltraLightSimpleModifierList(
owner: KtLightElement<KtModifierListOwner, PsiModifierListOwner>, private val modifiers: Set<String>
) : KtUltraLightModifierList<KtLightElement<KtModifierListOwner, PsiModifierListOwner>>(owner) {
owner: KtLightElement<KtModifierListOwner, PsiModifierListOwner>,
private val modifiers: Set<String>,
private val support: KtUltraLightSupport
) : KtUltraLightModifierList<KtLightElement<KtModifierListOwner, PsiModifierListOwner>>(owner, support) {
override fun hasModifierProperty(name: String) = name in modifiers
override fun copy() = KtUltraLightSimpleModifierList(owner, modifiers)
override fun copy() = KtUltraLightSimpleModifierList(owner, modifiers, support)
}
abstract class KtUltraLightModifierList<out T : KtLightElement<KtModifierListOwner, PsiModifierListOwner>>(owner: T) :
KtLightModifierList<T>(owner) {
abstract class KtUltraLightModifierList<out T : KtLightElement<KtModifierListOwner, PsiModifierListOwner>>(
owner: T,
private val support: KtUltraLightSupport
) : KtLightModifierList<T>(owner) {
override val clsDelegate: PsiModifierList
get() = throw IllegalStateException("Cls delegate shouldn't be loaded for ultra-light PSI!")
@@ -123,8 +113,22 @@ abstract class KtUltraLightModifierList<out T : KtLightElement<KtModifierListOwn
override fun addAnnotation(qualifiedName: String): PsiAnnotation = throwInvalidOperation()
override fun nonSourceAnnotations(sourceAnnotationNames: Set<String>): List<KtLightAbstractAnnotation> {
return emptyList()
override fun nonSourceAnnotationsForAnnotationType(sourceAnnotations: List<PsiAnnotation>): List<KtLightAbstractAnnotation> {
if (sourceAnnotations.isEmpty()) return listOf(createRetentionRuntimeAnnotation(support, this))
return mutableListOf<KtLightAbstractAnnotation>().also { result ->
sourceAnnotations.mapNotNullTo(result) { sourceAnnotation ->
sourceAnnotation.tryConvertAsTarget(support)
?: sourceAnnotation.tryConvertAsRetention(support)
?: sourceAnnotation.tryConvertAsMustBeDocumented(support)
}
if (!result.any { it.qualifiedName == "java.lang.annotation.Retention" }) {
result.add(createRetentionRuntimeAnnotation(support, this))
}
}
}
}
@@ -180,7 +184,10 @@ fun isFromSources(lightElement: KtLightElement<*, *>): Boolean {
return true
}
private fun getAnnotationDescriptors(declaration: KtAnnotated, annotatedLightElement: KtLightElement<*, *>): List<AnnotationDescriptor> {
private fun getAnnotationDescriptors(
declaration: KtAnnotated,
annotatedLightElement: KtLightElement<*, *>
): List<AnnotationDescriptor> {
val context = LightClassGenerationSupport.getInstance(declaration.project).analyze(declaration)
val descriptor = if (declaration is KtParameter && declaration.isPropertyParameter()) {
@@ -227,4 +234,3 @@ private fun hasAnnotationsInSource(declaration: KtAnnotated): Boolean {
return false
}
@@ -0,0 +1,130 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.asJava.elements
import com.intellij.psi.CommonClassNames.JAVA_LANG_ANNOTATION_RETENTION
import com.intellij.psi.CommonClassNames.JAVA_LANG_ANNOTATION_TARGET
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiArrayInitializerMemberValue
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNameValuePair
import org.jetbrains.kotlin.asJava.classes.KtUltraLightSimpleAnnotation
import org.jetbrains.kotlin.asJava.classes.KtUltraLightSupport
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
private const val JAVA_LANG_ANNOTATION_DOCUMENTED = "java.lang.annotation.Documented"
private fun PsiAnnotation.extractAnnotationFqName(attributeName: String): String? {
val targetAttribute =
attributes.firstOrNull { it.attributeName == attributeName } as? KtLightPsiNameValuePair
targetAttribute ?: return null
val valueTarget = (targetAttribute.value as? KtLightPsiLiteral)?.value as? Pair<*, *>
valueTarget ?: return null
val classId = valueTarget.first as? ClassId
classId ?: return null
val name = valueTarget.second as? Name
name ?: return null
return "${classId.asSingleFqName().asString()}.${name.identifier}"
}
private fun PsiAnnotation.extractArrayAnnotationFqNames(attributeName: String): List<String>? =
attributes.firstOrNull { it.attributeName == attributeName }
?.let { it as? PsiNameValuePair }
?.let { (it.value as? PsiArrayInitializerMemberValue) }
?.let { arrayInitializer ->
arrayInitializer.initializers.filterIsInstance<KtLightPsiLiteral>()
.map { it.value }
.filterIsInstance<Pair<ClassId, Name>>()
.map { "${it.first.asSingleFqName().asString()}.${it.second.identifier}" }
}
private val javaAnnotationElementTypeId = ClassId.fromString("java.lang.annotation.ElementType")
private val targetMapping = hashMapOf(
"kotlin.annotation.AnnotationTarget.CLASS" to EnumValue(javaAnnotationElementTypeId, Name.identifier("TYPE")),
"kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS" to EnumValue(javaAnnotationElementTypeId, Name.identifier("ANNOTATION_TYPE")),
"kotlin.annotation.AnnotationTarget.FIELD" to EnumValue(javaAnnotationElementTypeId, Name.identifier("FIELD")),
"kotlin.annotation.AnnotationTarget.LOCAL_VARIABLE" to EnumValue(javaAnnotationElementTypeId, Name.identifier("LOCAL_VARIABLE")),
"kotlin.annotation.AnnotationTarget.VALUE_PARAMETER" to EnumValue(javaAnnotationElementTypeId, Name.identifier("PARAMETER")),
"kotlin.annotation.AnnotationTarget.CONSTRUCTOR" to EnumValue(javaAnnotationElementTypeId, Name.identifier("CONSTRUCTOR")),
"kotlin.annotation.AnnotationTarget.FUNCTION" to EnumValue(javaAnnotationElementTypeId, Name.identifier("METHOD")),
"kotlin.annotation.AnnotationTarget.PROPERTY_GETTER" to EnumValue(javaAnnotationElementTypeId, Name.identifier("METHOD")),
"kotlin.annotation.AnnotationTarget.PROPERTY_SETTER" to EnumValue(javaAnnotationElementTypeId, Name.identifier("METHOD"))
)
internal fun PsiAnnotation.tryConvertAsTarget(support: KtUltraLightSupport): KtLightAbstractAnnotation? {
if (FQ_NAMES.target.asString() != qualifiedName) return null
val attributeValues = extractArrayAnnotationFqNames("allowedTargets")
?: extractAnnotationFqName("value")?.let { listOf(it) }
attributeValues ?: return null
val convertedValues = attributeValues.mapNotNull { targetMapping[it] }.distinct()
val targetAttributes = "value" to ArrayValue(convertedValues) { module -> module.builtIns.array.defaultType }
return KtUltraLightSimpleAnnotation(
JAVA_LANG_ANNOTATION_TARGET,
listOf(targetAttributes),
support,
parent
)
}
private val javaAnnotationRetentionPolicyId = ClassId.fromString("java.lang.annotation.RetentionPolicy")
private val retentionMapping = hashMapOf(
"kotlin.annotation.AnnotationRetention.SOURCE" to EnumValue(javaAnnotationRetentionPolicyId, Name.identifier("SOURCE")),
"kotlin.annotation.AnnotationRetention.BINARY" to EnumValue(javaAnnotationRetentionPolicyId, Name.identifier("CLASS")),
"kotlin.annotation.AnnotationRetention.RUNTIME" to EnumValue(javaAnnotationRetentionPolicyId, Name.identifier("RUNTIME"))
)
internal fun createRetentionRuntimeAnnotation(support: KtUltraLightSupport, parent: PsiElement) =
KtUltraLightSimpleAnnotation(
JAVA_LANG_ANNOTATION_RETENTION,
listOf("value" to retentionMapping["kotlin.annotation.AnnotationRetention.RUNTIME"]!!),
support,
parent
)
internal fun PsiAnnotation.tryConvertAsRetention(support: KtUltraLightSupport): KtLightAbstractAnnotation? {
if (FQ_NAMES.retention.asString() != qualifiedName) return null
val convertedValue = extractAnnotationFqName("value")
?.let { retentionMapping[it] }
?: null
convertedValue ?: return null
return KtUltraLightSimpleAnnotation(
JAVA_LANG_ANNOTATION_RETENTION,
listOf("value" to convertedValue),
support,
parent
)
}
internal fun PsiAnnotation.tryConvertAsMustBeDocumented(support: KtUltraLightSupport): KtLightAbstractAnnotation? {
if (FQ_NAMES.mustBeDocumented.asString() != qualifiedName) return null
return KtUltraLightSimpleAnnotation(
JAVA_LANG_ANNOTATION_DOCUMENTED,
emptyList(),
support,
parent
)
}
@@ -41,8 +41,10 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType
@@ -105,21 +107,22 @@ class KtLightAnnotationForSourceEntry(
}
private fun getAttributeValue(name: String?, useDefault: Boolean): PsiAnnotationMemberValue? {
val callEntry = getCallEntry(name ?: "value") ?: return null
val valueArgument = callEntry.value.arguments.firstOrNull()
if (valueArgument != null) {
ktLightAnnotationParameterList.attributes.find { (it as KtLightPsiNameValuePair).valueArgument === valueArgument }?.let {
return it.value
}
}
ktLightAnnotationParameterList.attributes
.find { it.name == (name ?: "value") }
?.let { return it.value }
if (useDefault) {
val callEntry = getCallEntry(name ?: "value") ?: return null
if (callEntry.key.declaresOrInheritsDefaultValue()) {
when (val psiElement = callEntry.key.source.getPsi()) {
is KtParameter ->
return psiElement.defaultValue?.let { convertToLightAnnotationMemberValue(this, it) }
is PsiAnnotationMethod ->
return psiElement.defaultValue
}
if (useDefault && callEntry.key.declaresOrInheritsDefaultValue()) {
when (val psiElement = callEntry.key.source.getPsi()) {
is KtParameter ->
return psiElement.defaultValue?.let { convertToLightAnnotationMemberValue(this, it) }
is PsiAnnotationMethod ->
return psiElement.defaultValue
}
}
return null
@@ -142,44 +145,92 @@ class KtLightAnnotationForSourceEntry(
PsiAnnotationParameterList {
override val kotlinOrigin: KtElement? get() = null
private val _attributes: Array<PsiNameValuePair> by lazyPub {
this@KtLightAnnotationForSourceEntry.kotlinOrigin.valueArguments.map { makeLightPsiNameValuePair(it as KtValueArgument) }
.toTypedArray<PsiNameValuePair>()
}
private fun checkIfToArrayConversionExpected(callEntry: Map.Entry<ValueParameterDescriptor, ResolvedValueArgument>): Boolean {
private fun makeArrayInitializerIfExpected(pair: KtLightPsiNameValuePair): PsiAnnotationMemberValue? {
val valueArgument = pair.valueArgument
val name = valueArgument.name ?: "value"
val callEntry = getCallEntry(name) ?: return null
val valueArguments = callEntry.value.arguments
val argument = valueArguments.firstOrNull()?.getArgumentExpression() ?: return null
if (!callEntry.key.type.let { KotlinBuiltIns.isArrayOrPrimitiveArray(it) }) return null
if (argument !is KtStringTemplateExpression &&
argument !is KtConstantExpression &&
argument !is KtClassLiteralExpression &&
getAnnotationName(argument) == null
) {
return null
if (!callEntry.key.isVararg) {
return false
}
val parent = PsiTreeUtil.findCommonParent(valueArguments.map { it.getArgumentExpression() }) as KtElement
return KtLightPsiArrayInitializerMemberValue(parent, pair) { self ->
valueArguments.mapNotNull {
it.getArgumentExpression()?.let { convertToLightAnnotationMemberValue(self, it) }
//Anno()
val valueArgument = callEntry.value.arguments.firstOrNull() ?: return false
//Anno(1,2,3)
if (valueArgument is VarargValueArgument) {
return true
}
//Anno(*[1,2,3])
if (valueArgument is KtValueArgument && valueArgument.isSpread) {
return false
}
//Anno(a = [1,2,3])
return !valueArgument.isNamed()
}
private fun getWrappedToArrayNameValuePair(
resolvedArgumentEntry: Map.Entry<ValueParameterDescriptor, ResolvedValueArgument>
): KtLightPsiNameValuePair {
val argumentExpressions =
resolvedArgumentEntry.value.arguments.mapNotNull { varargArgument -> varargArgument.getArgumentExpression() }
val parent = PsiTreeUtil.findCommonParent(argumentExpressions) as? KtElement
?: this@KtLightAnnotationForSourceEntry.kotlinOrigin.valueArgumentList
?: this@KtLightAnnotationForSourceEntry.kotlinOrigin
val argumentName = resolvedArgumentEntry.key.name.asString()
return KtLightPsiNameValuePair(
parent,
argumentName,
this
) { self ->
KtLightPsiArrayInitializerMemberValue(parent, self) { memberValue ->
argumentExpressions.map { argumentExpression ->
convertToLightAnnotationMemberValue(memberValue, argumentExpression)
}
}
}
}
private fun makeLightPsiNameValuePair(valueArgument: KtValueArgument) = KtLightPsiNameValuePair(valueArgument, this) { self ->
makeArrayInitializerIfExpected(self)
?: self.valueArgument.getArgumentExpression()?.let { convertToLightAnnotationMemberValue(self, it) }
private fun getNotWrappedToArrayNameValuePair(
resolvedArgumentEntry: Map.Entry<ValueParameterDescriptor, ResolvedValueArgument>
): KtLightPsiNameValuePair? {
val firstArgument = resolvedArgumentEntry.value.arguments.firstOrNull() ?: return null
val argumentExpression = firstArgument.getArgumentExpression() ?: return null
val argumentName = resolvedArgumentEntry.key.name.asString()
return KtLightPsiNameValuePair(
firstArgument.asElement(),
argumentName,
this
) { valuePair -> convertToLightAnnotationMemberValue(valuePair, argumentExpression) }
}
private val _attributes: Array<PsiNameValuePair> by lazyPub {
if (this@KtLightAnnotationForSourceEntry.kotlinOrigin.valueArguments.isEmpty()) {
return@lazyPub emptyArray()
}
val resolvedArguments =
this@KtLightAnnotationForSourceEntry.kotlinOrigin.getResolvedCall()?.valueArguments
resolvedArguments ?: return@lazyPub emptyArray()
resolvedArguments.mapNotNull { resolvedArgumentEntry ->
if (checkIfToArrayConversionExpected(resolvedArgumentEntry)) {
getWrappedToArrayNameValuePair(resolvedArgumentEntry)
} else {
getNotWrappedToArrayNameValuePair(resolvedArgumentEntry)
}
}.toTypedArray()
}
override fun getAttributes(): Array<PsiNameValuePair> = _attributes
}