summaryrefslogtreecommitdiff
path: root/plugins/kotlin/uast/uast-kotlin-base/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt
blob: 5bea2bb5c63813e3df8249d09591948b310b94d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.uast.kotlin

import com.intellij.openapi.components.ServiceManager
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.ResolveResult
import org.jetbrains.kotlin.asJava.toLightAnnotation
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.idea.util.actionUnderSafeAnalyzeBlock
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.internal.multiResolveResults

sealed class KotlinUAnnotationBase<T : KtCallElement>(
    final override val sourcePsi: T,
    givenParent: UElement?
) : KotlinAbstractUElement(givenParent), UAnnotationEx, UAnchorOwner, UMultiResolvable {

    abstract override val javaPsi: PsiAnnotation?

    final override val psi: PsiElement = sourcePsi

    protected abstract fun annotationUseSiteTarget(): AnnotationUseSiteTarget?

    override val qualifiedName: String? by lz {
        baseResolveProviderService.qualifiedAnnotationName(sourcePsi)
    }

    override val attributeValues: List<UNamedExpression> by lz {
        baseResolveProviderService.convertValueArguments(sourcePsi, this) ?: emptyList()
    }

    override fun findAttributeValue(name: String?): UExpression? =
        findDeclaredAttributeValue(name) ?: findAttributeDefaultValue(name ?: "value")

    override fun findDeclaredAttributeValue(name: String?): UExpression? {
        return attributeValues.find {
            it.name == name ||
                    (name == null && it.name == "value") ||
                    (name == "value" && it.name == null)
        }?.expression
    }

    private fun findAttributeDefaultValue(name: String): UExpression? {
        return baseResolveProviderService.findDefaultValueForAnnotationAttribute(sourcePsi, name)?.let {
            languagePlugin?.convertWithParent(it)
        }
    }

    override fun convertParent(): UElement? {
        sourcePsi.parent.safeAs<KtAnnotatedExpression>()?.let { annotatedExpression ->
            return annotatedExpression.baseExpression?.let {
                baseResolveProviderService.baseKotlinConverter.convertExpression(it, null, DEFAULT_EXPRESSION_TYPES_LIST)
            }
        }

        val superParent = super.convertParent() ?: return null
        if (annotationUseSiteTarget() == AnnotationUseSiteTarget.RECEIVER) {
            (superParent.uastParent as? KotlinUMethod)?.uastParameters?.firstIsInstanceOrNull<KotlinReceiverUParameter>()?.let {
                return it
            }
        }
        return superParent
    }

    override fun multiResolve(): Iterable<ResolveResult> = sourcePsi.multiResolveResults().asIterable()
}

class KotlinUAnnotation(
    annotationEntry: KtAnnotationEntry,
    givenParent: UElement?
) : KotlinUAnnotationBase<KtAnnotationEntry>(annotationEntry, givenParent), UAnnotation {

    override val javaPsi: PsiAnnotation? by lz {
        annotationEntry.actionUnderSafeAnalyzeBlock({ annotationEntry.toLightAnnotation() }, { null })
    }

    override fun annotationUseSiteTarget() = sourcePsi.useSiteTarget?.getAnnotationUseSiteTarget()

    override fun resolve(): PsiClass? {
        return baseResolveProviderService.resolveToClass(sourcePsi, this)
    }

    override val uastAnchor by lz {
        KotlinUIdentifier(
            javaPsi?.nameReferenceElement,
            annotationEntry.typeReference?.nameElement,
            this
        )
    }

}

class KotlinUNestedAnnotation private constructor(
    original: KtCallExpression,
    givenParent: UElement?
) : KotlinUAnnotationBase<KtCallExpression>(original, givenParent) {

    override val javaPsi: PsiAnnotation? by lz { original.toLightAnnotation() }

    override fun annotationUseSiteTarget(): AnnotationUseSiteTarget? = null

    override fun resolve(): PsiClass? {
        return baseResolveProviderService.resolveToClassIfConstructorCall(sourcePsi, this)
    }

    override val uastAnchor by lz {
        KotlinUIdentifier(
            javaPsi?.nameReferenceElement?.referenceNameElement,
            (original.calleeExpression as? KtNameReferenceExpression)?.getReferencedNameElement(),
            this
        )
    }

    companion object {
        fun create(ktCallExpression: KtCallExpression, givenParent: UElement?): KotlinUNestedAnnotation? {
            val service = ServiceManager.getService(BaseKotlinUastResolveProviderService::class.java)
            return if (service.isAnnotationConstructorCall(ktCallExpression))
                KotlinUNestedAnnotation(ktCallExpression, givenParent)
            else
                null
        }
    }

}