From ebd6caaa714e64a4b5d01a3a475d057cd9e23709 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Wed, 26 Dec 2018 15:54:16 +0300 Subject: [PATCH] Light-annotations: fix for reading class-literal varargs (KT-29027, IDEA-204252) --- .../asJava/elements/lightAnnotations.kt | 8 +- .../asJava/elements/lightAnnotations.kt.173 | 8 +- .../kotlin/asJava/KtLightAnnotationTest.kt | 33 ++++++++ .../asJava/KtLightAnnotationTest.kt.173 | 82 ++++++++++++++++++- 4 files changed, 123 insertions(+), 8 deletions(-) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt index 5ac4b73e8cf..7f69bac6681 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt @@ -157,9 +157,13 @@ class KtLightAnnotationForSourceEntry( val valueArguments = callEntry.value.arguments val argument = valueArguments.firstOrNull()?.getArgumentExpression() ?: return null - if (!callEntry.key.type.let { KotlinBuiltIns.isArray(it) }) return null + if (!callEntry.key.type.let { KotlinBuiltIns.isArrayOrPrimitiveArray(it) }) return null - if (argument !is KtStringTemplateExpression && argument !is KtConstantExpression && getAnnotationName(argument) == null) { + if (argument !is KtStringTemplateExpression && + argument !is KtConstantExpression && + argument !is KtClassLiteralExpression && + getAnnotationName(argument) == null + ) { return null } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 index 5ce28aff1db..06c8ad8f398 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 @@ -159,9 +159,13 @@ class KtLightAnnotationForSourceEntry( val valueArguments = callEntry.value.arguments val argument = valueArguments.firstOrNull()?.getArgumentExpression() ?: return null - if (!callEntry.key.type.let { KotlinBuiltIns.isArray(it) }) return null + if (!callEntry.key.type.let { KotlinBuiltIns.isArrayOrPrimitiveArray(it) }) return null - if (argument !is KtStringTemplateExpression && argument !is KtConstantExpression && getAnnotationName(argument) == null) { + if (argument !is KtStringTemplateExpression && + argument !is KtConstantExpression && + argument !is KtClassLiteralExpression && + getAnnotationName(argument) == null + ) { return null } diff --git a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt index a1283d761cb..8d02f1c2062 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt @@ -474,6 +474,39 @@ class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() { } + private fun doVarargTest(type: String, parameters: List) { + val paramsJoined = parameters.joinToString(", ") + + myFixture.addClass( + """ + public @interface Annotation { + $type[] value() default {}; + } + """.trimIndent() + ) + + myFixture.configureByText( + "AnnotatedClass.kt", """ + @Annotation($paramsJoined) + open class AnnotatedClass + """.trimIndent() + ) + myFixture.testHighlighting("Annotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("value") as PsiArrayInitializerMemberValue + assertTextAndRange("($paramsJoined)", annotationAttributeVal) + UsefulTestCase.assertInstanceOf(annotationAttributeVal.parent, PsiNameValuePair::class.java) + for ((i, arg) in parameters.withIndex()) { + assertTextAndRange(arg, annotationAttributeVal.initializers[i]) + } + } + + + fun testVarargInt() = doVarargTest("int", listOf("1", "2", "3")) + + fun testVarargClasses() = doVarargTest("""Class""", listOf("Any::class", "String::class", "Int::class")) + fun testVarargWithSpread() { myFixture.addClass(""" public @interface Annotation { diff --git a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 index ab4b7fca6d9..78f9c10df93 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 +++ b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 @@ -27,6 +27,7 @@ import com.intellij.testFramework.LightProjectDescriptor import com.intellij.testFramework.UsefulTestCase import junit.framework.TestCase import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry +import org.jetbrains.kotlin.asJava.elements.KtLightPsiArrayInitializerMemberValue import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.idea.completion.test.assertInstanceOf import org.jetbrains.kotlin.idea.facet.configureFacet @@ -241,6 +242,44 @@ class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() { ) } + fun testArrayOfClassLiterals() { + myFixture.addClass( + """ + public @interface ClazzAnnotation { + Class[] cls(); + } + """.trimIndent() + ) + + myFixture.configureByText( + "AnnotatedClass.kt", """ + @ClazzAnnotation(cls = [String::class, Throwable::class, ShortArray::class, Array>::class, Long::class, Unit::class]) + class AnnotatedClass + """.trimIndent() + ) + myFixture.testHighlighting("AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("cls") as KtLightPsiArrayInitializerMemberValue + assertTextAndRange( + "[String::class, Throwable::class, ShortArray::class, Array>::class, Long::class, Unit::class]", + annotationAttributeVal + ) + val classLiterals = annotationAttributeVal.initializers.toList().map { it as PsiClassObjectAccessExpression } + val scope = GlobalSearchScope.everythingScope(project) + TestCase.assertEquals( + listOf( + PsiType.getJavaLangString(myFixture.psiManager, scope), + PsiType.getTypeByName("java.lang.Throwable", project, scope), + PsiType.SHORT.createArrayType(), + PsiType.getTypeByName("java.lang.Integer", project, scope).createArrayType().createArrayType(), + PsiType.LONG, + PsiType.getTypeByName("kotlin.Unit", project, scope) + ), + classLiterals.map { it.operand.type } + ) + } + fun testAnnotationsInAnnotationsArrayDeclarations() { myFixture.addClass(""" public @interface OuterAnnotation { @@ -437,6 +476,39 @@ class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() { } + private fun doVarargTest(type: String, parameters: List) { + val paramsJoined = parameters.joinToString(", ") + + myFixture.addClass( + """ + public @interface Annotation { + $type[] value() default {}; + } + """.trimIndent() + ) + + myFixture.configureByText( + "AnnotatedClass.kt", """ + @Annotation($paramsJoined) + open class AnnotatedClass + """.trimIndent() + ) + myFixture.testHighlighting("Annotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("value") as PsiArrayInitializerMemberValue + assertTextAndRange("($paramsJoined)", annotationAttributeVal) + UsefulTestCase.assertInstanceOf(annotationAttributeVal.parent, PsiNameValuePair::class.java) + for ((i, arg) in parameters.withIndex()) { + assertTextAndRange(arg, annotationAttributeVal.initializers[i]) + } + } + + + fun testVarargInt() = doVarargTest("int", listOf("1", "2", "3")) + + fun testVarargClasses() = doVarargTest("""Class""", listOf("Any::class", "String::class", "Int::class")) + fun testVarargWithSpread() { myFixture.addClass(""" public @interface Annotation { @@ -687,10 +759,12 @@ class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() { } private fun PsiModifierListOwner.expectAnnotations(number: Int): Array = - this.modifierList!!.annotations.apply { - TestCase.assertEquals("expected one annotation, found ${this.joinToString(", ") { it.qualifiedName ?: "unknown" }}", - number, size) - } + this.modifierList!!.annotations.apply { + assertEquals( + "expected $number annotation(s), found [${this.joinToString(", ") { it.qualifiedName ?: "unknown" }}]", + number, size + ) + } private fun configureKotlinVersion(version: String) { WriteAction.run {