Spring Support: Fixed computation of values in Java annotations referred by Kotlin annotation entries

#KT-11702 Fixed
This commit is contained in:
Alexey Sedunov
2016-04-04 13:06:04 +03:00
parent a818d4714e
commit eeac739f69
11 changed files with 122 additions and 19 deletions
+1
View File
@@ -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
@@ -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<KtAnnotationEntry, PsiAnnotation> {
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 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 -> {
@@ -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
}
}
}
@@ -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>")
}
}
@@ -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>")
}
}
@@ -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>
@@ -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<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() {
@@ -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);
}
}