lightAnnotations: KtLightPsiClassObjectAccessExpression added

as implementation of `PsiClassObjectAccessExpression` for class literals in annotations
This commit is contained in:
Nicolay Mitropolsky
2018-06-04 18:19:46 +03:00
committed by xiexed
parent 92727d2945
commit 0fb879dfc2
6 changed files with 135 additions and 36 deletions
@@ -9,14 +9,16 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.impl.LanguageConstantExpressionEvaluator import com.intellij.psi.impl.LanguageConstantExpressionEvaluator
import com.intellij.psi.impl.light.LightIdentifier import com.intellij.psi.impl.light.LightIdentifier
import com.intellij.psi.impl.light.LightTypeElement
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe 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( class KtLightPsiArrayInitializerMemberValue(
override val kotlinOrigin: KtElement, override val kotlinOrigin: KtElement,
@@ -30,7 +32,7 @@ class KtLightPsiArrayInitializerMemberValue(
override fun isPhysical(): Boolean = false override fun isPhysical(): Boolean = false
} }
class KtLightPsiLiteral( open class KtLightPsiLiteral(
override val kotlinOrigin: KtExpression, override val kotlinOrigin: KtExpression,
val lightParent: PsiElement val lightParent: PsiElement
) : KtLightElementBase(lightParent), PsiLiteralExpression { ) : KtLightElementBase(lightParent), PsiLiteralExpression {
@@ -41,19 +43,7 @@ class KtLightPsiLiteral(
override fun getType(): PsiType? { override fun getType(): PsiType? {
val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin) val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin)
val kotlinType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, kotlinOrigin] ?: return null val kotlinType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, kotlinOrigin] ?: return null
val typeFqName = kotlinType.constructor.declarationDescriptor?.fqNameSafe?.asString() ?: return null return psiType(kotlinType, kotlinOrigin)
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
}
} }
override fun getParent(): PsiElement = lightParent override fun getParent(): PsiElement = lightParent
@@ -70,6 +60,34 @@ class KtLightPsiLiteral(
override fun getReferences(): Array<out PsiReference> = kotlinOrigin.references override fun getReferences(): Array<out PsiReference> = 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<SimpleType>() ?: 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( class KtLightPsiNameValuePair private constructor(
override val kotlinOrigin: KtElement, override val kotlinOrigin: KtElement,
val valueArgument: KtValueArgument, val valueArgument: KtValueArgument,
@@ -9,14 +9,16 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.impl.LanguageConstantExpressionEvaluator import com.intellij.psi.impl.LanguageConstantExpressionEvaluator
import com.intellij.psi.impl.light.LightIdentifier import com.intellij.psi.impl.light.LightIdentifier
import com.intellij.psi.impl.light.LightTypeElement
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe 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( class KtLightPsiArrayInitializerMemberValue(
override val kotlinOrigin: KtElement, override val kotlinOrigin: KtElement,
@@ -30,7 +32,7 @@ class KtLightPsiArrayInitializerMemberValue(
override fun isPhysical(): Boolean = true override fun isPhysical(): Boolean = true
} }
class KtLightPsiLiteral( open class KtLightPsiLiteral(
override val kotlinOrigin: KtExpression, override val kotlinOrigin: KtExpression,
val lightParent: PsiElement val lightParent: PsiElement
) : KtLightElementBase(lightParent), PsiLiteralExpression { ) : KtLightElementBase(lightParent), PsiLiteralExpression {
@@ -41,19 +43,7 @@ class KtLightPsiLiteral(
override fun getType(): PsiType? { override fun getType(): PsiType? {
val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin) val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin)
val kotlinType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, kotlinOrigin] ?: return null val kotlinType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, kotlinOrigin] ?: return null
val typeFqName = kotlinType.constructor.declarationDescriptor?.fqNameSafe?.asString() ?: return null return psiType(kotlinType, kotlinOrigin)
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
}
} }
override fun getParent(): PsiElement = lightParent override fun getParent(): PsiElement = lightParent
@@ -70,6 +60,34 @@ class KtLightPsiLiteral(
override fun getReferences(): Array<out PsiReference> = kotlinOrigin.references override fun getReferences(): Array<out PsiReference> = 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<SimpleType>() ?: 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( class KtLightPsiNameValuePair private constructor(
override val kotlinOrigin: KtElement, override val kotlinOrigin: KtElement,
val valueArgument: KtValueArgument, val valueArgument: KtValueArgument,
@@ -330,6 +330,9 @@ private fun KtElement.getResolvedCall(): ResolvedCall<out CallableDescriptor>? {
fun convertToLightAnnotationMemberValue(lightParent: PsiElement, argument: KtExpression): PsiAnnotationMemberValue { fun convertToLightAnnotationMemberValue(lightParent: PsiElement, argument: KtExpression): PsiAnnotationMemberValue {
val argument = unwrapCall(argument) val argument = unwrapCall(argument)
when (argument) { when (argument) {
is KtClassLiteralExpression -> {
return KtLightPsiClassObjectAccessExpression(argument, lightParent)
}
is KtStringTemplateExpression, is KtConstantExpression -> { is KtStringTemplateExpression, is KtConstantExpression -> {
return KtLightPsiLiteral(argument, lightParent) return KtLightPsiLiteral(argument, lightParent)
} }
@@ -22,6 +22,7 @@ import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable import org.jetbrains.annotations.Nullable
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.cannotModify import org.jetbrains.kotlin.asJava.classes.cannotModify
import org.jetbrains.kotlin.asJava.classes.lazyPub import org.jetbrains.kotlin.asJava.classes.lazyPub
@@ -331,6 +332,9 @@ private fun KtElement.getResolvedCall(): ResolvedCall<out CallableDescriptor>? {
fun convertToLightAnnotationMemberValue(lightParent: PsiElement, argument: KtExpression): PsiAnnotationMemberValue { fun convertToLightAnnotationMemberValue(lightParent: PsiElement, argument: KtExpression): PsiAnnotationMemberValue {
val argument = unwrapCall(argument) val argument = unwrapCall(argument)
when (argument) { when (argument) {
is KtClassLiteralExpression -> {
return KtLightPsiClassObjectAccessExpression(argument, lightParent)
}
is KtStringTemplateExpression, is KtConstantExpression -> { is KtStringTemplateExpression, is KtConstantExpression -> {
return KtLightPsiLiteral(argument, lightParent) return KtLightPsiLiteral(argument, lightParent)
} }
@@ -393,8 +397,10 @@ private fun getAnnotationName(callee: KtExpression): String? {
return null return null
} }
@TestOnly
var accessAnnotationsClsDelegateIsAllowed = false var accessAnnotationsClsDelegateIsAllowed = false
@TestOnly
fun <T> withAllowedAnnotationsClsDelegate(body: () -> T): T { fun <T> withAllowedAnnotationsClsDelegate(body: () -> T): T {
val prev = accessAnnotationsClsDelegateIsAllowed val prev = accessAnnotationsClsDelegateIsAllowed
try { try {
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.idea.completion.test.assertInstanceOf import org.jetbrains.kotlin.idea.completion.test.assertInstanceOf
import org.jetbrains.kotlin.idea.facet.configureFacet import org.jetbrains.kotlin.idea.facet.configureFacet
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet 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.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor 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() { fun testAnnotationsInAnnotationsArrayDeclarations() {
myFixture.addClass(""" myFixture.addClass("""
public @interface OuterAnnotation { public @interface OuterAnnotation {
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.idea.completion.test.assertInstanceOf import org.jetbrains.kotlin.idea.completion.test.assertInstanceOf
import org.jetbrains.kotlin.idea.facet.configureFacet import org.jetbrains.kotlin.idea.facet.configureFacet
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet 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.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor 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() { fun testAnnotationsInAnnotationsArrayDeclarations() {
myFixture.addClass(""" myFixture.addClass("""
public @interface OuterAnnotation { public @interface OuterAnnotation {