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
|
||||
- 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
|
||||
}
|
||||
}
|
||||
}
|
||||
+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
|
||||
|
||||
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() {
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user