lightAnnotations: remove several brittle assertions

Add some test cases with "red" code
This commit is contained in:
Pavel V. Talanov
2017-08-22 15:37:23 +03:00
parent a6b446cc4f
commit 7fecec1141
2 changed files with 87 additions and 32 deletions
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.asJava.elements
import com.intellij.lang.Language
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
@@ -53,6 +52,8 @@ abstract class KtLightAbstractAnnotation(parent: PsiElement, computeDelegate: ()
open fun fqNameMatches(fqName: String): Boolean = qualifiedName == fqName
}
private typealias AnnotationValueOrigin = () -> PsiElement?
class KtLightAnnotationForSourceEntry(
private val qualifiedName: String,
override val kotlinOrigin: KtCallElement,
@@ -65,9 +66,9 @@ class KtLightAnnotationForSourceEntry(
open inner class LightElementValue<out D : PsiElement>(
val delegate: D,
private val parent: PsiElement,
private val originalExpressionProvider: () -> PsiElement
valueOrigin: AnnotationValueOrigin
) : PsiAnnotationMemberValue, PsiElement by delegate {
val originalExpression: PsiElement by lazyPub { originalExpressionProvider() }
val originalExpression: PsiElement? by lazyPub(valueOrigin)
fun getConstantValue(): Any? {
val expression = originalExpression as? KtExpression ?: return null
@@ -77,12 +78,12 @@ class KtLightAnnotationForSourceEntry(
}
override fun getReference() = references.singleOrNull()
override fun getReferences() = originalExpression.references
override fun getLanguage(): Language = KotlinLanguage.INSTANCE
override fun getReferences() = originalExpression?.references.orEmpty()
override fun getLanguage() = KotlinLanguage.INSTANCE
override fun getNavigationElement() = originalExpression
override fun getTextRange() = originalExpression.textRange ?: TextRange.EMPTY_RANGE
override fun getTextRange() = originalExpression?.textRange ?: TextRange.EMPTY_RANGE
override fun getParent() = parent
override fun getText(): String = originalExpression.text
override fun getText() = originalExpression?.text.orEmpty()
override fun replace(newElement: PsiElement): PsiElement {
val value = (newElement as? PsiLiteral)?.value as? String ?: return this
@@ -101,24 +102,23 @@ class KtLightAnnotationForSourceEntry(
}
}
private fun getMemberValueAsCallArgument(memberValue: PsiElement, callHolder: KtCallElement): PsiElement {
val resolvedCall = callHolder.getResolvedCall()!!
private fun getMemberValueAsCallArgument(memberValue: PsiElement, callHolder: KtCallElement): PsiElement? {
val resolvedCall = callHolder.getResolvedCall() ?: return null
val annotationConstructor = resolvedCall.resultingDescriptor
val parameterName =
memberValue.getNonStrictParentOfType<PsiNameValuePair>()?.name ?:
memberValue.getNonStrictParentOfType<PsiAnnotationMethod>()?.name ?:
"value"
val parameter = annotationConstructor.valueParameters.singleOrNull { it.name.asString() == parameterName }
?: error("single parameter '$parameterName' was not found in ${annotationConstructor.valueParameters.map { it.name.asString() }}")
val resolvedArgument = resolvedCall.valueArguments[parameter]!!
val parameter = annotationConstructor.valueParameters.singleOrNull { it.name.asString() == parameterName } ?: return null
val resolvedArgument = resolvedCall.valueArguments[parameter] ?: return null
return when (resolvedArgument) {
is DefaultValueArgument -> {
val psi = parameter.source.getPsi()
when (psi) {
is KtParameter -> psi.defaultValue!!
is PsiAnnotationMethod -> psi.defaultValue!!
else -> throw UnsupportedOperationException("$psi of type ${psi?.javaClass}")
is KtParameter -> psi.defaultValue
is PsiAnnotationMethod -> psi.defaultValue
else -> error("$psi of type ${psi?.javaClass}")
}
}
@@ -135,7 +135,7 @@ class KtLightAnnotationForSourceEntry(
it.asKtCall() ?: it
}
else -> throw IllegalArgumentException("resolvedArgument: {$resolvedArgument} cant be processed")
else -> error("resolvedArgument: ${resolvedArgument.javaClass} cant be processed")
}
}
@@ -149,24 +149,24 @@ class KtLightAnnotationForSourceEntry(
open inner class LightExpressionValue<out D : PsiExpression>(
delegate: D,
parent: PsiElement,
originalExpressionProvider: () -> PsiElement
) : LightElementValue<D>(delegate, parent, originalExpressionProvider), PsiExpression {
valueOrigin: AnnotationValueOrigin
) : LightElementValue<D>(delegate, parent, valueOrigin), PsiExpression {
override fun getType(): PsiType? = delegate.type
}
inner class LightStringLiteral(
delegate: PsiLiteralExpression,
parent: PsiElement,
originalExpressionProvider: () -> PsiElement
) : LightExpressionValue<PsiLiteralExpression>(delegate, parent, originalExpressionProvider), PsiLiteralExpression {
valueOrigin: AnnotationValueOrigin
) : LightExpressionValue<PsiLiteralExpression>(delegate, parent, valueOrigin), PsiLiteralExpression {
override fun getValue() = delegate.value
}
inner class LightClassLiteral(
delegate: PsiClassObjectAccessExpression,
parent: PsiElement,
originalExpressionProvider: () -> PsiElement
) : LightExpressionValue<PsiClassObjectAccessExpression>(delegate, parent, originalExpressionProvider), PsiClassObjectAccessExpression {
valueOrigin: AnnotationValueOrigin
) : LightExpressionValue<PsiClassObjectAccessExpression>(delegate, parent, valueOrigin), PsiClassObjectAccessExpression {
override fun getType() = delegate.type
override fun getOperand(): PsiTypeElement = delegate.operand
}
@@ -174,8 +174,8 @@ class KtLightAnnotationForSourceEntry(
inner class LightArrayInitializerValue(
delegate: PsiArrayInitializerMemberValue,
parent: PsiElement,
originalExpressionProvider: () -> PsiElement
) : LightElementValue<PsiArrayInitializerMemberValue>(delegate, parent, originalExpressionProvider), PsiArrayInitializerMemberValue {
valueOrigin: AnnotationValueOrigin
) : LightElementValue<PsiArrayInitializerMemberValue>(delegate, parent, valueOrigin), PsiArrayInitializerMemberValue {
private val _initializers by lazyPub {
delegate.initializers.mapIndexed { i, it ->
wrapAnnotationValue(it, this, {
@@ -187,20 +187,20 @@ class KtLightAnnotationForSourceEntry(
override fun getInitializers() = _initializers
}
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue, parent: PsiElement, ktOriginForElement: () -> PsiElement): PsiAnnotationMemberValue {
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue, parent: PsiElement, ktOrigin: AnnotationValueOrigin): PsiAnnotationMemberValue {
return when {
value is PsiLiteralExpression && value.value is String -> LightStringLiteral(value, parent, ktOriginForElement)
value is PsiClassObjectAccessExpression -> LightClassLiteral(value, parent, ktOriginForElement)
value is PsiExpression -> LightExpressionValue(value, parent, ktOriginForElement)
value is PsiArrayInitializerMemberValue -> LightArrayInitializerValue(value, parent, ktOriginForElement)
value is PsiLiteralExpression && value.value is String -> LightStringLiteral(value, parent, ktOrigin)
value is PsiClassObjectAccessExpression -> LightClassLiteral(value, parent, ktOrigin)
value is PsiExpression -> LightExpressionValue(value, parent, ktOrigin)
value is PsiArrayInitializerMemberValue -> LightArrayInitializerValue(value, parent, ktOrigin)
value is PsiAnnotation -> KtLightAnnotationForSourceEntry(
value.qualifiedName!!,
ktOriginForElement().let {
it.asKtCall() ?: throw UnsupportedOperationException("cant convert $it to KtCallElement")
ktOrigin().let {
it?.asKtCall() ?: throw UnsupportedOperationException("cant convert $it to KtCallElement")
},
parent, { value }
)
else -> LightElementValue(value, parent, ktOriginForElement)
else -> LightElementValue(value, parent, ktOrigin)
}
}
@@ -287,6 +287,61 @@ class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() {
}
fun testWrongNamesPassed() {
myFixture.configureByText("AnnotatedClass.kt", """
annotation class Anno1(val i:Int , val j: Int)
@Anno1(k = 3, l = 5)
class AnnotatedClass
""".trimIndent())
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
val annotation = annotations.first()
TestCase.assertNull(annotation.findAttributeValue("k"))
TestCase.assertNull(annotation.findAttributeValue("l"))
TestCase.assertNull(annotation.findAttributeValue("i"))
TestCase.assertNull(annotation.findAttributeValue("j"))
}
fun testWrongValuesPassed() {
myFixture.configureByText("AnnotatedClass.kt", """
annotation class Anno1(val i: Int , val j: Int)
@Anno1(i = true, j = false)
class AnnotatedClass
""".trimIndent())
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
val annotation = annotations.first()
assertTextAndRange("true", annotation.findAttributeValue("i")!!)
assertTextAndRange("false", annotation.findAttributeValue("j")!!)
}
fun testDuplicateParameters() {
myFixture.configureByText("AnnotatedClass.kt", """
annotation class Anno1(val i:Int , val i: Boolean)
@Anno1(i = true, i = 3)
class AnnotatedClass
""".trimIndent())
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
val annotation = annotations.first()
assertTextAndRange("", annotation.findAttributeValue("i")!!)
}
fun testMissingDefault() {
myFixture.configureByText("AnnotatedClass.kt", """
annotation class Anno1(val i: Int = 0)
@Anno1()
class AnnotatedClass
""".trimIndent())
val (annotation) = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
assertTextAndRange("0", annotation.findAttributeValue("i")!!)
}
private fun assertTextAndRange(expected: String, psiElement: PsiElement) {
TestCase.assertEquals(expected, psiElement.text)
TestCase.assertEquals(expected, psiElement.textRange.substring(psiElement.containingFile.text))