181: UAST: UAnchorOwner and type-specific javaPsi support
This commit is contained in:
committed by
Nikolay Krasko
parent
ffe33844b7
commit
224480ac5e
+152
@@ -0,0 +1,152 @@
|
||||
package org.jetbrains.uast.kotlin
|
||||
|
||||
import com.intellij.psi.PsiAnnotation
|
||||
import com.intellij.psi.PsiClass
|
||||
import org.jetbrains.kotlin.asJava.toLightAnnotation
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import org.jetbrains.uast.*
|
||||
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
|
||||
import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
|
||||
|
||||
abstract class KotlinUAnnotationBase(
|
||||
final override val psi: KtElement,
|
||||
givenParent: UElement?
|
||||
) : KotlinAbstractUElement(givenParent), UAnnotationEx, UAnchorOwner {
|
||||
|
||||
abstract override val javaPsi: PsiAnnotation?
|
||||
|
||||
final override val sourcePsi = psi
|
||||
|
||||
protected abstract fun annotationUseSiteTarget(): AnnotationUseSiteTarget?
|
||||
|
||||
private val resolvedCall: ResolvedCall<*>? by lz { psi.getResolvedCall(psi.analyze()) }
|
||||
|
||||
override val qualifiedName: String?
|
||||
get() = annotationClassDescriptor.takeUnless(ErrorUtils::isError)
|
||||
?.fqNameUnsafe
|
||||
?.takeIf(FqNameUnsafe::isSafe)
|
||||
?.toSafe()
|
||||
?.toString()
|
||||
|
||||
override val attributeValues: List<UNamedExpression> by lz {
|
||||
resolvedCall?.valueArguments?.entries?.mapNotNull {
|
||||
val arguments = it.value.arguments
|
||||
val name = it.key.name.asString()
|
||||
when {
|
||||
arguments.size == 1 ->
|
||||
KotlinUNamedExpression.create(name, arguments.first(), this)
|
||||
arguments.size > 1 ->
|
||||
KotlinUNamedExpression.create(name, arguments, this)
|
||||
else -> null
|
||||
}
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
protected abstract val annotationClassDescriptor: ClassDescriptor?
|
||||
|
||||
override fun resolve(): PsiClass? {
|
||||
val descriptor = annotationClassDescriptor ?: return null
|
||||
return descriptor.toSource()?.getMaybeLightElement(this) as? PsiClass
|
||||
}
|
||||
|
||||
override fun findAttributeValue(name: String?): UExpression? =
|
||||
findDeclaredAttributeValue(name) ?: findAttributeDefaultValue(name ?: "value")
|
||||
|
||||
fun findAttributeValueExpression(arg: ValueArgument): UExpression? {
|
||||
val mapping = resolvedCall?.getArgumentMapping(arg)
|
||||
return (mapping as? ArgumentMatch)?.let { match ->
|
||||
val namedExpression = attributeValues.find { it.name == match.valueParameter.name.asString() }
|
||||
namedExpression?.expression as? KotlinUVarargExpression ?: namedExpression
|
||||
}
|
||||
}
|
||||
|
||||
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? {
|
||||
val parameter = annotationClassDescriptor
|
||||
?.unsubstitutedPrimaryConstructor
|
||||
?.valueParameters
|
||||
?.find { it.name.asString() == name } ?: return null
|
||||
|
||||
val defaultValue = (parameter.source.getPsi() as? KtParameter)?.defaultValue ?: return null
|
||||
return getLanguagePlugin().convertWithParent(defaultValue)
|
||||
}
|
||||
|
||||
override fun convertParent(): UElement? {
|
||||
val superParent = super.convertParent() ?: return null
|
||||
if (annotationUseSiteTarget() == AnnotationUseSiteTarget.RECEIVER) {
|
||||
(superParent.uastParent as? KotlinUMethod)?.uastParameters?.firstIsInstance<KotlinReceiverUParameter>()?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return superParent
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinUAnnotation(
|
||||
val annotationEntry: KtAnnotationEntry,
|
||||
givenParent: UElement?
|
||||
) : KotlinUAnnotationBase(annotationEntry, givenParent), UAnnotation {
|
||||
|
||||
override val javaPsi = annotationEntry.toLightAnnotation()
|
||||
|
||||
private val resolvedAnnotation: AnnotationDescriptor? by lz { annotationEntry.analyze()[BindingContext.ANNOTATION, annotationEntry] }
|
||||
|
||||
override val annotationClassDescriptor: ClassDescriptor?
|
||||
get() = resolvedAnnotation?.annotationClass
|
||||
|
||||
override fun annotationUseSiteTarget() = annotationEntry.useSiteTarget?.getAnnotationUseSiteTarget()
|
||||
|
||||
override val uastAnchor by lazy {
|
||||
KotlinUIdentifier(
|
||||
javaPsi?.nameReferenceElement?.referenceNameElement,
|
||||
annotationEntry.typeReference?.typeElement?.let {
|
||||
(it as? KtUserType)?.referenceExpression?.getReferencedNameElement() ?: it.navigationElement
|
||||
},
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class KotlinUNestedAnnotation(
|
||||
private val original: KtCallExpression,
|
||||
givenParent: UElement?,
|
||||
private val classDescriptor: ClassDescriptor?
|
||||
) : KotlinUAnnotationBase(original, givenParent) {
|
||||
override val javaPsi: PsiAnnotation? by lazy { original.toLightAnnotation() }
|
||||
override val annotationClassDescriptor: ClassDescriptor?
|
||||
get() = classDescriptor
|
||||
|
||||
override fun annotationUseSiteTarget(): AnnotationUseSiteTarget? = null
|
||||
|
||||
override val uastAnchor by lazy {
|
||||
KotlinUIdentifier(
|
||||
javaPsi?.nameReferenceElement?.referenceNameElement,
|
||||
(original.calleeExpression as? KtNameReferenceExpression)?.getReferencedNameElement(),
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
|
||||
import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
|
||||
import org.jetbrains.uast.kotlin.declarations.UastLightIdentifier
|
||||
|
||||
abstract class AbstractKotlinUClass(givenParent: UElement?) : KotlinAbstractUElement(givenParent), UClass, JvmDeclarationUElement {
|
||||
abstract class AbstractKotlinUClass(givenParent: UElement?) : KotlinAbstractUElement(givenParent), UClassTypeSpecific, UAnchorOwner,
|
||||
JvmDeclarationUElement {
|
||||
|
||||
override val uastDeclarations by lz {
|
||||
mutableListOf<UDeclaration>().apply {
|
||||
@@ -79,8 +80,7 @@ open class KotlinUClass private constructor(
|
||||
|
||||
override fun getContainingFile(): PsiFile? = unwrapFakeFileForLightClass(psi.containingFile)
|
||||
|
||||
override val uastAnchor: UElement
|
||||
get() = KotlinUIdentifier(nameIdentifier, ktClass?.nameIdentifier, this)
|
||||
override val uastAnchor by lazy { KotlinUIdentifier(nameIdentifier, ktClass?.nameIdentifier, this) }
|
||||
|
||||
override fun getInnerClasses(): Array<UClass> {
|
||||
// filter DefaultImpls to avoid processing same methods from original interface multiple times
|
||||
@@ -211,10 +211,9 @@ class KotlinUAnonymousClass(
|
||||
|
||||
override fun getContainingFile(): PsiFile = unwrapFakeFileForLightClass(psi.containingFile)
|
||||
|
||||
override val uastAnchor: UElement?
|
||||
get() {
|
||||
val ktClassOrObject = (psi.originalElement as? KtLightClass)?.kotlinOrigin as? KtObjectDeclaration ?: return null
|
||||
return KotlinUIdentifier(ktClassOrObject.getObjectKeyword(), this)
|
||||
override val uastAnchor by lazy {
|
||||
val ktClassOrObject = (psi.originalElement as? KtLightClass)?.kotlinOrigin as? KtObjectDeclaration ?: return@lazy null
|
||||
KotlinUIdentifier(ktClassOrObject.getObjectKeyword(), this)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -227,8 +226,7 @@ class KotlinScriptUClass(
|
||||
|
||||
override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, psi.kotlinOrigin)
|
||||
|
||||
override val uastAnchor: UElement
|
||||
get() = KotlinUIdentifier(nameIdentifier, sourcePsi?.nameIdentifier, this)
|
||||
override val uastAnchor by lazy { KotlinUIdentifier(nameIdentifier, sourcePsi?.nameIdentifier, this) }
|
||||
|
||||
override val javaPsi: PsiClass = psi
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.jetbrains.uast.kotlin.*
|
||||
open class KotlinUMethod(
|
||||
psi: KtLightMethod,
|
||||
givenParent: UElement?
|
||||
) : KotlinAbstractUElement(givenParent), UAnnotationMethod, JavaUElementWithComments, PsiMethod by psi {
|
||||
) : KotlinAbstractUElement(givenParent), UAnnotationMethod, UMethodTypeSpecific, UAnchorOwner, JavaUElementWithComments, PsiMethod by psi {
|
||||
override val psi: KtLightMethod = unwrap<UMethod, KtLightMethod>(psi)
|
||||
|
||||
override val javaPsi = psi
|
||||
@@ -72,12 +72,13 @@ open class KotlinUMethod(
|
||||
uParameters
|
||||
}
|
||||
|
||||
override val uastAnchor: UElement
|
||||
get() = KotlinUIdentifier(
|
||||
override val uastAnchor by lazy {
|
||||
KotlinUIdentifier(
|
||||
nameIdentifier,
|
||||
(sourcePsi as? PsiNameIdentifierOwner)?.nameIdentifier ?: sourcePsi?.navigationElement,
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
override val uastBody by lz {
|
||||
@@ -108,9 +109,9 @@ open class KotlinUMethod(
|
||||
override val isOverride: Boolean
|
||||
get() = (kotlinOrigin as? KtCallableDeclaration)?.hasModifier(KtTokens.OVERRIDE_KEYWORD) ?: false
|
||||
|
||||
override fun getBody(): PsiCodeBlock? = super.getBody()
|
||||
override fun getBody(): PsiCodeBlock? = super<UAnnotationMethod>.getBody()
|
||||
|
||||
override fun getOriginalElement(): PsiElement? = super.getOriginalElement()
|
||||
override fun getOriginalElement(): PsiElement? = super<UAnnotationMethod>.getOriginalElement()
|
||||
|
||||
override fun equals(other: Any?) = other is KotlinUMethod && psi == other.psi
|
||||
|
||||
|
||||
+8
-2
@@ -100,7 +100,8 @@ abstract class AbstractKotlinUVariable(givenParent: UElement?)
|
||||
|
||||
override fun equals(other: Any?) = other is AbstractKotlinUVariable && psi == other.psi
|
||||
|
||||
class WrappedUAnnotation(psiAnnotation: PsiAnnotation, override val uastParent: UElement) : UAnnotation, JvmDeclarationUElement {
|
||||
class WrappedUAnnotation(psiAnnotation: PsiAnnotation, override val uastParent: UElement) : UAnnotation, UAnchorOwner,
|
||||
JvmDeclarationUElement {
|
||||
|
||||
override val javaPsi: PsiAnnotation = psiAnnotation
|
||||
override val psi: PsiAnnotation = javaPsi
|
||||
@@ -110,6 +111,8 @@ abstract class AbstractKotlinUVariable(givenParent: UElement?)
|
||||
psi.parameterList.attributes.map { WrappedUNamedExpression(it, this) }
|
||||
}
|
||||
|
||||
override val uastAnchor by lazy { KotlinUIdentifier(javaPsi.nameReferenceElement?.referenceNameElement, null, this) }
|
||||
|
||||
class WrappedUNamedExpression(pair: PsiNameValuePair, override val uastParent: UElement?) : UNamedExpression, JvmDeclarationUElement {
|
||||
override val name: String? = pair.name
|
||||
override val psi = pair
|
||||
@@ -217,7 +220,8 @@ class KotlinReceiverUParameter(
|
||||
|
||||
}
|
||||
|
||||
class KotlinNullabilityUAnnotation(val annotatedElement: PsiElement, override val uastParent: UElement) : UAnnotation, JvmDeclarationUElement {
|
||||
class KotlinNullabilityUAnnotation(val annotatedElement: PsiElement, override val uastParent: UElement) : UAnnotationEx, UAnchorOwner,
|
||||
JvmDeclarationUElement {
|
||||
|
||||
private fun getTargetType(annotatedElement: PsiElement): KotlinType? {
|
||||
if (annotatedElement is KtTypeReference) {
|
||||
@@ -236,6 +240,8 @@ class KotlinNullabilityUAnnotation(val annotatedElement: PsiElement, override va
|
||||
return null
|
||||
}
|
||||
|
||||
override val uastAnchor: UIdentifier? = null
|
||||
|
||||
val nullability by lz { getTargetType(annotatedElement)?.nullability() }
|
||||
|
||||
override val attributeValues: List<UNamedExpression>
|
||||
|
||||
+2
-3
@@ -18,7 +18,6 @@ package org.jetbrains.uast.kotlin.declarations
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiIdentifier
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
|
||||
@@ -35,7 +34,7 @@ class UastLightIdentifier(lightOwner: PsiNameIdentifierOwner, ktDeclaration: KtN
|
||||
}
|
||||
|
||||
class KotlinUIdentifier private constructor(
|
||||
override val javaPsi: PsiIdentifier?,
|
||||
override val javaPsi: PsiElement?,
|
||||
override val sourcePsi: PsiElement?,
|
||||
override val psi: PsiElement?,
|
||||
givenParent: UElement?
|
||||
@@ -47,6 +46,6 @@ class KotlinUIdentifier private constructor(
|
||||
|
||||
override val uastParent: UElement? by lazy { givenParent ?: sourcePsi?.parent?.toUElement() }
|
||||
|
||||
constructor(javaPsi: PsiIdentifier?, sourcePsi: PsiElement?, uastParent: UElement?) : this(javaPsi, sourcePsi, javaPsi, uastParent)
|
||||
constructor(javaPsi: PsiElement?, sourcePsi: PsiElement?, uastParent: UElement?) : this(javaPsi, sourcePsi, javaPsi, uastParent)
|
||||
constructor(sourcePsi: PsiElement?, uastParent: UElement?) : this(null, sourcePsi, sourcePsi, uastParent)
|
||||
}
|
||||
+1
-4
@@ -101,7 +101,7 @@ class KotlinUFunctionCallExpression(
|
||||
}
|
||||
|
||||
private fun createVarargsHolder(arguments: List<ValueArgument>, parent: UElement?): KotlinUExpressionList =
|
||||
KotlinUExpressionList(null, VARARGS, parent).apply {
|
||||
KotlinUExpressionList(null, UastSpecialExpressionKind.VARARGS, parent).apply {
|
||||
expressions = arguments.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), parent) }
|
||||
}
|
||||
|
||||
@@ -160,6 +160,3 @@ class KotlinUFunctionCallExpression(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Deprecated("will be replaced by one from uast api when it comes")
|
||||
val VARARGS = UastSpecialExpressionKind("varargs")
|
||||
@@ -0,0 +1,9 @@
|
||||
class SimpleAnnotated {
|
||||
@Suppress("abc")
|
||||
fun method() {
|
||||
println("Hello, world!")
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin("1.0")
|
||||
val property: String = "Mary"
|
||||
}
|
||||
@@ -304,6 +304,7 @@ class KotlinUastApiTest : AbstractKotlinUastTest() {
|
||||
val lightAnnotation = convertedUAnnotation.getAsJavaPsiElement(PsiAnnotation::class.java)
|
||||
?: throw AssertionError("can't get lightAnnotation from $convertedUAnnotation")
|
||||
assertEquals("Annotation", lightAnnotation.qualifiedName)
|
||||
assertEquals("Annotation", (convertedUAnnotation as UAnchorOwner).uastAnchor?.sourcePsi?.text)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user