diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt index 389285b4a81..5bfbb7775eb 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -204,8 +205,12 @@ class KotlinUFunctionCallExpression( private fun isAnnotationArgumentArrayInitializer(): Boolean { val resolvedCall = resolvedCall ?: return false - // KtAnnotationEntry -> KtValueArgumentList -> KtValueArgument -> arrayOf call - return psi.parents.elementAtOrNull(2) is KtAnnotationEntry && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall) + // KtAnnotationEntry (or KtCallExpression when annotation is nested) -> KtValueArgumentList -> KtValueArgument -> arrayOf call + return when (val elementAt2 = psi.parents.elementAtOrNull(2)) { + is KtAnnotationEntry -> true + is KtCallExpression -> elementAt2.getParentOfType(true, KtDeclaration::class.java) != null + else -> false + } && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall) } override fun convertParent(): UElement? = super.convertParent().let { result -> diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.182 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.182 index 7cc4f3ed77d..c378a7c6e1d 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.182 +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.182 @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -148,8 +149,12 @@ class KotlinUFunctionCallExpression( private fun isAnnotationArgumentArrayInitializer(): Boolean { val resolvedCall = resolvedCall ?: return false - // KtAnnotationEntry -> KtValueArgumentList -> KtValueArgument -> arrayOf call - return psi.parents.elementAtOrNull(2) is KtAnnotationEntry && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall) + // KtAnnotationEntry (or KtCallExpression when annotation is nested) -> KtValueArgumentList -> KtValueArgument -> arrayOf call + return when (val elementAt2 = psi.parents.elementAtOrNull(2)) { + is KtAnnotationEntry -> true + is KtCallExpression -> elementAt2.getParentOfType(true, KtDeclaration::class.java) != null + else -> false + } && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall) } override fun convertParent(): UElement? = super.convertParent().let { result -> diff --git a/plugins/uast-kotlin/testData/AnnotationComplex.kt b/plugins/uast-kotlin/testData/AnnotationComplex.kt index 6a3e4353072..1e841586f95 100644 --- a/plugins/uast-kotlin/testData/AnnotationComplex.kt +++ b/plugins/uast-kotlin/testData/AnnotationComplex.kt @@ -1,4 +1,4 @@ -annotation class Annotation +annotation class Annotation(vararg val strings: String) @Annotation class A @@ -8,10 +8,13 @@ annotation class AnnotationInner(val value: Annotation) @AnnotationArray(Annotation()) class B1 -@AnnotationArray(value = Annotation()) +@AnnotationArray(value = Annotation("sv1", "sv2")) class B2 annotation class AnnotationArray(vararg val value: Annotation) -@AnnotationArray(Annotation()) -class C \ No newline at end of file +@AnnotationArray(Annotation(strings = arrayOf("sar1", "sar2"))) +class C + +@AnnotationArray(Annotation(strings = ["[sar]1", "[sar]2"])) +class C2 \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/AnnotationComplex.log.txt b/plugins/uast-kotlin/testData/AnnotationComplex.log.txt index 671589376f6..44e589e9f6f 100644 --- a/plugins/uast-kotlin/testData/AnnotationComplex.log.txt +++ b/plugins/uast-kotlin/testData/AnnotationComplex.log.txt @@ -1,5 +1,6 @@ UFile (package = ) UClass (name = Annotation) + UAnnotationMethod (name = strings) UClass (name = A) UAnnotation (fqName = Annotation) UAnnotationMethod (name = A) @@ -15,16 +16,34 @@ UFile (package = ) UClass (name = B2) UAnnotation (fqName = AnnotationArray) UNamedExpression (name = value) - UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 2)) UIdentifier (Identifier (Annotation)) USimpleNameReferenceExpression (identifier = ) + ULiteralExpression (value = "sv1") + ULiteralExpression (value = "sv2") UAnnotationMethod (name = B2) UClass (name = AnnotationArray) UAnnotationMethod (name = value) UClass (name = C) UAnnotation (fqName = AnnotationArray) UNamedExpression (name = value) - UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1)) UIdentifier (Identifier (Annotation)) USimpleNameReferenceExpression (identifier = ) + UCallExpression (kind = UastCallKind(name='array_initializer'), argCount = 2)) + UIdentifier (Identifier (arrayOf)) + USimpleNameReferenceExpression (identifier = arrayOf) + ULiteralExpression (value = "sar1") + ULiteralExpression (value = "sar2") UAnnotationMethod (name = C) + UClass (name = C2) + UAnnotation (fqName = AnnotationArray) + UNamedExpression (name = value) + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1)) + UIdentifier (Identifier (Annotation)) + USimpleNameReferenceExpression (identifier = ) + UCallExpression (kind = UastCallKind(name='array_initializer'), argCount = 2)) + UIdentifier (Identifier ([)) + ULiteralExpression (value = "[sar]1") + ULiteralExpression (value = "[sar]2") + UAnnotationMethod (name = C2) diff --git a/plugins/uast-kotlin/testData/AnnotationComplex.render.txt b/plugins/uast-kotlin/testData/AnnotationComplex.render.txt index 8704be645c5..87b57fda535 100644 --- a/plugins/uast-kotlin/testData/AnnotationComplex.render.txt +++ b/plugins/uast-kotlin/testData/AnnotationComplex.render.txt @@ -1,4 +1,5 @@ public abstract annotation Annotation { + public abstract fun strings() : java.lang.String[] = UastEmptyExpression } public final class A { @@ -24,3 +25,7 @@ public abstract annotation AnnotationArray { public final class C { public fun C() = UastEmptyExpression } + +public final class C2 { + public fun C2() = UastEmptyExpression +} diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt index 42c29283bdf..065cc24d8e3 100644 --- a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt @@ -17,6 +17,7 @@ import org.jetbrains.uast.test.env.kotlin.findElementByTextFromPsi import org.jetbrains.uast.visitor.AbstractUastVisitor import org.junit.Assert import org.junit.Test +import kotlin.test.fail as kfail class KotlinUastApiTest : AbstractKotlinUastTest() { @@ -297,8 +298,8 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { @Test fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file -> - file.findElementByTextFromPsi("@AnnotationArray(value = Annotation())") - .findElementByTextFromPsi("Annotation()") + file.findElementByTextFromPsi("@AnnotationArray(value = Annotation(\"sv1\", \"sv2\"))") + .findElementByTextFromPsi("Annotation(\"sv1\", \"sv2\")") .sourcePsiElement .let { referenceExpression -> val convertedUAnnotation = referenceExpression @@ -315,6 +316,44 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { } } + @Test + fun testNestedAnnotationParameters() = doTest("AnnotationComplex") { _, file -> + + fun UFile.annotationAndParam(refText: String, check: (PsiAnnotation, String?) -> Unit) { + findElementByTextFromPsi(refText) + .let { expression -> + val (annotation: PsiAnnotation, paramname: String?) = + getContainingAnnotationEntry(expression) ?: kfail("annotation not found for '$refText' ($expression)") + check(annotation, paramname) + } + } + + file.annotationAndParam("sv1") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals(null, paramname) + } + file.annotationAndParam("sv2") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals(null, paramname) + } + file.annotationAndParam("sar1") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals("strings", paramname) + } + file.annotationAndParam("sar2") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals("strings", paramname) + } + file.annotationAndParam("[sar]1") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals("strings", paramname) + } + file.annotationAndParam("[sar]2") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals("strings", paramname) + } + } + @Test fun testParametersDisorder() = doTest("ParametersDisorder") { _, file -> diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.191 b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.191 index 798d453fdd5..2d88b3a850c 100644 --- a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.191 +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.191 @@ -17,6 +17,7 @@ import org.jetbrains.uast.test.env.kotlin.findElementByTextFromPsi import org.jetbrains.uast.visitor.AbstractUastVisitor import org.junit.Assert import org.junit.Test +import kotlin.test.fail as kfail class KotlinUastApiTest : AbstractKotlinUastTest() { @@ -297,8 +298,8 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { @Test fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file -> - file.findElementByTextFromPsi("@AnnotationArray(value = Annotation())") - .findElementByTextFromPsi("Annotation()") + file.findElementByTextFromPsi("@AnnotationArray(value = Annotation(\"sv1\", \"sv2\"))") + .findElementByTextFromPsi("Annotation(\"sv1\", \"sv2\")") .sourcePsiElement .let { referenceExpression -> val convertedUAnnotation = referenceExpression @@ -315,6 +316,44 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { } } + @Test + fun testNestedAnnotationParameters() = doTest("AnnotationComplex") { _, file -> + + fun UFile.annotationAndParam(refText: String, check: (PsiAnnotation, String?) -> Unit) { + findElementByTextFromPsi(refText) + .let { expression -> + val (annotation: PsiAnnotation, paramname: String?) = + getContainingAnnotationEntry(expression) ?: kfail("annotation not found for '$refText' ($expression)") + check(annotation, paramname) + } + } + + file.annotationAndParam("sv1") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals(null, paramname) + } + file.annotationAndParam("sv2") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals(null, paramname) + } + file.annotationAndParam("sar1") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals("strings", paramname) + } + file.annotationAndParam("sar2") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals("strings", paramname) + } + file.annotationAndParam("[sar]1") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals("strings", paramname) + } + file.annotationAndParam("[sar]2") { annotation, paramname -> + assertEquals("Annotation", annotation.qualifiedName) + assertEquals("strings", paramname) + } + } + @Test fun testParametersDisorder() = doTest("ParametersDisorder") { _, file ->