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.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<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(
override val kotlinOrigin: KtElement,
val valueArgument: KtValueArgument,
@@ -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<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(
override val kotlinOrigin: KtElement,
val valueArgument: KtValueArgument,
@@ -330,6 +330,9 @@ private fun KtElement.getResolvedCall(): ResolvedCall<out CallableDescriptor>? {
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)
}
@@ -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<out CallableDescriptor>? {
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 <T> withAllowedAnnotationsClsDelegate(body: () -> T): T {
val prev = accessAnnotationsClsDelegateIsAllowed
try {
@@ -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 {
@@ -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 {