Spring Support: Fixed computation of values in Java annotations referred by Kotlin annotation entries
#KT-11702 Fixed
This commit is contained in:
@@ -160,6 +160,7 @@ Issues fixed:
|
|||||||
- [KT-11178](https://youtrack.jetbrains.com/issue/KT-11178) Don't show "Change type arguments" fix when there's nothing to change
|
- [KT-11178](https://youtrack.jetbrains.com/issue/KT-11178) Don't show "Change type arguments" fix when there's nothing to change
|
||||||
- Fix several issues leading to exceptions: [KT-11579](https://youtrack.jetbrains.com/issue/KT-11579), [KT-11580](https://youtrack.jetbrains.com/issue/KT-11580)
|
- Fix several issues leading to exceptions: [KT-11579](https://youtrack.jetbrains.com/issue/KT-11579), [KT-11580](https://youtrack.jetbrains.com/issue/KT-11580)
|
||||||
- Fixed NoSuchFieldException in Kotlin module settings on IDEA Ultimate
|
- Fixed NoSuchFieldException in Kotlin module settings on IDEA Ultimate
|
||||||
|
- [KT-11702](https://youtrack.jetbrains.com/issue/KT-11702) Fixed resolution of Kotlin beans with custom name
|
||||||
|
|
||||||
#### Debugger
|
#### Debugger
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
|
|||||||
import com.intellij.util.IncorrectOperationException
|
import com.intellij.util.IncorrectOperationException
|
||||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtParameter
|
import org.jetbrains.kotlin.psi.KtParameter
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
@@ -37,7 +36,7 @@ class KtLightAnnotation(
|
|||||||
private val owner: PsiAnnotationOwner
|
private val owner: PsiAnnotationOwner
|
||||||
) : PsiAnnotation by clsDelegate, KtLightElement<KtAnnotationEntry, PsiAnnotation> {
|
) : PsiAnnotation by clsDelegate, KtLightElement<KtAnnotationEntry, PsiAnnotation> {
|
||||||
inner class LightExpressionValue(private val delegate: PsiExpression) : PsiAnnotationMemberValue, PsiExpression by delegate {
|
inner class LightExpressionValue(private val delegate: PsiExpression) : PsiAnnotationMemberValue, PsiExpression by delegate {
|
||||||
val originalExpression: KtExpression? by lazy {
|
val originalExpression: PsiElement? by lazy {
|
||||||
val nameAndValue = getStrictParentOfType<PsiNameValuePair>() ?: return@lazy null
|
val nameAndValue = getStrictParentOfType<PsiNameValuePair>() ?: return@lazy null
|
||||||
val annotationEntry = this@KtLightAnnotation.kotlinOrigin
|
val annotationEntry = this@KtLightAnnotation.kotlinOrigin
|
||||||
val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry)
|
val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry)
|
||||||
@@ -49,7 +48,12 @@ class KtLightAnnotation(
|
|||||||
val resolvedArgument = resolvedCall.valueArguments[parameter] ?: return@lazy null
|
val resolvedArgument = resolvedCall.valueArguments[parameter] ?: return@lazy null
|
||||||
when (resolvedArgument) {
|
when (resolvedArgument) {
|
||||||
is DefaultValueArgument -> {
|
is DefaultValueArgument -> {
|
||||||
(parameter.source.getPsi() as? KtParameter)?.defaultValue
|
val psi = parameter.source.getPsi()
|
||||||
|
when (psi) {
|
||||||
|
is KtParameter -> psi.defaultValue
|
||||||
|
is PsiAnnotationMethod -> psi.defaultValue
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ExpressionValueArgument -> {
|
is ExpressionValueArgument -> {
|
||||||
|
|||||||
@@ -16,16 +16,29 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea
|
package org.jetbrains.kotlin.idea
|
||||||
|
|
||||||
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import com.intellij.psi.PsiConstantEvaluationHelper
|
import com.intellij.psi.PsiConstantEvaluationHelper
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiExpression
|
||||||
import com.intellij.psi.impl.ConstantExpressionEvaluator
|
import com.intellij.psi.impl.ConstantExpressionEvaluator
|
||||||
import org.jetbrains.kotlin.asJava.KtLightAnnotation
|
import org.jetbrains.kotlin.asJava.KtLightAnnotation
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator as FrontendConstantExpressionEvaluator
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator as FrontendConstantExpressionEvaluator
|
||||||
|
|
||||||
class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator {
|
class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator {
|
||||||
|
private fun evalConstantValue(constantValue: ConstantValue<*>): Any? {
|
||||||
|
return if (constantValue is ArrayValue) {
|
||||||
|
val items = constantValue.value.map { evalConstantValue(it) }
|
||||||
|
items.singleOrNull() ?: items
|
||||||
|
}
|
||||||
|
else constantValue.value
|
||||||
|
}
|
||||||
|
|
||||||
override fun computeConstantExpression(expression: PsiElement, throwExceptionOnOverflow: Boolean): Any? {
|
override fun computeConstantExpression(expression: PsiElement, throwExceptionOnOverflow: Boolean): Any? {
|
||||||
return computeExpression(expression, throwExceptionOnOverflow, null)
|
return computeExpression(expression, throwExceptionOnOverflow, null)
|
||||||
}
|
}
|
||||||
@@ -37,11 +50,24 @@ class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator {
|
|||||||
): Any? {
|
): Any? {
|
||||||
if (expression !is KtLightAnnotation.LightExpressionValue) return null
|
if (expression !is KtLightAnnotation.LightExpressionValue) return null
|
||||||
val expressionToCompute = expression.originalExpression ?: return null
|
val expressionToCompute = expression.originalExpression ?: return null
|
||||||
val resolutionFacade = expressionToCompute.getResolutionFacade()
|
return when (expressionToCompute) {
|
||||||
val evaluator = FrontendConstantExpressionEvaluator(resolutionFacade.moduleDescriptor.builtIns)
|
is KtExpression -> {
|
||||||
val evaluatorTrace = DelegatingBindingTrace(resolutionFacade.analyze(expressionToCompute), "Evaluating annotation argument")
|
val resolutionFacade = expressionToCompute.getResolutionFacade()
|
||||||
val constant = evaluator.evaluateExpression(expressionToCompute, evaluatorTrace) ?: return null
|
val evaluator = FrontendConstantExpressionEvaluator(resolutionFacade.moduleDescriptor.builtIns)
|
||||||
if (constant.isError) return null
|
val evaluatorTrace = DelegatingBindingTrace(resolutionFacade.analyze(expressionToCompute), "Evaluating annotation argument")
|
||||||
return constant.getValue(TypeUtils.NO_EXPECTED_TYPE)
|
|
||||||
|
val constant = evaluator.evaluateExpression(expressionToCompute, evaluatorTrace) ?: return null
|
||||||
|
if (constant.isError) return null
|
||||||
|
evalConstantValue(constant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE))
|
||||||
|
}
|
||||||
|
|
||||||
|
is PsiExpression -> {
|
||||||
|
JavaPsiFacade.getInstance(expressionToCompute.project)
|
||||||
|
.constantEvaluationHelper
|
||||||
|
.computeExpression(expression, throwExceptionOnOverflow, auxEvaluator)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// NUMBER: 1
|
||||||
|
// EXIST: { lookupString:"buildBar" }
|
||||||
|
|
||||||
|
package a
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||||
|
|
||||||
|
class FooBean
|
||||||
|
class BarBean
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
open class KotlinAnnotated {
|
||||||
|
@Bean(name=arrayOf("annFooBean")) open fun buildFoo(): FooBean = FooBean()
|
||||||
|
@Bean open fun buildBar(): BarBean = BarBean()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
fun test() {
|
||||||
|
val context = AnnotationConfigApplicationContext("a");
|
||||||
|
context.getBean("build<caret>")
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
// NUMBER: 2
|
||||||
|
// EXIST: { lookupString:"annFooBean" }
|
||||||
|
// EXIST: { lookupString:"kotlinAnnotated" }
|
||||||
|
|
||||||
|
package a
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||||
|
|
||||||
|
class FooBean
|
||||||
|
class BarBean
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
open class KotlinAnnotated {
|
||||||
|
@Bean(name=arrayOf("annFooBean")) open fun buildFoo(): FooBean = FooBean()
|
||||||
|
@Bean open fun buildBar(): BarBean = BarBean()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
fun test() {
|
||||||
|
val context = AnnotationConfigApplicationContext("a");
|
||||||
|
context.getBean("ann<caret>")
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
||||||
|
<beans>
|
||||||
|
<bean id="fooBean" class="java.lang.String"/>
|
||||||
|
<bean id="fooBean2" class="java.lang.String"/>
|
||||||
|
<bean id="barBean" class="java.lang.String"/>
|
||||||
|
<bean id="scopedBean" class="test.ScopedBean"/>
|
||||||
|
</beans>
|
||||||
+7
-4
@@ -16,19 +16,22 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.spring.tests.references
|
package org.jetbrains.kotlin.idea.spring.tests.references
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import com.intellij.util.PathUtil
|
import com.intellij.util.PathUtil
|
||||||
import org.jetbrains.kotlin.idea.completion.test.AbstractJvmBasicCompletionTest
|
import org.jetbrains.kotlin.idea.completion.test.AbstractJvmBasicCompletionTest
|
||||||
import org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
|
import org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
|
||||||
import org.jetbrains.kotlin.idea.test.TestFixtureExtension
|
import org.jetbrains.kotlin.idea.test.TestFixtureExtension
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
abstract class AbstractSpringReferenceCompletionTest : AbstractJvmBasicCompletionTest() {
|
abstract class AbstractSpringReferenceCompletionTest : AbstractJvmBasicCompletionTest() {
|
||||||
override fun setUpFixture(testPath: String) {
|
override fun setUpFixture(testPath: String) {
|
||||||
super.setUpFixture(testPath)
|
val mainFileName = PathUtil.getFileName(testPath)
|
||||||
|
val baseName = FileUtil.getNameWithoutExtension(mainFileName)
|
||||||
|
val configFileName = if (baseName.endsWith("Xml")) "$baseName-config.xml" else mainFileName
|
||||||
TestFixtureExtension
|
TestFixtureExtension
|
||||||
.loadFixture<SpringTestFixtureExtension>(myModule)
|
.loadFixture<SpringTestFixtureExtension>(myModule)
|
||||||
.configureFileSet(myFixture, listOf(PathUtil.toSystemIndependentName(File(testPath).parent + "/spring-config.xml")))
|
.configureFileSet(myFixture, listOf(PathUtil.toSystemIndependentName("${PathUtil.getParentPath(testPath)}/$configFileName")))
|
||||||
|
|
||||||
|
super.setUpFixture(testPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tearDownFixture() {
|
override fun tearDownFixture() {
|
||||||
|
|||||||
+18
-6
@@ -35,15 +35,27 @@ public class SpringReferenceCompletionTestGenerated extends AbstractSpringRefere
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("ultimate/testData/spring/core/references/completion/variants"), Pattern.compile("^(.+)\\.kt$"), true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("ultimate/testData/spring/core/references/completion/variants"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("scopeReference.kt")
|
@TestMetadata("beanWithDefaultName.kt")
|
||||||
public void testScopeReference() throws Exception {
|
public void testBeanWithDefaultName() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/scopeReference.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/beanWithDefaultName.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("springBeanReference.kt")
|
@TestMetadata("beanWithExplicitName.kt")
|
||||||
public void testSpringBeanReference() throws Exception {
|
public void testBeanWithExplicitName() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/springBeanReference.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/beanWithExplicitName.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("scopeReferenceXml.kt")
|
||||||
|
public void testScopeReferenceXml() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/scopeReferenceXml.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("springBeanReferenceXml.kt")
|
||||||
|
public void testSpringBeanReferenceXml() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/springBeanReferenceXml.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user