From f8601479de94933caad320aa9dda4824363bf650 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Wed, 13 Dec 2017 17:50:07 +0300 Subject: [PATCH] Uast: handling `@receiver` annotations --- .../kotlin/declarations/KotlinUAnnotation.kt | 13 +++++++++++++ .../uast/kotlin/declarations/KotlinUMethod.kt | 12 +++++++++++- .../kotlin/declarations/KotlinUVariable.kt | 18 ++++++++++++++++++ plugins/uast-kotlin/testData/ReceiverFun.kt | 3 +++ .../uast-kotlin/testData/ReceiverFun.log.txt | 10 ++++++++++ .../testData/ReceiverFun.render.txt | 6 ++++++ .../tests/SimpleKotlinRenderLogTest.kt | 3 +++ 7 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 plugins/uast-kotlin/testData/ReceiverFun.kt create mode 100644 plugins/uast-kotlin/testData/ReceiverFun.log.txt create mode 100644 plugins/uast-kotlin/testData/ReceiverFun.render.txt diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt index 0970cf7867f..f0babf2c91f 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt @@ -3,6 +3,7 @@ package org.jetbrains.uast.kotlin import com.intellij.psi.PsiClass import org.jetbrains.kotlin.asJava.toLightAnnotation import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.ValueArgument @@ -12,7 +13,9 @@ 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.source.getPsi +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUMethod class KotlinUAnnotation( override val psi: KtAnnotationEntry, @@ -78,5 +81,15 @@ class KotlinUAnnotation( 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 (psi.useSiteTarget?.getAnnotationUseSiteTarget() == AnnotationUseSiteTarget.RECEIVER) { + (superParent.uastParent as? KotlinUMethod)?.uastParameters?.firstIsInstance()?.let { + return it + } + } + return superParent + } } diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt index c007c2d7fea..3e45ea0ba7d 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.asJava.elements.isSetter import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.uast.* import org.jetbrains.uast.java.internal.JavaUElementWithComments import org.jetbrains.uast.kotlin.* @@ -59,8 +60,17 @@ open class KotlinUMethod( .map { KotlinUAnnotation(it, this) } } + private val receiver by lz { (sourcePsi as? KtCallableDeclaration)?.receiverTypeReference } + override val uastParameters by lz { - psi.parameterList.parameters.map { KotlinUParameter(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, this) } + val lightParams = psi.parameterList.parameters + val receiver = receiver ?: return@lz lightParams.map { + KotlinUParameter(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, this) + } + val receiverLight = lightParams.firstOrNull() ?: return@lz emptyList() + val uParameters = SmartList(KotlinReceiverUParameter(receiverLight, receiver, this)) + lightParams.drop(1).mapTo(uParameters) { KotlinUParameter(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, this) } + uParameters } override val uastAnchor: UElement diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt index e9e2ea3b840..1c614c52580 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt @@ -201,9 +201,27 @@ open class KotlinUParameter( } } +class KotlinReceiverUParameter( + psi: PsiParameter, + private val receiver: KtTypeReference, + givenParent: UElement? +) : KotlinUParameter(psi, receiver, givenParent) { + + override val annotations: List by lz { + receiver.annotationEntries + .filter { it.useSiteTarget?.getAnnotationUseSiteTarget() == AnnotationUseSiteTarget.RECEIVER } + .map { KotlinUAnnotation(it, this) } + + super.annotations + } + +} + class KotlinNullabilityUAnnotation(val annotatedElement: PsiElement, override val uastParent: UElement) : UAnnotation, JvmDeclarationUElement { 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 } } diff --git a/plugins/uast-kotlin/testData/ReceiverFun.kt b/plugins/uast-kotlin/testData/ReceiverFun.kt new file mode 100644 index 00000000000..c4581c6f980 --- /dev/null +++ b/plugins/uast-kotlin/testData/ReceiverFun.kt @@ -0,0 +1,3 @@ +annotation class MyReceiverAnnotation + +fun @receiver:MyReceiverAnnotation String.foo() = this.length \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/ReceiverFun.log.txt b/plugins/uast-kotlin/testData/ReceiverFun.log.txt new file mode 100644 index 00000000000..c2681851fd6 --- /dev/null +++ b/plugins/uast-kotlin/testData/ReceiverFun.log.txt @@ -0,0 +1,10 @@ +UFile (package = ) + UClass (name = ReceiverFunKt) + UAnnotationMethod (name = foo) + UParameter (name = $receiver) + UAnnotation (fqName = MyReceiverAnnotation) + UAnnotation (fqName = org.jetbrains.annotations.NotNull) + UQualifiedReferenceExpression + UThisExpression (label = null) + USimpleNameReferenceExpression (identifier = length) + UClass (name = MyReceiverAnnotation) diff --git a/plugins/uast-kotlin/testData/ReceiverFun.render.txt b/plugins/uast-kotlin/testData/ReceiverFun.render.txt new file mode 100644 index 00000000000..231bfba4458 --- /dev/null +++ b/plugins/uast-kotlin/testData/ReceiverFun.render.txt @@ -0,0 +1,6 @@ +public final class ReceiverFunKt { + public static final fun foo(@MyReceiverAnnotation @org.jetbrains.annotations.NotNull $receiver: java.lang.String) : int = this.length +} + +public abstract annotation MyReceiverAnnotation { +} diff --git a/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt b/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt index 15f6782cca9..aaec47ab04c 100644 --- a/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt +++ b/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt @@ -64,4 +64,7 @@ class SimpleKotlinRenderLogTest : AbstractKotlinRenderLogTest() { @Test fun testClassAnnotation() = doTest("ClassAnnotation") + + @Test + fun testReceiverFun() = doTest("ReceiverFun") }