From 0fb879dfc25c764a1b9c857a56cd65f64724d793 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Mon, 4 Jun 2018 18:19:46 +0300 Subject: [PATCH] lightAnnotations: `KtLightPsiClassObjectAccessExpression` added as implementation of `PsiClassObjectAccessExpression` for class literals in annotations --- .../elements/KtLightAnnotationsValues.kt | 54 ++++++++++++------- .../elements/KtLightAnnotationsValues.kt.173 | 54 ++++++++++++------- .../asJava/elements/lightAnnotations.kt | 3 ++ .../asJava/elements/lightAnnotations.kt.173 | 6 +++ .../kotlin/asJava/KtLightAnnotationTest.kt | 27 ++++++++++ .../asJava/KtLightAnnotationTest.kt.173 | 27 ++++++++++ 6 files changed, 135 insertions(+), 36 deletions(-) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt index 43dd696052d..e0da5f4e256 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt @@ -9,14 +9,16 @@ import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.* import com.intellij.psi.impl.LanguageConstantExpressionEvaluator import com.intellij.psi.impl.light.LightIdentifier +import com.intellij.psi.impl.light.LightTypeElement import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.asJava.LightClassGenerationSupport -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.SimpleType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class KtLightPsiArrayInitializerMemberValue( override val kotlinOrigin: KtElement, @@ -30,7 +32,7 @@ class KtLightPsiArrayInitializerMemberValue( override fun isPhysical(): Boolean = false } -class KtLightPsiLiteral( +open class KtLightPsiLiteral( override val kotlinOrigin: KtExpression, val lightParent: PsiElement ) : KtLightElementBase(lightParent), PsiLiteralExpression { @@ -41,19 +43,7 @@ class KtLightPsiLiteral( override fun getType(): PsiType? { val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin) val kotlinType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, kotlinOrigin] ?: return null - val typeFqName = kotlinType.constructor.declarationDescriptor?.fqNameSafe?.asString() ?: return null - return when (typeFqName) { - "kotlin.Int" -> PsiType.INT - "kotlin.Long" -> PsiType.LONG - "kotlin.Short" -> PsiType.SHORT - "kotlin.Boolean" -> PsiType.BOOLEAN - "kotlin.Byte" -> PsiType.BYTE - "kotlin.Char" -> PsiType.CHAR - "kotlin.Double" -> PsiType.DOUBLE - "kotlin.Float" -> PsiType.FLOAT - "kotlin.String" -> PsiType.getJavaLangString(kotlinOrigin.manager, GlobalSearchScope.projectScope(kotlinOrigin.project)) - else -> null - } + return psiType(kotlinType, kotlinOrigin) } override fun getParent(): PsiElement = lightParent @@ -70,6 +60,34 @@ class KtLightPsiLiteral( override fun getReferences(): Array = kotlinOrigin.references } +class KtLightPsiClassObjectAccessExpression(override val kotlinOrigin: KtClassLiteralExpression, lightParent: PsiElement) : + KtLightPsiLiteral(kotlinOrigin, lightParent), PsiClassObjectAccessExpression { + override fun getType(): PsiType { + val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin) + val kotlinType = bindingContext[BindingContext.COMPILE_TIME_VALUE, kotlinOrigin] + ?.getValue(TypeUtils.NO_EXPECTED_TYPE).safeAs() ?: return PsiType.VOID + return psiType(kotlinType, kotlinOrigin) ?: return PsiType.VOID + } + + override fun getOperand(): PsiTypeElement = LightTypeElement(kotlinOrigin.manager, type) +} + +private fun psiType(kotlinType: KotlinType, context: PsiElement): PsiType? { + val typeFqName = kotlinType.constructor.declarationDescriptor?.fqNameSafe?.asString() ?: return null + return when (typeFqName) { + "kotlin.Int" -> PsiType.INT + "kotlin.Long" -> PsiType.LONG + "kotlin.Short" -> PsiType.SHORT + "kotlin.Boolean" -> PsiType.BOOLEAN + "kotlin.Byte" -> PsiType.BYTE + "kotlin.Char" -> PsiType.CHAR + "kotlin.Double" -> PsiType.DOUBLE + "kotlin.Float" -> PsiType.FLOAT + "kotlin.String" -> PsiType.getJavaLangString(context.manager, GlobalSearchScope.projectScope(context.project)) + else -> PsiType.getTypeByName(typeFqName, context.project, context.resolveScope) + } +} + class KtLightPsiNameValuePair private constructor( override val kotlinOrigin: KtElement, val valueArgument: KtValueArgument, diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt.173 b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt.173 index 4ee02af06d9..87cab2de1bd 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt.173 +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightAnnotationsValues.kt.173 @@ -9,14 +9,16 @@ import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.* import com.intellij.psi.impl.LanguageConstantExpressionEvaluator import com.intellij.psi.impl.light.LightIdentifier +import com.intellij.psi.impl.light.LightTypeElement import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.asJava.LightClassGenerationSupport -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.SimpleType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class KtLightPsiArrayInitializerMemberValue( override val kotlinOrigin: KtElement, @@ -30,7 +32,7 @@ class KtLightPsiArrayInitializerMemberValue( override fun isPhysical(): Boolean = true } -class KtLightPsiLiteral( +open class KtLightPsiLiteral( override val kotlinOrigin: KtExpression, val lightParent: PsiElement ) : KtLightElementBase(lightParent), PsiLiteralExpression { @@ -41,19 +43,7 @@ class KtLightPsiLiteral( override fun getType(): PsiType? { val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin) val kotlinType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, kotlinOrigin] ?: return null - val typeFqName = kotlinType.constructor.declarationDescriptor?.fqNameSafe?.asString() ?: return null - return when (typeFqName) { - "kotlin.Int" -> PsiType.INT - "kotlin.Long" -> PsiType.LONG - "kotlin.Short" -> PsiType.SHORT - "kotlin.Boolean" -> PsiType.BOOLEAN - "kotlin.Byte" -> PsiType.BYTE - "kotlin.Char" -> PsiType.CHAR - "kotlin.Double" -> PsiType.DOUBLE - "kotlin.Float" -> PsiType.FLOAT - "kotlin.String" -> PsiType.getJavaLangString(kotlinOrigin.manager, GlobalSearchScope.projectScope(kotlinOrigin.project)) - else -> null - } + return psiType(kotlinType, kotlinOrigin) } override fun getParent(): PsiElement = lightParent @@ -70,6 +60,34 @@ class KtLightPsiLiteral( override fun getReferences(): Array = kotlinOrigin.references } +class KtLightPsiClassObjectAccessExpression(override val kotlinOrigin: KtClassLiteralExpression, lightParent: PsiElement) : + KtLightPsiLiteral(kotlinOrigin, lightParent), PsiClassObjectAccessExpression { + override fun getType(): PsiType { + val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin) + val kotlinType = bindingContext[BindingContext.COMPILE_TIME_VALUE, kotlinOrigin] + ?.getValue(TypeUtils.NO_EXPECTED_TYPE).safeAs() ?: return PsiType.VOID + return psiType(kotlinType, kotlinOrigin) ?: return PsiType.VOID + } + + override fun getOperand(): PsiTypeElement = LightTypeElement(kotlinOrigin.manager, type) +} + +private fun psiType(kotlinType: KotlinType, context: PsiElement): PsiType? { + val typeFqName = kotlinType.constructor.declarationDescriptor?.fqNameSafe?.asString() ?: return null + return when (typeFqName) { + "kotlin.Int" -> PsiType.INT + "kotlin.Long" -> PsiType.LONG + "kotlin.Short" -> PsiType.SHORT + "kotlin.Boolean" -> PsiType.BOOLEAN + "kotlin.Byte" -> PsiType.BYTE + "kotlin.Char" -> PsiType.CHAR + "kotlin.Double" -> PsiType.DOUBLE + "kotlin.Float" -> PsiType.FLOAT + "kotlin.String" -> PsiType.getJavaLangString(context.manager, GlobalSearchScope.projectScope(context.project)) + else -> PsiType.getTypeByName(typeFqName, context.project, context.resolveScope) + } +} + class KtLightPsiNameValuePair private constructor( override val kotlinOrigin: KtElement, val valueArgument: KtValueArgument, 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 a4f0b766009..eb0f081157e 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 @@ -330,6 +330,9 @@ private fun KtElement.getResolvedCall(): ResolvedCall? { fun convertToLightAnnotationMemberValue(lightParent: PsiElement, argument: KtExpression): PsiAnnotationMemberValue { val argument = unwrapCall(argument) when (argument) { + is KtClassLiteralExpression -> { + return KtLightPsiClassObjectAccessExpression(argument, lightParent) + } is KtStringTemplateExpression, is KtConstantExpression -> { return KtLightPsiLiteral(argument, lightParent) } 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 c280a3d291e..b261464bbd0 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 @@ -22,6 +22,7 @@ import com.intellij.psi.* import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.Nullable +import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.classes.cannotModify import org.jetbrains.kotlin.asJava.classes.lazyPub @@ -331,6 +332,9 @@ private fun KtElement.getResolvedCall(): ResolvedCall? { fun convertToLightAnnotationMemberValue(lightParent: PsiElement, argument: KtExpression): PsiAnnotationMemberValue { val argument = unwrapCall(argument) when (argument) { + is KtClassLiteralExpression -> { + return KtLightPsiClassObjectAccessExpression(argument, lightParent) + } is KtStringTemplateExpression, is KtConstantExpression -> { return KtLightPsiLiteral(argument, lightParent) } @@ -393,8 +397,10 @@ private fun getAnnotationName(callee: KtExpression): String? { return null } +@TestOnly var accessAnnotationsClsDelegateIsAllowed = false +@TestOnly fun withAllowedAnnotationsClsDelegate(body: () -> T): T { val prev = accessAnnotationsClsDelegateIsAllowed try { diff --git a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt index c5b41db5bfe..5b82777c77d 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.idea.completion.test.assertInstanceOf import org.jetbrains.kotlin.idea.facet.configureFacet import org.jetbrains.kotlin.idea.facet.getOrCreateFacet +import org.jetbrains.kotlin.idea.search.projectScope import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor @@ -208,6 +209,32 @@ class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() { }) } + fun testClassLiteral() { + myFixture.addClass( + """ + public @interface ClazzAnnotation { + Class cls(); + } + """.trimIndent() + ) + + myFixture.configureByText( + "AnnotatedClass.kt", """ + @ClazzAnnotation(cls = String::class) + class AnnotatedClass + """.trimIndent() + ) + myFixture.testHighlighting("AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("cls") as PsiClassObjectAccessExpression + assertTextAndRange("String::class", annotationAttributeVal) + TestCase.assertEquals( + PsiType.getJavaLangString(myFixture.psiManager, myFixture.project.projectScope()), + annotationAttributeVal.operand.type + ) + } + fun testAnnotationsInAnnotationsArrayDeclarations() { myFixture.addClass(""" public @interface OuterAnnotation { diff --git a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 index 25de982249c..aafd54dec58 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 +++ b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.idea.completion.test.assertInstanceOf import org.jetbrains.kotlin.idea.facet.configureFacet import org.jetbrains.kotlin.idea.facet.getOrCreateFacet +import org.jetbrains.kotlin.idea.search.projectScope import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor @@ -209,6 +210,32 @@ class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() { }) } + fun testClassLiteral() { + myFixture.addClass( + """ + public @interface ClazzAnnotation { + Class cls(); + } + """.trimIndent() + ) + + myFixture.configureByText( + "AnnotatedClass.kt", """ + @ClazzAnnotation(cls = String::class) + class AnnotatedClass + """.trimIndent() + ) + myFixture.testHighlighting("AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("cls") as PsiClassObjectAccessExpression + assertTextAndRange("String::class", annotationAttributeVal) + TestCase.assertEquals( + PsiType.getJavaLangString(myFixture.psiManager, myFixture.project.projectScope()), + annotationAttributeVal.operand.type + ) + } + fun testAnnotationsInAnnotationsArrayDeclarations() { myFixture.addClass(""" public @interface OuterAnnotation {