From 99af4689030c824b05121e0d92627cee148e8dc6 Mon Sep 17 00:00:00 2001 From: Ilya Muromtsev Date: Wed, 14 Oct 2026 23:27:48 +0300 Subject: [PATCH 1/3] test: Pin bean-name references of Kotlin @Qualifier values (#697) --- .../java/QualifierBeanReferenceTest.kt | 131 +++++++++++++ .../kotlin/QualifierBeanReferenceTest.kt | 176 ++++++++++++++++++ 2 files changed, 307 insertions(+) create mode 100644 modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/java/QualifierBeanReferenceTest.kt create mode 100644 modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/kotlin/QualifierBeanReferenceTest.kt diff --git a/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/java/QualifierBeanReferenceTest.kt b/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/java/QualifierBeanReferenceTest.kt new file mode 100644 index 000000000..238f5a95e --- /dev/null +++ b/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/java/QualifierBeanReferenceTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2026 Explyt Ltd + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.explyt.spring.core.reference.java + +import com.explyt.spring.core.references.CompleteBeanReference +import com.explyt.spring.core.service.SpringSearchServiceFacade +import com.explyt.spring.test.ExplytJavaLightTestCase +import com.explyt.spring.test.TestLibrary +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.openapi.util.registry.Registry +import com.intellij.psi.PsiLanguageInjectionHost +import com.intellij.psi.PsiMethod +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.uast.UAnnotation +import org.jetbrains.uast.getParentOfType +import org.jetbrains.uast.toUElement + +class QualifierBeanReferenceTest : ExplytJavaLightTestCase() { + override val libraries = arrayOf(TestLibrary.springContext_6_0_7, TestLibrary.springBootAutoConfigure_3_1_1) + + override fun setUp() { + super.setUp() + Registry.get("explyt.spring.root.runConfiguration").setValue(false, testRootDisposable) + } + + fun testFieldCompletionOffersOnlyMatchingBeanType() { + configureBeans() + configureConsumer("@Autowired @Qualifier(\"\") Animal injected;") + assertEquals(setOf("animal"), qualifierReference().variantNames()) + } + + fun testFieldQualifierResolvesToBeanFactory() { + val bean = configureBeans() + configureConsumer("@Autowired @Qualifier(\"animal\") Animal injected;") + assertResolvesTo(bean, qualifierReference()) + } + + fun testConstructorParameterQualifierResolvesToBeanFactory() { + val bean = configureBeans() + configureConsumer("Consumer(@Qualifier(\"animal\") Animal injected) {}") + assertResolvesTo(bean, qualifierReference()) + } + + fun testSetterParameterQualifierResolvesToBeanFactory() { + val bean = configureBeans() + configureConsumer("@Autowired void setAnimal(@Qualifier(\"animal\") Animal injected) {}") + assertResolvesTo(bean, qualifierReference()) + } + + private fun configureBeans(): PsiMethod { + myFixture.addFileToProject( + "com/explyt/demo/App.java", + "package com.explyt.demo; @org.springframework.boot.autoconfigure.SpringBootApplication public class App {}" + ) + myFixture.addFileToProject("com/explyt/demo/Animal.java", "package com.explyt.demo; public class Animal {}") + myFixture.addFileToProject("com/explyt/demo/Vehicle.java", "package com.explyt.demo; public class Vehicle {}") + myFixture.addFileToProject( + "com/explyt/demo/Beans.java", + """ + package com.explyt.demo; + import org.springframework.context.annotation.Bean; + import org.springframework.context.annotation.Configuration; + @Configuration(proxyBeanMethods = false) + public class Beans { + @Bean public Animal animal() { return new Animal(); } + @Bean public Vehicle vehicle() { return new Vehicle(); } + } + """.trimIndent() + ) + val beans = SpringSearchServiceFacade.getInstance(project).getAllActiveBeans(module) + val animalBeans = beans.filter { it.name == "animal" } + assertEquals("Precondition: one active animal factory", 1, animalBeans.size) + assertEquals("com.explyt.demo.Animal", animalBeans.single().psiClass.qualifiedName) + val vehicleBeans = beans.filter { it.name == "vehicle" } + assertEquals("Precondition: one active different-type factory", 1, vehicleBeans.size) + assertEquals("com.explyt.demo.Vehicle", vehicleBeans.single().psiClass.qualifiedName) + val method = animalBeans.single().psiMember as PsiMethod + assertEquals("animal", method.name) + assertEquals("com.explyt.demo.Beans", method.containingClass?.qualifiedName) + return method + } + + private fun configureConsumer(body: String) { + myFixture.configureByText( + "Consumer.java", + """ + package com.explyt.demo; + import org.springframework.stereotype.Component; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.beans.factory.annotation.Qualifier; + @Component public class Consumer { + $body + } + """.trimIndent() + ) + } + + private fun qualifierHost(): PsiLanguageInjectionHost { + val leaf = myFixture.file.findElementAt(myFixture.caretOffset) + val host = PsiTreeUtil.getParentOfType(leaf, PsiLanguageInjectionHost::class.java, false) + ?: error("Precondition: string host at caret") + val annotation = host.toUElement()?.getParentOfType() + assertEquals( + "Precondition: qualifier annotation resolves", + "org.springframework.beans.factory.annotation.Qualifier", + annotation?.resolve()?.qualifiedName + ) + return host + } + + private fun qualifierReference(): CompleteBeanReference { + val host = qualifierHost() + val references = host.references.filterIsInstance() + assertEquals("Expected one qualifier bean-name reference on ${host.text}", 1, references.size) + return references.single() + } + + private fun CompleteBeanReference.variantNames(): Set = + variants.map { (it as LookupElement).lookupString }.toSet() + + private fun assertResolvesTo(expected: PsiMethod, reference: CompleteBeanReference) { + val actual = reference.resolve() + assertTrue( + "Qualifier must resolve to Beans.animal, got $actual", + actual != null && psiManager.areElementsEquivalent(expected, actual) + ) + } +} diff --git a/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/kotlin/QualifierBeanReferenceTest.kt b/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/kotlin/QualifierBeanReferenceTest.kt new file mode 100644 index 000000000..70609bebf --- /dev/null +++ b/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/kotlin/QualifierBeanReferenceTest.kt @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2026 Explyt Ltd + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.explyt.spring.core.reference.kotlin + +import com.explyt.spring.core.references.CompleteBeanReference +import com.explyt.spring.core.service.SpringSearchServiceFacade +import com.explyt.spring.test.ExplytKotlinLightTestCase +import com.explyt.spring.test.TestLibrary +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.openapi.util.registry.Registry +import com.intellij.psi.ElementManipulators +import com.intellij.psi.PsiLanguageInjectionHost +import com.intellij.psi.PsiMethod +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.uast.UAnnotation +import org.jetbrains.uast.getParentOfType +import org.jetbrains.uast.toUElement + +class QualifierBeanReferenceTest : ExplytKotlinLightTestCase() { + override val libraries = arrayOf(TestLibrary.springContext_6_0_7, TestLibrary.springBootAutoConfigure_3_1_1) + + override fun setUp() { + super.setUp() + Registry.get("explyt.spring.root.runConfiguration").setValue(false, testRootDisposable) + } + + fun testPropertyCompletionOffersOnlyMatchingBeanType() { + configureBeans() + configureConsumer("@Autowired @Qualifier(\"\") lateinit var injected: Animal") + assertEquals(setOf("animal"), qualifierReference().variantNames()) + } + + fun testPropertyQualifierResolvesToBeanFactory() { + val bean = configureBeans() + configureConsumer("@Autowired @Qualifier(\"animal\") lateinit var injected: Animal") + assertResolvesTo(bean, qualifierReference()) + } + + fun testFieldUseSiteQualifierResolvesToBeanFactory() { + val bean = configureBeans() + configureConsumer("@Autowired @field:Qualifier(\"animal\") lateinit var injected: Animal") + assertResolvesTo(bean, qualifierReference()) + } + + fun testConstructorValQualifierResolvesToBeanFactory() { + val bean = configureBeans() + configureConsumer("", "(@Qualifier(\"animal\") val injected: Animal)") + assertResolvesTo(bean, qualifierReference()) + } + + fun testSetterParameterQualifierResolvesToBeanFactory() { + val bean = configureBeans() + configureConsumer("var injected: Animal? = null\n @Autowired set(@Qualifier(\"animal\") value) { field = value }") + assertResolvesTo(bean, qualifierReference()) + } + + fun testFunctionParameterQualifierResolvesToBeanFactory() { + val bean = configureBeans() + configureConsumer("@Autowired fun inject(@Qualifier(\"animal\") value: Animal) {}") + assertResolvesTo(bean, qualifierReference()) + } + + fun testMetaQualifierResolvesToBeanFactory() { + val bean = configureBeans() + configureConsumer( + "@Autowired @AnimalQualifier(\"animal\") lateinit var injected: Animal", + annotationDeclaration = "@Qualifier annotation class AnimalQualifier(val value: String)" + ) + val host = qualifierHost("com.explyt.demo.AnimalQualifier") + val metaAnnotation = host.toUElement()?.getParentOfType()?.resolve() + ?.getAnnotation("org.springframework.beans.factory.annotation.Qualifier") + assertEquals( + "Precondition: custom annotation is meta-qualified", + "org.springframework.beans.factory.annotation.Qualifier", + metaAnnotation?.resolveAnnotationType()?.qualifiedName + ) + assertResolvesTo(bean, qualifierReference(host)) + } + + fun testInterpolatedTemplateHasNoBeanNameReference() { + configureBeans() + configureConsumer( + "@Autowired @Qualifier(\"${'$'}{PREFIX}a\") lateinit var injected: Animal", + annotationDeclaration = "const val PREFIX = \"anim\"" + ) + val host = qualifierHost() + assertEquals(emptyList(), host.references.filterIsInstance()) + } + + fun testEscapedValueReferenceUsesWholeHostValueRange() { + val bean = configureBeans("ani\\nmal") + configureConsumer("@Autowired @Qualifier(\"ani\\nmal\") lateinit var injected: Animal") + val host = qualifierHost() + val reference = qualifierReference(host) + assertEquals(ElementManipulators.getValueTextRange(host), reference.rangeInElement) + assertResolvesTo(bean, reference) + } + + private fun configureBeans(animalName: String = "animal"): PsiMethod { + myFixture.addFileToProject( + "com/explyt/demo/App.java", + "package com.explyt.demo; @org.springframework.boot.autoconfigure.SpringBootApplication public class App {}" + ) + myFixture.addFileToProject("com/explyt/demo/Animal.java", "package com.explyt.demo; public class Animal {}") + myFixture.addFileToProject("com/explyt/demo/Vehicle.java", "package com.explyt.demo; public class Vehicle {}") + myFixture.addFileToProject( + "com/explyt/demo/Beans.java", + """ + package com.explyt.demo; + import org.springframework.context.annotation.Bean; + import org.springframework.context.annotation.Configuration; + @Configuration(proxyBeanMethods = false) + public class Beans { + @Bean("$animalName") public Animal animal() { return new Animal(); } + @Bean public Vehicle vehicle() { return new Vehicle(); } + } + """.trimIndent() + ) + val beans = SpringSearchServiceFacade.getInstance(project).getAllActiveBeans(module) + val animalBeans = beans.filter { it.name == animalName.replace("\\n", "\n") } + assertEquals("Precondition: one active animal factory", 1, animalBeans.size) + assertEquals("com.explyt.demo.Animal", animalBeans.single().psiClass.qualifiedName) + val vehicleBeans = beans.filter { it.name == "vehicle" } + assertEquals("Precondition: one active different-type factory", 1, vehicleBeans.size) + assertEquals("com.explyt.demo.Vehicle", vehicleBeans.single().psiClass.qualifiedName) + val method = animalBeans.single().psiMember as PsiMethod + assertEquals("animal", method.name) + assertEquals("com.explyt.demo.Beans", method.containingClass?.qualifiedName) + return method + } + + private fun configureConsumer(body: String, constructor: String = "", annotationDeclaration: String = "") { + myFixture.configureByText( + "Consumer.kt", + """ + package com.explyt.demo + import org.springframework.stereotype.Component + import org.springframework.beans.factory.annotation.Autowired + import org.springframework.beans.factory.annotation.Qualifier + $annotationDeclaration + @Component class Consumer$constructor { + $body + } + """.trimIndent() + ) + } + + private fun qualifierHost(annotationName: String = "org.springframework.beans.factory.annotation.Qualifier"): PsiLanguageInjectionHost { + val leaf = myFixture.file.findElementAt(myFixture.caretOffset) + val host = PsiTreeUtil.getParentOfType(leaf, PsiLanguageInjectionHost::class.java, false) + ?: error("Precondition: string host at caret") + val annotation = host.toUElement()?.getParentOfType() + assertEquals("Precondition: qualifier annotation resolves", annotationName, annotation?.resolve()?.qualifiedName) + return host + } + + private fun qualifierReference(host: PsiLanguageInjectionHost = qualifierHost()): CompleteBeanReference { + val references = host.references.filterIsInstance() + assertEquals("Expected one qualifier bean-name reference on ${host.text}", 1, references.size) + return references.single() + } + + private fun CompleteBeanReference.variantNames(): Set = + variants.map { (it as LookupElement).lookupString }.toSet() + + private fun assertResolvesTo(expected: PsiMethod, reference: CompleteBeanReference) { + val actual = reference.resolve() + assertTrue( + "Qualifier must resolve to Beans.animal, got $actual", + actual != null && psiManager.areElementsEquivalent(expected, actual) + ) + } +} From f786e6715cb290cc9ecc5a26a87e7fa8a3e99f49 Mon Sep 17 00:00:00 2001 From: Ilya Muromtsev Date: Thu, 15 Oct 2026 01:38:05 +0300 Subject: [PATCH 2/3] fix: Offer and resolve bean names in Kotlin @Qualifier values (#697) --- CHANGELOG.md | 1 + .../core/references/CompleteBeanReference.kt | 14 +++++------- .../QualifierReferenceContributor.kt | 22 +++++++++---------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3102cc363..52322db4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,6 +110,7 @@ - fix: support spring boot4 test annotations MockitoSpyBean/MockitoBean (#418) - fix: Event publisher navigation not working for child class if EvenListener has parent class (#419) - fix: Stop Spring inspections from logging a `PluginException` caused by `IndexNotReadyException` while the IDE indexes: they stay inactive during indexing and run again once it finishes (#688) +- fix: Offer and resolve bean names in Kotlin `@Qualifier` values on properties, fields and constructor or setter parameters, as in Java (#697) ### Spring Initializr - fix: Make `gradlew` and `mvnw` executable in a project generated through Spring Initializr, so the first `./gradlew` in a terminal no longer fails with "permission denied" (#60) diff --git a/modules/spring-core/src/main/kotlin/com/explyt/spring/core/references/CompleteBeanReference.kt b/modules/spring-core/src/main/kotlin/com/explyt/spring/core/references/CompleteBeanReference.kt index 9702de1de..b3e0ef7aa 100644 --- a/modules/spring-core/src/main/kotlin/com/explyt/spring/core/references/CompleteBeanReference.kt +++ b/modules/spring-core/src/main/kotlin/com/explyt/spring/core/references/CompleteBeanReference.kt @@ -12,7 +12,10 @@ import com.intellij.icons.AllIcons import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.util.TextRange import com.intellij.psi.* -import com.intellij.psi.util.parentOfType +import org.jetbrains.uast.UAnnotation +import org.jetbrains.uast.UVariable +import org.jetbrains.uast.getParentOfType +import org.jetbrains.uast.toUElement class CompleteBeanReference(element: PsiElement, private val beanName: String, textRange: TextRange) : PsiReferenceBase(element, textRange), PsiReference , HighlightedReference { @@ -44,11 +47,6 @@ class CompleteBeanReference(element: PsiElement, private val beanName: String, t return variants.toTypedArray() } - private fun getPsiType(): PsiType? { - return when (val psiElement = element.parentOfType()?.parent?.parent) { - is PsiField -> psiElement.type - is PsiParameter -> psiElement.type - else -> null - } - } + private fun getPsiType(): PsiType? = + (element.toUElement()?.getParentOfType()?.uastParent as? UVariable)?.type } diff --git a/modules/spring-core/src/main/kotlin/com/explyt/spring/core/references/contributors/QualifierReferenceContributor.kt b/modules/spring-core/src/main/kotlin/com/explyt/spring/core/references/contributors/QualifierReferenceContributor.kt index 471666b34..805760ba4 100644 --- a/modules/spring-core/src/main/kotlin/com/explyt/spring/core/references/contributors/QualifierReferenceContributor.kt +++ b/modules/spring-core/src/main/kotlin/com/explyt/spring/core/references/contributors/QualifierReferenceContributor.kt @@ -8,7 +8,6 @@ package com.explyt.spring.core.references.contributors import com.explyt.spring.core.SpringCoreClasses import com.explyt.spring.core.references.CompleteBeanReference import com.explyt.util.ExplytPsiUtil.isMetaAnnotatedBy -import com.intellij.openapi.util.TextRange import com.intellij.patterns.uast.injectionHostUExpression import com.intellij.psi.* import com.intellij.util.ProcessingContext @@ -27,24 +26,23 @@ class QualifierReferenceProvider: UastInjectionHostReferenceProvider() { host: PsiLanguageInjectionHost, context: ProcessingContext ): Array { - val valueLiteral = uExpression as? ULiteralExpression ?: return PsiReference.EMPTY_ARRAY + if (!uExpression.isPlainStringLiteral()) return PsiReference.EMPTY_ARRAY val uAnnotation = uExpression.getParentOfType() ?: return PsiReference.EMPTY_ARRAY - val valueText = valueLiteral.evaluateString() ?: return PsiReference.EMPTY_ARRAY - val literalExpressionPsi = valueLiteral.javaPsi ?: return PsiReference.EMPTY_ARRAY - val psiAnnotation = uAnnotation.javaPsi ?: return PsiReference.EMPTY_ARRAY - val psiAnnotationClass = psiAnnotation.resolveAnnotationType() ?: return PsiReference.EMPTY_ARRAY + val valueText = uExpression.evaluateString() ?: return PsiReference.EMPTY_ARRAY + val psiAnnotationClass = uAnnotation.resolve() ?: return PsiReference.EMPTY_ARRAY if (SpringCoreClasses.QUALIFIERS.none { it == uAnnotation.qualifiedName || psiAnnotationClass.isMetaAnnotatedBy(it) }) { return PsiReference.EMPTY_ARRAY } - val resolveAnnotationType = psiAnnotation.resolveAnnotationType() ?: return PsiReference.EMPTY_ARRAY - if (resolveAnnotationType.fields.isEmpty() && resolveAnnotationType.methods.isEmpty()) return PsiReference.EMPTY_ARRAY + if (psiAnnotationClass.fields.isEmpty() && psiAnnotationClass.methods.isEmpty()) return PsiReference.EMPTY_ARRAY - val rangeInElement = TextRange(0, valueText.length) - .shiftRight(ElementManipulators.getValueTextRange(literalExpressionPsi).startOffset) - return arrayOf(CompleteBeanReference(host, valueText, rangeInElement)) + return arrayOf(CompleteBeanReference(host, valueText, ElementManipulators.getValueTextRange(host))) } + private fun UExpression.isPlainStringLiteral(): Boolean = when (this) { + is ULiteralExpression -> true + is UPolyadicExpression -> operands.all { it is ULiteralExpression } + else -> false + } } - From aaae50c81efdbcd291e3d03de3f7806005e1c967 Mon Sep 17 00:00:00 2001 From: Ilya Muromtsev Date: Thu, 15 Oct 2026 01:59:50 +0300 Subject: [PATCH 3/3] test: Assert the qualifier reference range independently of the manipulator (#697) --- .../java/QualifierBeanReferenceTest.kt | 17 ++++++++++++++--- .../kotlin/QualifierBeanReferenceTest.kt | 12 ++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/java/QualifierBeanReferenceTest.kt b/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/java/QualifierBeanReferenceTest.kt index 238f5a95e..81533c7ca 100644 --- a/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/java/QualifierBeanReferenceTest.kt +++ b/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/java/QualifierBeanReferenceTest.kt @@ -10,6 +10,7 @@ import com.explyt.spring.core.service.SpringSearchServiceFacade import com.explyt.spring.test.ExplytJavaLightTestCase import com.explyt.spring.test.TestLibrary import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.registry.Registry import com.intellij.psi.PsiLanguageInjectionHost import com.intellij.psi.PsiMethod @@ -50,7 +51,17 @@ class QualifierBeanReferenceTest : ExplytJavaLightTestCase() { assertResolvesTo(bean, qualifierReference()) } - private fun configureBeans(): PsiMethod { + fun testEscapedValueReferenceUsesWholeHostValueRange() { + val bean = configureBeans("ani\\nmal") + configureConsumer("@Autowired @Qualifier(\"ani\\nmal\") Animal injected;") + val host = qualifierHost() + val reference = qualifierReference() + assertEquals(TextRange(1, host.textLength - 1), reference.rangeInElement) + assertEquals("ani\\nmal", reference.value) + assertResolvesTo(bean, reference) + } + + private fun configureBeans(animalName: String = "animal"): PsiMethod { myFixture.addFileToProject( "com/explyt/demo/App.java", "package com.explyt.demo; @org.springframework.boot.autoconfigure.SpringBootApplication public class App {}" @@ -65,13 +76,13 @@ class QualifierBeanReferenceTest : ExplytJavaLightTestCase() { import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) public class Beans { - @Bean public Animal animal() { return new Animal(); } + @Bean("$animalName") public Animal animal() { return new Animal(); } @Bean public Vehicle vehicle() { return new Vehicle(); } } """.trimIndent() ) val beans = SpringSearchServiceFacade.getInstance(project).getAllActiveBeans(module) - val animalBeans = beans.filter { it.name == "animal" } + val animalBeans = beans.filter { it.name == animalName.replace("\\n", "\n") } assertEquals("Precondition: one active animal factory", 1, animalBeans.size) assertEquals("com.explyt.demo.Animal", animalBeans.single().psiClass.qualifiedName) val vehicleBeans = beans.filter { it.name == "vehicle" } diff --git a/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/kotlin/QualifierBeanReferenceTest.kt b/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/kotlin/QualifierBeanReferenceTest.kt index 70609bebf..a86696146 100644 --- a/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/kotlin/QualifierBeanReferenceTest.kt +++ b/modules/spring-core/src/test/kotlin/com/explyt/spring/core/reference/kotlin/QualifierBeanReferenceTest.kt @@ -11,13 +11,11 @@ import com.explyt.spring.test.ExplytKotlinLightTestCase import com.explyt.spring.test.TestLibrary import com.intellij.codeInsight.lookup.LookupElement import com.intellij.openapi.util.registry.Registry -import com.intellij.psi.ElementManipulators +import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiLanguageInjectionHost import com.intellij.psi.PsiMethod import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.uast.UAnnotation -import org.jetbrains.uast.getParentOfType -import org.jetbrains.uast.toUElement +import org.jetbrains.uast.* class QualifierBeanReferenceTest : ExplytKotlinLightTestCase() { override val libraries = arrayOf(TestLibrary.springContext_6_0_7, TestLibrary.springBootAutoConfigure_3_1_1) @@ -81,12 +79,13 @@ class QualifierBeanReferenceTest : ExplytKotlinLightTestCase() { } fun testInterpolatedTemplateHasNoBeanNameReference() { - configureBeans() + configureBeans("anima") configureConsumer( "@Autowired @Qualifier(\"${'$'}{PREFIX}a\") lateinit var injected: Animal", annotationDeclaration = "const val PREFIX = \"anim\"" ) val host = qualifierHost() + assertEquals("anima", (host.toUElement() as? UExpression)?.evaluateString()) assertEquals(emptyList(), host.references.filterIsInstance()) } @@ -95,7 +94,8 @@ class QualifierBeanReferenceTest : ExplytKotlinLightTestCase() { configureConsumer("@Autowired @Qualifier(\"ani\\nmal\") lateinit var injected: Animal") val host = qualifierHost() val reference = qualifierReference(host) - assertEquals(ElementManipulators.getValueTextRange(host), reference.rangeInElement) + assertEquals(TextRange(1, host.textLength - 1), reference.rangeInElement) + assertEquals("ani\\nmal", reference.value) assertResolvesTo(bean, reference) }