From eeac739f69bd5aef32e8c272a60d4880db770412 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Mon, 4 Apr 2016 13:06:04 +0300 Subject: [PATCH] Spring Support: Fixed computation of values in Java annotations referred by Kotlin annotation entries #KT-11702 Fixed --- ChangeLog.md | 1 + .../kotlin/asJava/KtLightAnnotation.kt | 10 +++-- .../KotlinLightConstantExpressionEvaluator.kt | 38 ++++++++++++++++--- .../variants/beanWithDefaultName.kt | 24 ++++++++++++ .../variants/beanWithExplicitName.kt | 25 ++++++++++++ ...onfig.xml => scopeReferenceXml-config.xml} | 0 ...scopeReference.kt => scopeReferenceXml.kt} | 0 .../springBeanReferenceXml-config.xml | 8 ++++ ...Reference.kt => springBeanReferenceXml.kt} | 0 .../AbstractSpringReferenceCompletionTest.kt | 11 ++++-- ...pringReferenceCompletionTestGenerated.java | 24 +++++++++--- 11 files changed, 122 insertions(+), 19 deletions(-) create mode 100644 ultimate/testData/spring/core/references/completion/variants/beanWithDefaultName.kt create mode 100644 ultimate/testData/spring/core/references/completion/variants/beanWithExplicitName.kt rename ultimate/testData/spring/core/references/completion/variants/{spring-config.xml => scopeReferenceXml-config.xml} (100%) rename ultimate/testData/spring/core/references/completion/variants/{scopeReference.kt => scopeReferenceXml.kt} (100%) create mode 100644 ultimate/testData/spring/core/references/completion/variants/springBeanReferenceXml-config.xml rename ultimate/testData/spring/core/references/completion/variants/{springBeanReference.kt => springBeanReferenceXml.kt} (100%) diff --git a/ChangeLog.md b/ChangeLog.md index 8f9d94b6ba0..fa18732d055 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 - 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 +- [KT-11702](https://youtrack.jetbrains.com/issue/KT-11702) Fixed resolution of Kotlin beans with custom name #### Debugger diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightAnnotation.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightAnnotation.kt index bfa1f06d07c..7968a795a65 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightAnnotation.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightAnnotation.kt @@ -22,7 +22,6 @@ import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry import com.intellij.util.IncorrectOperationException import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.psi.KtAnnotationEntry -import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -37,7 +36,7 @@ class KtLightAnnotation( private val owner: PsiAnnotationOwner ) : PsiAnnotation by clsDelegate, KtLightElement { inner class LightExpressionValue(private val delegate: PsiExpression) : PsiAnnotationMemberValue, PsiExpression by delegate { - val originalExpression: KtExpression? by lazy { + val originalExpression: PsiElement? by lazy { val nameAndValue = getStrictParentOfType() ?: return@lazy null val annotationEntry = this@KtLightAnnotation.kotlinOrigin val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry) @@ -49,7 +48,12 @@ class KtLightAnnotation( val resolvedArgument = resolvedCall.valueArguments[parameter] ?: return@lazy null when (resolvedArgument) { 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 -> { diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt index eac57fc24ab..25cca4c8e72 100644 --- a/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt +++ b/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt @@ -16,16 +16,29 @@ package org.jetbrains.kotlin.idea +import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiConstantEvaluationHelper import com.intellij.psi.PsiElement +import com.intellij.psi.PsiExpression import com.intellij.psi.impl.ConstantExpressionEvaluator import org.jetbrains.kotlin.asJava.KtLightAnnotation 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.constants.ArrayValue +import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator as FrontendConstantExpressionEvaluator 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? { return computeExpression(expression, throwExceptionOnOverflow, null) } @@ -37,11 +50,24 @@ class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator { ): Any? { if (expression !is KtLightAnnotation.LightExpressionValue) return null val expressionToCompute = expression.originalExpression ?: return null - val resolutionFacade = expressionToCompute.getResolutionFacade() - val evaluator = FrontendConstantExpressionEvaluator(resolutionFacade.moduleDescriptor.builtIns) - val evaluatorTrace = DelegatingBindingTrace(resolutionFacade.analyze(expressionToCompute), "Evaluating annotation argument") - val constant = evaluator.evaluateExpression(expressionToCompute, evaluatorTrace) ?: return null - if (constant.isError) return null - return constant.getValue(TypeUtils.NO_EXPECTED_TYPE) + return when (expressionToCompute) { + is KtExpression -> { + val resolutionFacade = expressionToCompute.getResolutionFacade() + val evaluator = FrontendConstantExpressionEvaluator(resolutionFacade.moduleDescriptor.builtIns) + val evaluatorTrace = DelegatingBindingTrace(resolutionFacade.analyze(expressionToCompute), "Evaluating annotation argument") + + 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 + } } } \ No newline at end of file diff --git a/ultimate/testData/spring/core/references/completion/variants/beanWithDefaultName.kt b/ultimate/testData/spring/core/references/completion/variants/beanWithDefaultName.kt new file mode 100644 index 00000000000..5e76a257a97 --- /dev/null +++ b/ultimate/testData/spring/core/references/completion/variants/beanWithDefaultName.kt @@ -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") + } +} \ No newline at end of file diff --git a/ultimate/testData/spring/core/references/completion/variants/beanWithExplicitName.kt b/ultimate/testData/spring/core/references/completion/variants/beanWithExplicitName.kt new file mode 100644 index 00000000000..88bac89ef05 --- /dev/null +++ b/ultimate/testData/spring/core/references/completion/variants/beanWithExplicitName.kt @@ -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") + } +} \ No newline at end of file diff --git a/ultimate/testData/spring/core/references/completion/variants/spring-config.xml b/ultimate/testData/spring/core/references/completion/variants/scopeReferenceXml-config.xml similarity index 100% rename from ultimate/testData/spring/core/references/completion/variants/spring-config.xml rename to ultimate/testData/spring/core/references/completion/variants/scopeReferenceXml-config.xml diff --git a/ultimate/testData/spring/core/references/completion/variants/scopeReference.kt b/ultimate/testData/spring/core/references/completion/variants/scopeReferenceXml.kt similarity index 100% rename from ultimate/testData/spring/core/references/completion/variants/scopeReference.kt rename to ultimate/testData/spring/core/references/completion/variants/scopeReferenceXml.kt diff --git a/ultimate/testData/spring/core/references/completion/variants/springBeanReferenceXml-config.xml b/ultimate/testData/spring/core/references/completion/variants/springBeanReferenceXml-config.xml new file mode 100644 index 00000000000..b3fb31598a4 --- /dev/null +++ b/ultimate/testData/spring/core/references/completion/variants/springBeanReferenceXml-config.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/ultimate/testData/spring/core/references/completion/variants/springBeanReference.kt b/ultimate/testData/spring/core/references/completion/variants/springBeanReferenceXml.kt similarity index 100% rename from ultimate/testData/spring/core/references/completion/variants/springBeanReference.kt rename to ultimate/testData/spring/core/references/completion/variants/springBeanReferenceXml.kt diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/AbstractSpringReferenceCompletionTest.kt b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/AbstractSpringReferenceCompletionTest.kt index 53ba8756c94..aa20d14a8cb 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/AbstractSpringReferenceCompletionTest.kt +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/AbstractSpringReferenceCompletionTest.kt @@ -16,19 +16,22 @@ package org.jetbrains.kotlin.idea.spring.tests.references +import com.intellij.openapi.util.io.FileUtil import com.intellij.util.PathUtil import org.jetbrains.kotlin.idea.completion.test.AbstractJvmBasicCompletionTest import org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension import org.jetbrains.kotlin.idea.test.TestFixtureExtension -import java.io.File abstract class AbstractSpringReferenceCompletionTest : AbstractJvmBasicCompletionTest() { 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 .loadFixture(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() { diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java index f23764fb260..d1fa962540a 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java @@ -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); } - @TestMetadata("scopeReference.kt") - public void testScopeReference() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/scopeReference.kt"); + @TestMetadata("beanWithDefaultName.kt") + public void testBeanWithDefaultName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/beanWithDefaultName.kt"); doTest(fileName); } - @TestMetadata("springBeanReference.kt") - public void testSpringBeanReference() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/springBeanReference.kt"); + @TestMetadata("beanWithExplicitName.kt") + public void testBeanWithExplicitName() throws Exception { + 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); } }