All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.asJava.elements.lightAnnotations.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2022 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.*
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.cannotModify
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.asJava.fastCheckIsNullabilityApplied
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.descriptors.JavaClassConstructorDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
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.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.getType
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.nullability

class KtLightAnnotationForSourceEntry(
    private val name: String?,
    private val lazyQualifiedName: () -> String?,
    override val kotlinOrigin: KtCallElement,
    parent: PsiElement
) : KtLightAbstractAnnotation(parent) {

    private val _qualifiedName: String? by lazyPub { lazyQualifiedName() }

    override fun getOwner() = parent as? PsiAnnotationOwner

    override fun getQualifiedName(): String? = _qualifiedName

    override fun getName(): String? = name

    override fun findAttributeValue(name: String?) = getAttributeValue(name, true)

    override fun findDeclaredAttributeValue(name: String?): PsiAnnotationMemberValue? = getAttributeValue(name, false)

    private fun getCallEntry(name: String): MutableMap.MutableEntry? {
        val resolvedCall = kotlinOrigin.getResolvedCall() ?: return null
        return resolvedCall.valueArguments.entries.find { (param, _) -> param.name.asString() == name }
    }

    private fun getAttributeValue(name: String?, useDefault: Boolean): PsiAnnotationMemberValue? {

        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 { defaultValue ->
                            convertToLightAnnotationMemberValue(ktLightAnnotationParameterList, defaultValue)
                        }

                    is PsiAnnotationMethod ->
                        return psiElement.defaultValue
                }

            }
        }
        return null
    }

    override fun getNameReferenceElement(): PsiJavaCodeReferenceElement = KtLightPsiJavaCodeReferenceElement(
        kotlinOrigin.navigationElement,
        {
            (kotlinOrigin as? KtAnnotationEntry)?.typeReference?.reference
                ?: (kotlinOrigin.calleeExpression?.nameReference)?.references?.firstOrNull()
        },
        if (qualifiedName == CommonClassNames.JAVA_LANG_ANNOTATION_REPEATABLE) JAVA_LANG_ANNOTATION_REPEATABLE_SHORT_NAME else null,
    )

    private val ktLightAnnotationParameterList by lazyPub { KtLightAnnotationParameterList() }

    override fun getParameterList(): PsiAnnotationParameterList = ktLightAnnotationParameterList

    inner class KtLightAnnotationParameterList : KtLightElementBase(this),
        PsiAnnotationParameterList {
        override val kotlinOrigin: KtElement? get() = null

        private fun checkIfToArrayConversionExpected(callEntry: Map.Entry): Boolean {

            if (!callEntry.key.isVararg) {
                return false
            }

            //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
        ): KtLightPsiNameValuePair {

            val argumentExpressions =
                resolvedArgumentEntry.value.arguments.mapNotNull { varargArgument -> varargArgument.getArgumentExpression() }

            val parent = PsiTreeUtil.findCommonParent(argumentExpressions) as? KtElement
                ?: this@KtLightAnnotationForSourceEntry.kotlinOrigin.valueArgumentList
                ?: [email protected]

            val argumentName = resolvedArgumentEntry.key.name.asString()

            return KtLightPsiNameValuePair(
                parent,
                argumentName,
                this
            ) { self ->
                KtLightPsiArrayInitializerMemberValue(parent, self) { memberValue ->
                    argumentExpressions.map { argumentExpression ->
                        convertToLightAnnotationMemberValue(memberValue, argumentExpression)
                    }
                }
            }
        }

        private fun getNotWrappedToArrayNameValuePair(
            resolvedArgumentEntry: Map.Entry
        ): 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 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 = _attributes
    }


    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 KtLightAnnotationForSourceEntry).kotlinOrigin
    }

    override fun hashCode() = kotlinOrigin.hashCode()

    override fun  setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
}

class KtLightEmptyAnnotationParameterList(parent: PsiElement) : KtLightElementBase(parent), PsiAnnotationParameterList {
    override val kotlinOrigin: KtElement? get() = null
    override fun getAttributes(): Array = emptyArray()
}

open class KtLightNullabilityAnnotation>(val member: D, parent: PsiElement) :
    KtLightAbstractAnnotation(parent) {
    override fun fqNameMatches(fqName: String): Boolean {
        if (!isNullabilityAnnotation(fqName)) return false

        return super.fqNameMatches(fqName)
    }

    override val kotlinOrigin: Nothing? get() = null
    override fun  setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()

    override fun findAttributeValue(attributeName: String?): PsiAnnotationMemberValue? = null

    private val _qualifiedName: String? by lazyPub {
        val annotatedElement = member.takeIf(::isFromSources)?.kotlinOrigin
            ?: // it is out of our hands
            return@lazyPub null

        if (!fastCheckIsNullabilityApplied(member)) return@lazyPub null

        // all data-class generated members are not-null
        if (annotatedElement is KtClass && annotatedElement.isData()) return@lazyPub NotNull::class.java.name

        // objects and companion objects have NotNull annotation (if annotated element is implicit ctor then skip annotation)
        if (annotatedElement is KtObjectDeclaration) {
            if ((parent.parent as? PsiMethod)?.isConstructor == true) return@lazyPub null
            return@lazyPub NotNull::class.java.name
        }

        // don't annotate property setters
        if (annotatedElement is KtValVarKeywordOwner
            && member is KtLightMethod
            && (member.originalElement as? KtPropertyAccessor)?.isSetter == true
        ) {
            return@lazyPub null
        }

        if (annotatedElement is KtNamedFunction && annotatedElement.modifierList?.hasSuspendModifier() == true) {
            return@lazyPub Nullable::class.java.name
        }

        val kotlinType = getTargetType(annotatedElement) ?: return@lazyPub null

        if (KotlinBuiltIns.isPrimitiveType(kotlinType) && (annotatedElement as? KtParameter)?.isVarArg != true) {
            // no need to annotate them explicitly except the case when overriding reference-type makes it non-primitive for Jvm
            if (!(annotatedElement is KtCallableDeclaration && annotatedElement.hasModifier(KtTokens.OVERRIDE_KEYWORD))) return@lazyPub null

            val overriddenDescriptors =
                (annotatedElement.analyze()[BindingContext.DECLARATION_TO_DESCRIPTOR, annotatedElement] as? CallableMemberDescriptor)?.overriddenDescriptors
            if (overriddenDescriptors?.all { it.returnType == kotlinType } == true) return@lazyPub null
        }
        if (kotlinType.isUnit() && (annotatedElement !is KtValVarKeywordOwner)) return@lazyPub null // not annotate unit-functions
        if (kotlinType.isTypeParameter()) {
            if (!TypeUtils.hasNullableSuperType(kotlinType)) return@lazyPub NotNull::class.java.name
            if (!kotlinType.isMarkedNullable) return@lazyPub null
        }

        when (kotlinType.nullability()) {
            TypeNullability.NOT_NULL -> NotNull::class.java.name
            TypeNullability.NULLABLE -> Nullable::class.java.name
            TypeNullability.FLEXIBLE -> null
        }
    }

    override fun getQualifiedName(): String? = _qualifiedName

    internal fun KtTypeReference.getType(): KotlinType? = analyze()[BindingContext.TYPE, this]

    private fun getTargetType(annotatedElement: PsiElement): KotlinType? {
        if (annotatedElement is KtTypeReference) {
            annotatedElement.getType()?.let { return it }
        }
        if (annotatedElement is KtCallableDeclaration) {
            annotatedElement.typeReference?.getType()?.let { return it }
        }
        if (annotatedElement is KtNamedFunction) {
            annotatedElement.bodyExpression?.let { it.getType(it.analyze()) }?.let { return it }
        }
        if (annotatedElement is KtProperty) {
            (annotatedElement.initializer ?: annotatedElement.getter?.initializer)
                ?.let { it.getType(it.analyze()) }?.let { return it }
            annotatedElement.delegateExpression?.let { it.getType(it.analyze())?.arguments?.firstOrNull()?.type }?.let { return it }
        }

        return annotatedElement.getParentOfType(false)?.let {
            it.typeReference?.getType()
                ?: (it.initializer ?: it.getter?.initializer)
                    ?.let { initializer -> initializer.getType(initializer.analyze()) }
        }
    }

    override fun getNameReferenceElement(): PsiJavaCodeReferenceElement? = null

    override fun getParameterList(): PsiAnnotationParameterList = KtLightEmptyAnnotationParameterList(this)

    override fun findDeclaredAttributeValue(attributeName: String?): PsiAnnotationMemberValue? = null
}

internal fun isNullabilityAnnotation(qualifiedName: String?) = qualifiedName in backendNullabilityAnnotations

private val backendNullabilityAnnotations = arrayOf(Nullable::class.java.name, NotNull::class.java.name)

private fun KtElement.analyze(): BindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(this)

private fun KtElement.getResolvedCall(): ResolvedCall? {
    if (!isValid) return null
    val context = analyze()
    return this.getResolvedCall(context)
}

fun convertToLightAnnotationMemberValue(lightParent: PsiElement, argument: KtExpression): PsiAnnotationMemberValue {
    @Suppress("NAME_SHADOWING") val argument = unwrapCall(argument)
    when (argument) {
        is KtClassLiteralExpression -> {
            return KtLightPsiClassObjectAccessExpression(argument, lightParent)
        }

        is KtStringTemplateExpression, is KtConstantExpression -> {
            return KtLightPsiLiteral(argument, lightParent)
        }

        is KtCallExpression -> {
            val arguments = argument.valueArguments
            val annotationName = argument.calleeExpression?.let { getAnnotationName(it) }
            if (annotationName != null) {
                return KtLightAnnotationForSourceEntry(
                    name = annotationName,
                    lazyQualifiedName = { annotationName },
                    kotlinOrigin = argument,
                    parent = lightParent
                )
            }
            val resolvedCall = argument.getResolvedCall()
            if (resolvedCall != null && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall))
                return KtLightPsiArrayInitializerMemberValue(
                    argument,
                    lightParent
                ) { self ->
                    arguments.mapNotNull {
                        it.getArgumentExpression()?.let { expression -> convertToLightAnnotationMemberValue(self, expression) }
                    }
                }
        }

        is KtCollectionLiteralExpression -> {
            val arguments = argument.getInnerExpressions()
            if (arguments.isNotEmpty())
                return KtLightPsiArrayInitializerMemberValue(
                    argument,
                    lightParent
                ) { self -> arguments.map { convertToLightAnnotationMemberValue(self, it) } }
        }
    }
    // everything else (like complex constant references) considered as PsiLiteral-s
    return KtLightPsiLiteral(argument, lightParent)
}

private val KtExpression.nameReference: KtNameReferenceExpression?
    get() = when (this) {
        is KtConstructorCalleeExpression -> constructorReferenceExpression as? KtNameReferenceExpression
        else -> this as? KtNameReferenceExpression
    }

private fun unwrapCall(callee: KtExpression): KtExpression = when (callee) {
    is KtDotQualifiedExpression -> callee.lastChild as? KtCallExpression ?: callee
    else -> callee
}

private fun getAnnotationName(callee: KtExpression): String? {
    @Suppress("NAME_SHADOWING") val callee = unwrapCall(callee)
    val resultingDescriptor = callee.getResolvedCall()?.resultingDescriptor
    if (resultingDescriptor is ClassConstructorDescriptor) {
        val ktClass = resultingDescriptor.constructedClass.source.getPsi() as? KtClass
        if (ktClass?.isAnnotation() == true) return ktClass.fqName?.toString()
    }
    if (resultingDescriptor is JavaClassConstructorDescriptor) {
        val psiClass = resultingDescriptor.constructedClass.source.getPsi() as? PsiClass
        if (psiClass?.isAnnotationType == true) return psiClass.qualifiedName
    }
    return null
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy