181: Light annotations made strictly non-physical (KT-22565)
(cherry picked from commit cf6e21d)
This commit is contained in:
committed by
Nikolay Krasko
parent
5a1a6a3780
commit
9bac6c5a71
+379
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.asJava.elements
|
||||
|
||||
import com.intellij.openapi.diagnostic.Attachment
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.*
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.asJava.classes.cannotModify
|
||||
import org.jetbrains.kotlin.asJava.classes.lazyPub
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.asJava.elements.lightAnnotations")
|
||||
|
||||
abstract class KtLightAbstractAnnotation(parent: PsiElement, computeDelegate: () -> PsiAnnotation) :
|
||||
KtLightElementBase(parent), PsiAnnotation, KtLightElement<KtCallElement, PsiAnnotation> {
|
||||
override val clsDelegate by lazyPub(computeDelegate)
|
||||
|
||||
override fun getNameReferenceElement() = clsDelegate.nameReferenceElement
|
||||
|
||||
override fun getOwner() = parent as? PsiAnnotationOwner
|
||||
|
||||
override fun getMetaData() = clsDelegate.metaData
|
||||
|
||||
override fun getParameterList() = clsDelegate.parameterList
|
||||
|
||||
override fun canNavigate(): Boolean = super<KtLightElementBase>.canNavigate()
|
||||
|
||||
override fun canNavigateToSource(): Boolean = super<KtLightElementBase>.canNavigateToSource()
|
||||
|
||||
override fun navigate(requestFocus: Boolean) = super<KtLightElementBase>.navigate(requestFocus)
|
||||
|
||||
open fun fqNameMatches(fqName: String): Boolean = qualifiedName == fqName
|
||||
}
|
||||
|
||||
private typealias AnnotationValueOrigin = () -> PsiElement?
|
||||
|
||||
class KtLightAnnotationForSourceEntry(
|
||||
private val qualifiedName: String,
|
||||
override val kotlinOrigin: KtCallElement,
|
||||
parent: PsiElement,
|
||||
computeDelegate: () -> PsiAnnotation
|
||||
) : KtLightAbstractAnnotation(parent, computeDelegate) {
|
||||
|
||||
override fun getQualifiedName() = qualifiedName
|
||||
|
||||
open inner class LightElementValue<out D : PsiElement>(
|
||||
val delegate: D,
|
||||
private val parent: PsiElement,
|
||||
valueOrigin: AnnotationValueOrigin
|
||||
) : PsiAnnotationMemberValue, PsiCompiledElement, PsiElement by delegate {
|
||||
override fun getMirror(): PsiElement = delegate
|
||||
|
||||
val originalExpression: PsiElement? by lazyPub(valueOrigin)
|
||||
|
||||
fun getConstantValue(): Any? {
|
||||
val expression = originalExpression as? KtExpression ?: return null
|
||||
val annotationEntry = this@KtLightAnnotationForSourceEntry.kotlinOrigin
|
||||
val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry)
|
||||
return context[BindingContext.COMPILE_TIME_VALUE, expression]?.getValue(TypeUtils.NO_EXPECTED_TYPE)
|
||||
}
|
||||
|
||||
override fun getReference() = references.singleOrNull()
|
||||
override fun getReferences() = originalExpression?.references.orEmpty()
|
||||
override fun getLanguage() = KotlinLanguage.INSTANCE
|
||||
override fun getNavigationElement() = originalExpression
|
||||
override fun isPhysical(): Boolean = false
|
||||
override fun getTextRange() = originalExpression?.textRange ?: TextRange.EMPTY_RANGE
|
||||
override fun getStartOffsetInParent() = originalExpression?.startOffsetInParent ?: 0
|
||||
override fun getParent() = parent
|
||||
override fun getText() = originalExpression?.text.orEmpty()
|
||||
override fun getContainingFile(): PsiFile? = if (originalExpression?.containingFile == kotlinOrigin.containingFile)
|
||||
kotlinOrigin.containingFile else delegate.containingFile
|
||||
|
||||
override fun replace(newElement: PsiElement): PsiElement {
|
||||
val value = (newElement as? PsiLiteral)?.value as? String ?: return this
|
||||
val origin = originalExpression
|
||||
|
||||
val exprToReplace =
|
||||
if (origin is KtCallExpression /*arrayOf*/) {
|
||||
unwrapArray(origin.valueArguments)
|
||||
}
|
||||
else {
|
||||
origin as? KtExpression
|
||||
} ?: return this
|
||||
exprToReplace.replace(KtPsiFactory(this).createExpression("\"${StringUtil.escapeStringCharacters(value)}\""))
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
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>()?.takeIf { it.containingClass?.isAnnotationType == true }?.name ?:
|
||||
"value"
|
||||
|
||||
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 -> error("$psi of type ${psi?.javaClass}")
|
||||
}
|
||||
}
|
||||
|
||||
is ExpressionValueArgument -> {
|
||||
val argExpression = resolvedArgument.valueArgument?.getArgumentExpression()
|
||||
argExpression?.asKtCall()
|
||||
?: argExpression
|
||||
?: error("resolvedArgument ($resolvedArgument) has no arg expression")
|
||||
}
|
||||
|
||||
is VarargValueArgument ->
|
||||
memberValue.unwrapArray(resolvedArgument.arguments)
|
||||
?: resolvedArgument.arguments.first().asElement().let {
|
||||
(it as? KtValueArgument)
|
||||
?.takeIf {
|
||||
it.getSpreadElement() != null ||
|
||||
it.getArgumentName() != null ||
|
||||
it.getArgumentExpression() is KtCollectionLiteralExpression
|
||||
}
|
||||
?.getArgumentExpression() ?: it.parent
|
||||
}
|
||||
|
||||
else -> error("resolvedArgument: ${resolvedArgument.javaClass} cant be processed")
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiElement.unwrapArray(arguments: List<ValueArgument>): PsiElement? {
|
||||
val arrayInitializer = parent as? PsiArrayInitializerMemberValue ?: return null
|
||||
val exprIndex = arrayInitializer.initializers.indexOf(this)
|
||||
if (exprIndex < 0 || exprIndex >= arguments.size) return null
|
||||
return arguments[exprIndex].getArgumentExpression()
|
||||
}
|
||||
|
||||
open inner class LightExpressionValue<out D : PsiExpression>(
|
||||
delegate: D,
|
||||
parent: PsiElement,
|
||||
valueOrigin: AnnotationValueOrigin
|
||||
) : LightElementValue<D>(delegate, parent, valueOrigin), PsiExpression {
|
||||
override fun getType(): PsiType? = delegate.type
|
||||
}
|
||||
|
||||
inner class LightPsiLiteral(
|
||||
delegate: PsiLiteralExpression,
|
||||
parent: PsiElement,
|
||||
valueOrigin: AnnotationValueOrigin
|
||||
) : LightExpressionValue<PsiLiteralExpression>(delegate, parent, valueOrigin), PsiLiteralExpression {
|
||||
override fun getValue() = delegate.value
|
||||
}
|
||||
|
||||
inner class LightClassLiteral(
|
||||
delegate: PsiClassObjectAccessExpression,
|
||||
parent: PsiElement,
|
||||
valueOrigin: AnnotationValueOrigin
|
||||
) : LightExpressionValue<PsiClassObjectAccessExpression>(delegate, parent, valueOrigin), PsiClassObjectAccessExpression {
|
||||
override fun getType() = delegate.type
|
||||
override fun getOperand(): PsiTypeElement = delegate.operand
|
||||
}
|
||||
|
||||
inner class LightArrayInitializerValue(
|
||||
delegate: PsiArrayInitializerMemberValue,
|
||||
parent: PsiElement,
|
||||
valueOrigin: AnnotationValueOrigin
|
||||
) : LightElementValue<PsiArrayInitializerMemberValue>(delegate, parent, valueOrigin), PsiArrayInitializerMemberValue {
|
||||
private val _initializers by lazyPub {
|
||||
delegate.initializers.mapIndexed { i, memberValue ->
|
||||
wrapAnnotationValue(memberValue, this, {
|
||||
originalExpression.let { ktOrigin ->
|
||||
when {
|
||||
ktOrigin is KtCallExpression
|
||||
&& memberValue is PsiAnnotation
|
||||
&& isAnnotationConstructorCall(ktOrigin, memberValue) -> ktOrigin
|
||||
ktOrigin is KtValueArgumentList -> ktOrigin.arguments.getOrNull(i)?.getArgumentExpression()
|
||||
ktOrigin is KtCallElement -> ktOrigin.valueArguments.getOrNull(i)?.getArgumentExpression()
|
||||
ktOrigin is KtCollectionLiteralExpression -> ktOrigin.getInnerExpressions().getOrNull(i)
|
||||
delegate.initializers.size == 1 -> ktOrigin
|
||||
else -> null
|
||||
}.also {
|
||||
if (it == null)
|
||||
LOG.error("error wrapping ${memberValue.javaClass} for ${ktOrigin?.javaClass} in ${ktOrigin?.containingFile}",
|
||||
Attachment(
|
||||
"source_fragments.txt",
|
||||
"origin: '${psiReport(ktOrigin)}', delegate: ${psiReport(delegate)}, parent: ${psiReport(parent)}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
override fun getInitializers() = _initializers
|
||||
}
|
||||
|
||||
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue, parent: PsiElement, ktOrigin: AnnotationValueOrigin): PsiAnnotationMemberValue =
|
||||
when {
|
||||
value is PsiLiteralExpression -> LightPsiLiteral(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 -> {
|
||||
val origin = ktOrigin()
|
||||
val ktCallElement = origin?.asKtCall()
|
||||
val qualifiedName = value.qualifiedName
|
||||
if (qualifiedName != null && ktCallElement != null)
|
||||
KtLightAnnotationForSourceEntry(qualifiedName, ktCallElement, parent, { value })
|
||||
else {
|
||||
LOG.error("can't convert ${origin?.javaClass} to KtCallElement in ${origin?.containingFile} (value = ${value.javaClass})",
|
||||
Attachment("source_fragments.txt", "origin: '${psiReport(origin)}', value: '${psiReport(value)}'"))
|
||||
LightElementValue(value, parent, ktOrigin) // or maybe create a LightErrorAnnotationMemberValue instead?
|
||||
}
|
||||
}
|
||||
else -> LightElementValue(value, parent, ktOrigin)
|
||||
}
|
||||
|
||||
override fun getName() = null
|
||||
|
||||
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue): PsiAnnotationMemberValue = wrapAnnotationValue(value, this, {
|
||||
getMemberValueAsCallArgument(value, kotlinOrigin)
|
||||
})
|
||||
|
||||
override fun findAttributeValue(name: String?) = clsDelegate.findAttributeValue(name)?.let { wrapAnnotationValue(it) }
|
||||
|
||||
override fun findDeclaredAttributeValue(name: String?) = clsDelegate.findDeclaredAttributeValue(name)?.let { wrapAnnotationValue(it) }
|
||||
|
||||
override fun getParameterList(): PsiAnnotationParameterList = KtLightAnnotationParameterList(super.getParameterList())
|
||||
|
||||
inner class KtLightAnnotationParameterList(private val list: PsiAnnotationParameterList) : KtLightElementBase(this),
|
||||
PsiAnnotationParameterList {
|
||||
override val kotlinOrigin get() = null
|
||||
override fun getAttributes(): Array<PsiNameValuePair> = list.attributes.map { KtLightPsiNameValuePair(it) }.toTypedArray()
|
||||
|
||||
inner class KtLightPsiNameValuePair(private val psiNameValuePair: PsiNameValuePair) : KtLightElementBase(this),
|
||||
PsiNameValuePair {
|
||||
override fun setValue(newValue: PsiAnnotationMemberValue): PsiAnnotationMemberValue = psiNameValuePair.setValue(newValue)
|
||||
override fun getNameIdentifier(): PsiIdentifier? = psiNameValuePair.nameIdentifier
|
||||
override fun getLiteralValue(): String? = (value as? PsiLiteralValue)?.value?.toString()
|
||||
override val kotlinOrigin: KtElement? = null
|
||||
|
||||
override fun getValue(): PsiAnnotationMemberValue? = psiNameValuePair.value?.let { wrapAnnotationValue(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun delete() = kotlinOrigin.delete()
|
||||
|
||||
override fun toString() = "@$qualifiedName"
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || other::class.java != this::class.java) return false
|
||||
return kotlinOrigin == (other as KtLightAnnotationForSourceEntry).kotlinOrigin
|
||||
}
|
||||
|
||||
override fun hashCode() = kotlinOrigin.hashCode()
|
||||
|
||||
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
|
||||
}
|
||||
|
||||
class KtLightNonSourceAnnotation(
|
||||
parent: PsiElement, clsDelegate: PsiAnnotation
|
||||
) : KtLightAbstractAnnotation(parent, { clsDelegate }) {
|
||||
override val kotlinOrigin: KtAnnotationEntry? get() = null
|
||||
override fun getQualifiedName() = clsDelegate.qualifiedName
|
||||
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
|
||||
override fun findAttributeValue(attributeName: String?) = clsDelegate.findAttributeValue(attributeName)
|
||||
override fun findDeclaredAttributeValue(attributeName: String?) = clsDelegate.findDeclaredAttributeValue(attributeName)
|
||||
}
|
||||
|
||||
class KtLightNonExistentAnnotation(parent: KtLightElement<*, *>) : KtLightElementBase(parent), PsiAnnotation {
|
||||
override val kotlinOrigin get() = null
|
||||
override fun toString() = this.javaClass.name
|
||||
|
||||
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
|
||||
|
||||
override fun getNameReferenceElement() = null
|
||||
override fun findAttributeValue(attributeName: String?) = null
|
||||
override fun getQualifiedName() = null
|
||||
override fun getOwner() = parent as? PsiAnnotationOwner
|
||||
override fun findDeclaredAttributeValue(attributeName: String?) = null
|
||||
override fun getMetaData() = null
|
||||
override fun getParameterList() = KtLightEmptyAnnotationParameterList(this)
|
||||
|
||||
override fun canNavigate(): Boolean = super<KtLightElementBase>.canNavigate()
|
||||
|
||||
override fun canNavigateToSource(): Boolean = super<KtLightElementBase>.canNavigateToSource()
|
||||
|
||||
override fun navigate(requestFocus: Boolean) = super<KtLightElementBase>.navigate(requestFocus)
|
||||
}
|
||||
|
||||
class KtLightEmptyAnnotationParameterList(parent: PsiElement) : KtLightElementBase(parent), PsiAnnotationParameterList {
|
||||
override val kotlinOrigin get() = null
|
||||
override fun getAttributes(): Array<PsiNameValuePair> = emptyArray()
|
||||
}
|
||||
|
||||
class KtLightNullabilityAnnotation(member: KtLightElement<*, PsiModifierListOwner>, parent: PsiElement) : KtLightAbstractAnnotation(parent, {
|
||||
// searching for last because nullability annotations are generated after backend generates source annotations
|
||||
member.clsDelegate.modifierList?.annotations?.findLast {
|
||||
isNullabilityAnnotation(it.qualifiedName)
|
||||
} ?: KtLightNonExistentAnnotation(member)
|
||||
}) {
|
||||
override fun fqNameMatches(fqName: String): Boolean {
|
||||
if (!isNullabilityAnnotation(fqName)) return false
|
||||
|
||||
return super.fqNameMatches(fqName)
|
||||
}
|
||||
|
||||
override val kotlinOrigin get() = null
|
||||
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
|
||||
|
||||
override fun findAttributeValue(attributeName: String?) = null
|
||||
|
||||
override fun getQualifiedName(): String? = clsDelegate.qualifiedName
|
||||
|
||||
override fun findDeclaredAttributeValue(attributeName: String?) = null
|
||||
}
|
||||
|
||||
internal fun isNullabilityAnnotation(qualifiedName: String?) = qualifiedName in backendNullabilityAnnotations
|
||||
|
||||
private val backendNullabilityAnnotations = arrayOf(Nullable::class.java.name, NotNull::class.java.name)
|
||||
|
||||
private fun KtElement.getResolvedCall(): ResolvedCall<out CallableDescriptor>? {
|
||||
val context = LightClassGenerationSupport.getInstance(this.project).analyze(this)
|
||||
return this.getResolvedCall(context)
|
||||
}
|
||||
|
||||
private fun isAnnotationConstructorCall(callExpression: KtCallExpression, psiAnnotation: PsiAnnotation) =
|
||||
(callExpression.getResolvedCall()?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass?.fqNameUnsafe?.toString() == psiAnnotation.qualifiedName
|
||||
|
||||
private fun PsiElement.asKtCall(): KtCallElement? = (this as? KtElement)?.getResolvedCall()?.call?.callElement as? KtCallElement
|
||||
|
||||
private fun psiReport(psiElement: PsiElement?): String {
|
||||
if (psiElement == null) return "null"
|
||||
val text = try {
|
||||
psiElement.text
|
||||
}
|
||||
catch (e: Exception) {
|
||||
"${e.javaClass.simpleName}:${e.message}"
|
||||
}
|
||||
return "${psiElement.javaClass.canonicalName}[$text]"
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.asJava
|
||||
|
||||
import com.intellij.openapi.application.WriteAction
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry
|
||||
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.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
|
||||
class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
|
||||
fun testBooleanAnnotationDefaultValue() {
|
||||
myFixture.addClass("""
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Autowired {
|
||||
boolean required() default true;
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
class AnnotatedClass{
|
||||
@Autowired
|
||||
lateinit var bean: String
|
||||
}
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("Autowired.java", "AnnotatedClass.kt")
|
||||
|
||||
val annotations = myFixture.findClass("AnnotatedClass").fields.single()
|
||||
.expectAnnotations(2).single { it.qualifiedName == "Autowired" }
|
||||
val annotationAttributeVal = annotations.findAttributeValue("required") as PsiElement
|
||||
assertTextRangeAndValue("true", true, annotationAttributeVal)
|
||||
}
|
||||
|
||||
fun testStringAnnotationWithUnnamedParameter() {
|
||||
myFixture.addClass("""
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Qualifier {
|
||||
String value();
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
class AnnotatedClass {
|
||||
fun bar(@Qualifier("foo") param: String){}
|
||||
}
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("Qualifier.java", "AnnotatedClass.kt")
|
||||
|
||||
val annotation = myFixture.findClass("AnnotatedClass").methods.first { it.name == "bar" }.parameterList.parameters.single()
|
||||
.expectAnnotations(2).single { it.qualifiedName == "Qualifier" }
|
||||
val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement
|
||||
assertTextRangeAndValue("\"foo\"", "foo", annotationAttributeVal)
|
||||
}
|
||||
|
||||
fun testAnnotationsInAnnotationsDeclarations() {
|
||||
myFixture.addClass("""
|
||||
public @interface OuterAnnotation {
|
||||
InnerAnnotation attribute();
|
||||
@interface InnerAnnotation {
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@OuterAnnotation(attribute = OuterAnnotation.InnerAnnotation())
|
||||
open class AnnotatedClass
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("OuterAnnotation.java", "AnnotatedClass.kt")
|
||||
|
||||
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
|
||||
val annotationAttributeVal = annotations.first().findAttributeValue("attribute") as PsiElement
|
||||
assertTextAndRange("InnerAnnotation()", annotationAttributeVal)
|
||||
}
|
||||
|
||||
fun testAnnotationsInAnnotationsArrayDeclarations() {
|
||||
myFixture.addClass("""
|
||||
public @interface OuterAnnotation {
|
||||
InnerAnnotation[] attributes();
|
||||
@interface InnerAnnotation {
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@OuterAnnotation(attributes = arrayOf(OuterAnnotation.InnerAnnotation()))
|
||||
open class AnnotatedClass
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("OuterAnnotation.java", "AnnotatedClass.kt")
|
||||
|
||||
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
|
||||
val annotationAttributeVal = annotations.first().findAttributeValue("attributes") as PsiArrayInitializerMemberValue
|
||||
assertTextAndRange("arrayOf(OuterAnnotation.InnerAnnotation())", annotationAttributeVal)
|
||||
assertTextAndRange("InnerAnnotation()", annotationAttributeVal.initializers[0])
|
||||
}
|
||||
|
||||
|
||||
fun testAnnotationsInAnnotationsFinalDeclarations() {
|
||||
myFixture.addClass("""
|
||||
public @interface OuterAnnotation {
|
||||
InnerAnnotation attribute();
|
||||
@interface InnerAnnotation {
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@OuterAnnotation(attribute = OuterAnnotation.InnerAnnotation())
|
||||
class AnnotatedClass
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("OuterAnnotation.java", "AnnotatedClass.kt")
|
||||
|
||||
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
|
||||
val annotationAttributeVal = annotations.first().findAttributeValue("attribute") as PsiElement
|
||||
assertTextAndRange("InnerAnnotation()", annotationAttributeVal)
|
||||
}
|
||||
|
||||
fun testAnnotationsInAnnotationsInAnnotationsDeclarations() {
|
||||
myFixture.addClass("""
|
||||
public @interface OuterAnnotation {
|
||||
InnerAnnotation attribute();
|
||||
@interface InnerAnnotation {
|
||||
InnerInnerAnnotation attribute();
|
||||
@interface InnerInnerAnnotation {
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@OuterAnnotation(attribute = OuterAnnotation.InnerAnnotation(attribute = OuterAnnotation.InnerAnnotation.InnerInnerAnnotation()))
|
||||
open class AnnotatedClass //There is another exception if class is not open
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("OuterAnnotation.java", "AnnotatedClass.kt")
|
||||
|
||||
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
|
||||
val annotationAttributeVal = annotations.first().findAttributeValue("attribute") as PsiElement
|
||||
assertTextAndRange("InnerAnnotation(attribute = OuterAnnotation.InnerAnnotation.InnerInnerAnnotation())", annotationAttributeVal)
|
||||
|
||||
annotationAttributeVal as PsiAnnotation
|
||||
val innerAnnotationAttributeVal = annotationAttributeVal.findAttributeValue("attribute") as PsiElement
|
||||
assertTextAndRange("InnerInnerAnnotation()", innerAnnotationAttributeVal)
|
||||
}
|
||||
|
||||
fun testKotlinAnnotations() {
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
annotation class Anno1(val anno2: Anno2)
|
||||
annotation class Anno2(val anno3: Anno3)
|
||||
annotation class Anno3
|
||||
|
||||
@Anno1(Anno2(Anno3()))
|
||||
class AnnotatedClass
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("AnnotatedClass.kt")
|
||||
|
||||
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
|
||||
val annotationAttributeVal = annotations.first().findAttributeValue("anno2") as PsiElement
|
||||
assertTextAndRange("Anno2(Anno3())", annotationAttributeVal)
|
||||
|
||||
annotationAttributeVal as PsiAnnotation
|
||||
val innerAnnotationAttributeVal = annotationAttributeVal.findAttributeValue("anno3") as PsiElement
|
||||
assertTextAndRange("Anno3()", innerAnnotationAttributeVal)
|
||||
assertIsKtLightAnnotation("Anno3()", innerAnnotationAttributeVal)
|
||||
}
|
||||
|
||||
fun testKotlinAnnotationWithStringArray() {
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
annotation class Anno(val params: Array<String>)
|
||||
@Anno(arrayOf("abc", "def"))
|
||||
class AnnotatedClass
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("AnnotatedClass.kt")
|
||||
|
||||
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
|
||||
val annotationAttributeVal = annotations.first().findAttributeValue("params") as PsiElement
|
||||
assertTextAndRange("arrayOf(\"abc\", \"def\")", annotationAttributeVal)
|
||||
|
||||
annotationAttributeVal as PsiArrayInitializerMemberValue
|
||||
assertTextAndRange("\"abc\"", annotationAttributeVal.initializers[0])
|
||||
assertTextAndRange("\"def\"", annotationAttributeVal.initializers[1])
|
||||
}
|
||||
|
||||
fun testKotlinAnnotationWithStringArrayLiteral() {
|
||||
configureKotlinVersion("1.2")
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
annotation class Anno(val params: Array<String>)
|
||||
@Anno(params = ["abc", "def"])
|
||||
class AnnotatedClass
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("AnnotatedClass.kt")
|
||||
|
||||
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1)
|
||||
val annotationAttributeVal = annotations.first().findAttributeValue("params") as PsiElement
|
||||
assertTextAndRange("[\"abc\", \"def\"]", annotationAttributeVal)
|
||||
|
||||
annotationAttributeVal as PsiArrayInitializerMemberValue
|
||||
assertTextAndRange("\"abc\"", annotationAttributeVal.initializers[0].assertInstanceOf<PsiLiteral>())
|
||||
assertTextAndRange("\"def\"", annotationAttributeVal.initializers[1])
|
||||
}
|
||||
|
||||
|
||||
fun testKotlinAnnotationsArray() {
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
annotation class Anno1(val anno2: Array<Anno2>)
|
||||
annotation class Anno2(val v:Int)
|
||||
|
||||
@Anno1(anno2 = arrayOf(Anno2(1), Anno2(2)))
|
||||
class AnnotatedClass
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("AnnotatedClass.kt")
|
||||
|
||||
val annotation = myFixture.findClass("AnnotatedClass").expectAnnotations(1).single()
|
||||
|
||||
val annotationAttributeVal = annotation.findAttributeValue("anno2") as PsiArrayInitializerMemberValue
|
||||
assertTextAndRange("arrayOf(Anno2(1), Anno2(2))", annotationAttributeVal)
|
||||
annotationAttributeVal.initializers[0].let { innerAnnotationAttributeVal ->
|
||||
assertTextAndRange("Anno2(1)", innerAnnotationAttributeVal)
|
||||
assertIsKtLightAnnotation("Anno2(1)", innerAnnotationAttributeVal)
|
||||
innerAnnotationAttributeVal as PsiAnnotation
|
||||
val value = innerAnnotationAttributeVal.findAttributeValue("v").assertInstanceOf<PsiLiteral>()
|
||||
assertTextAndRange("1", value)
|
||||
}
|
||||
|
||||
|
||||
val attributeValueFromParameterList = annotation.parameterList.attributes.single().value as PsiArrayInitializerMemberValue
|
||||
assertTextAndRange("arrayOf(Anno2(1), Anno2(2))", attributeValueFromParameterList)
|
||||
attributeValueFromParameterList.initializers[0].let { innerAnnotationAttributeVal ->
|
||||
assertTextAndRange("Anno2(1)", innerAnnotationAttributeVal)
|
||||
assertIsKtLightAnnotation("Anno2(1)", innerAnnotationAttributeVal)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun testVarargAnnotation() {
|
||||
|
||||
myFixture.configureByText("Outer.java", """
|
||||
@interface Outer{
|
||||
Inner[] value() default {};
|
||||
}
|
||||
|
||||
@interface Inner{
|
||||
}
|
||||
""")
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@Outer(Inner())
|
||||
class MyAnnotated {}
|
||||
""".trimIndent())
|
||||
|
||||
val annotations = myFixture.findClass("MyAnnotated").expectAnnotations(1)
|
||||
annotations[0].let { annotation ->
|
||||
val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement
|
||||
assertTextAndRange("(Inner())", annotationAttributeVal)
|
||||
annotationAttributeVal as PsiArrayInitializerMemberValue
|
||||
annotationAttributeVal.initializers[0].let { innerAnnotationAttributeVal ->
|
||||
assertTextAndRange("Inner()", innerAnnotationAttributeVal)
|
||||
assertIsKtLightAnnotation("Inner()", innerAnnotationAttributeVal)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun testVarargWithSpread() {
|
||||
myFixture.addClass("""
|
||||
public @interface Annotation {
|
||||
String[] value();
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@Annotation(value = *arrayOf("a", "b", "c"))
|
||||
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("arrayOf(\"a\", \"b\", \"c\")", annotationAttributeVal)
|
||||
for ((i, arg) in listOf("\"a\"", "\"b\"", "\"c\"").withIndex()) {
|
||||
assertTextAndRange(arg, annotationAttributeVal.initializers[i])
|
||||
}
|
||||
}
|
||||
|
||||
fun testVarargWithSpreadComplex() {
|
||||
myFixture.addClass("""
|
||||
public @interface Annotation {
|
||||
String[] value();
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@Annotation(value = arrayOf(*arrayOf("a", "b"), "c", *arrayOf("d", "e")))
|
||||
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("arrayOf(*arrayOf(\"a\", \"b\"), \"c\", *arrayOf(\"d\", \"e\"))", annotationAttributeVal)
|
||||
for ((i, arg) in listOf("arrayOf(\"a\", \"b\")", "\"c\"", "arrayOf(\"d\", \"e\")").withIndex()) {
|
||||
assertTextAndRange(arg, annotationAttributeVal.initializers[i])
|
||||
}
|
||||
}
|
||||
|
||||
fun testVarargWithArrayLiteral() {
|
||||
myFixture.addClass("""
|
||||
public @interface Annotation {
|
||||
String[] value();
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@Annotation(value = ["a", "b", "c"])
|
||||
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("[\"a\", \"b\", \"c\"]", annotationAttributeVal)
|
||||
for ((i, arg) in listOf("\"a\"", "\"b\"", "\"c\"").withIndex()) {
|
||||
assertTextAndRange(arg, annotationAttributeVal.initializers[i])
|
||||
}
|
||||
}
|
||||
|
||||
fun testVarargWithSingleArg() {
|
||||
myFixture.addClass("""
|
||||
public @interface Annotation {
|
||||
String[] value();
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@Annotation(value = "a")
|
||||
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("\"a\"", annotationAttributeVal)
|
||||
for ((i, arg) in listOf("\"a\"").withIndex()) {
|
||||
assertTextAndRange(arg, annotationAttributeVal.initializers[i])
|
||||
}
|
||||
}
|
||||
|
||||
fun testVarargWithArrayLiteralAndSpread() {
|
||||
myFixture.addClass("""
|
||||
public @interface Annotation {
|
||||
String[] value();
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@Annotation(*["a", "b", "c"])
|
||||
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("[\"a\", \"b\", \"c\"]", annotationAttributeVal)
|
||||
for ((i, arg) in listOf("\"a\"", "\"b\"", "\"c\"").withIndex()) {
|
||||
assertTextAndRange(arg, annotationAttributeVal.initializers[i])
|
||||
}
|
||||
}
|
||||
|
||||
fun testRepeatableAnnotationsArray() {
|
||||
|
||||
myFixture.configureByText("RAnno.java", """
|
||||
import java.lang.annotation.Repeatable;
|
||||
|
||||
@interface RContainer{
|
||||
RAnno[] value() default {};
|
||||
}
|
||||
|
||||
@Repeatable(RContainer.class)
|
||||
public @interface RAnno {
|
||||
String[] value() default {};
|
||||
}
|
||||
""")
|
||||
myFixture.configureByText("AnnotatedClass.kt", """
|
||||
@RAnno()
|
||||
@RAnno("1")
|
||||
@RAnno("1", "2")
|
||||
class AnnotatedClass
|
||||
""".trimIndent())
|
||||
|
||||
val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(3)
|
||||
annotations[0].let { annotation ->
|
||||
val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement
|
||||
assertTextAndRange("{}", annotationAttributeVal)
|
||||
annotationAttributeVal as PsiArrayInitializerMemberValue
|
||||
TestCase.assertTrue(annotationAttributeVal.initializers.isEmpty())
|
||||
}
|
||||
annotations[1].let { annotation ->
|
||||
val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement
|
||||
assertTextAndRange("(\"1\")", annotationAttributeVal)
|
||||
annotationAttributeVal as PsiArrayInitializerMemberValue
|
||||
annotationAttributeVal.initializers[0].let { innerAnnotationAttributeVal ->
|
||||
assertTextAndRange("\"1\"", innerAnnotationAttributeVal)
|
||||
}
|
||||
}
|
||||
annotations[2].let { annotation ->
|
||||
val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement
|
||||
assertTextAndRange("(\"1\", \"2\")", annotationAttributeVal)
|
||||
annotationAttributeVal as PsiArrayInitializerMemberValue
|
||||
annotationAttributeVal.initializers[0].let { innerAnnotationAttributeVal ->
|
||||
assertTextAndRange("\"1\"", innerAnnotationAttributeVal)
|
||||
}
|
||||
annotationAttributeVal.initializers[1].let { innerAnnotationAttributeVal ->
|
||||
assertTextAndRange("\"2\"", innerAnnotationAttributeVal)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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("mismatch for $psiElement of ${psiElement.javaClass}", expected, psiElement.text)
|
||||
TestCase.assertEquals(expected, psiElement.textRange.substring(psiElement.containingFile.text))
|
||||
TestCase.assertEquals(psiElement, PsiAnchor.create(psiElement).retrieve())
|
||||
}
|
||||
|
||||
private fun assertIsKtLightAnnotation(expected: String, psiElement: PsiElement) {
|
||||
TestCase.assertEquals(expected, (psiElement as KtLightAnnotationForSourceEntry).kotlinOrigin.text)
|
||||
}
|
||||
|
||||
private fun assertTextRangeAndValue(expected: String, value: Any?, psiElement: PsiElement) {
|
||||
assertTextAndRange(expected, psiElement)
|
||||
val result = JavaPsiFacade.getInstance(project).constantEvaluationHelper.computeConstantExpression(psiElement)
|
||||
TestCase.assertEquals(value, result)
|
||||
val smartPointer = SmartPointerManager.getInstance(psiElement.project).createSmartPsiElementPointer(psiElement)
|
||||
assertTextAndRange(expected, smartPointer.element!!)
|
||||
}
|
||||
|
||||
private fun PsiModifierListOwner.expectAnnotations(number: Int): Array<PsiAnnotation> =
|
||||
this.modifierList!!.annotations.apply {
|
||||
TestCase.assertEquals("expected one annotation, found ${this.joinToString(", ") { it.qualifiedName ?: "unknown" }}",
|
||||
number, size)
|
||||
}
|
||||
|
||||
private fun configureKotlinVersion(version: String) {
|
||||
WriteAction.run<Throwable> {
|
||||
val modelsProvider = IdeModifiableModelsProviderImpl(project)
|
||||
val facet = module.getOrCreateFacet(modelsProvider, useProjectSettings = false)
|
||||
facet.configureFacet(version, LanguageFeature.State.DISABLED, null, modelsProvider)
|
||||
modelsProvider.commit()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user