181: UAST: UAnchorOwner and type-specific javaPsi support

This commit is contained in:
Nicolay Mitropolsky
2018-01-26 13:25:51 +03:00
committed by Nikolay Krasko
parent ffe33844b7
commit 224480ac5e
8 changed files with 186 additions and 23 deletions
@@ -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
@@ -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>
@@ -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)
}
@@ -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")
+9
View File
@@ -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)
}
}