From 26f4517a0e05ad1cb894660264b63ebefc0a9dec Mon Sep 17 00:00:00 2001 From: Evgenii Zakharchenko Date: Mon, 9 Sep 2024 15:38:56 +0200 Subject: [PATCH] [spring] IDEA-357318 Spring: inject on completion in constructor produces broken code GitOrigin-RevId: 79fa4eeac4be5e1520963e1813be22c94db52b69 --- .../lang/jvm/actions/CreateFieldRequest.java | 8 ++++++++ ...AddFieldActionCreateCallableFromUsageFix.kt | 6 +++++- .../KotlinUastBaseCodeGenerationPlugin.kt | 16 +++++++++++++--- .../uast/generate/UastCodeGenerationPlugin.kt | 2 +- .../generate/JavaUastCodeGenerationPlugin.kt | 18 +++++++++++------- 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/java/java-analysis-api/src/com/intellij/lang/jvm/actions/CreateFieldRequest.java b/java/java-analysis-api/src/com/intellij/lang/jvm/actions/CreateFieldRequest.java index bdbc98760067..1403d0dc9dcd 100644 --- a/java/java-analysis-api/src/com/intellij/lang/jvm/actions/CreateFieldRequest.java +++ b/java/java-analysis-api/src/com/intellij/lang/jvm/actions/CreateFieldRequest.java @@ -56,6 +56,14 @@ public interface CreateFieldRequest extends ActionRequest { */ boolean isConstant(); + /** + * Only for Kotlin + * Determines whether an empty initializer should be created for the field. + * + * @return true if an empty initializer should be created, false otherwise. + */ + default boolean isCreateEmptyInitializer() { return true; } + /** * @return should start live template after a new field was created. */ diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/AddFieldActionCreateCallableFromUsageFix.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/AddFieldActionCreateCallableFromUsageFix.kt index a8898f93dc8d..c653a0d3c89d 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/AddFieldActionCreateCallableFromUsageFix.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/AddFieldActionCreateCallableFromUsageFix.kt @@ -36,7 +36,11 @@ class AddFieldActionCreateCallableFromUsageFix( val initializer = if (requestInitializer is JvmLong) { psiFactory.createExpression("${requestInitializer.longValue}L") } else if (!lateinit) { - psiFactory.createExpression("TODO(\"initialize me\")") + if (request.isCreateEmptyInitializer) { + psiFactory.createExpression("TODO(\"initialize me\")") + } else { + null + } } else null if (!request.isValid) null else PropertyInfo( diff --git a/plugins/kotlin/uast/uast-kotlin-base/src/org/jetbrains/uast/kotlin/generate/KotlinUastBaseCodeGenerationPlugin.kt b/plugins/kotlin/uast/uast-kotlin-base/src/org/jetbrains/uast/kotlin/generate/KotlinUastBaseCodeGenerationPlugin.kt index c1edb2928ea5..91509e299694 100644 --- a/plugins/kotlin/uast/uast-kotlin-base/src/org/jetbrains/uast/kotlin/generate/KotlinUastBaseCodeGenerationPlugin.kt +++ b/plugins/kotlin/uast/uast-kotlin-base/src/org/jetbrains/uast/kotlin/generate/KotlinUastBaseCodeGenerationPlugin.kt @@ -85,7 +85,7 @@ abstract class KotlinUastBaseCodeGenerationPlugin : UastCodeGenerationPlugin { TODO("Not implemented") } - override fun initializeField(uField: UField, uParameter: UParameter): UExpression? { + override fun initializeField(uField: UField, uParameter: UParameter, anchor: PsiElement?, addBefore: Boolean): UExpression? { val uMethod = uParameter.getParentOfType(UMethod::class.java, false) ?: return null val sourcePsi = uMethod.sourcePsi ?: return null if (sourcePsi is KtPrimaryConstructor) { @@ -125,8 +125,18 @@ abstract class KotlinUastBaseCodeGenerationPlugin : UastCodeGenerationPlugin { appendFixedText(" = ") appendName(Name.identifier(uParameter.name)) } - - body.addBefore(assignmentExpression, body.rBrace) + if (anchor != null) { + val newLine = ktPsiFactory.createWhiteSpace("\n") + if (addBefore) { + val anchor = body.addBefore(newLine, anchor) + body.addBefore(assignmentExpression, anchor) + } else { + val anchor = body.addAfter(newLine, anchor) + body.addAfter(assignmentExpression, anchor) + } + } else { + body.addBefore(assignmentExpression, body.rBrace) + } return assignmentExpression.toUElementOfType() } diff --git a/uast/uast-common/src/org/jetbrains/uast/generate/UastCodeGenerationPlugin.kt b/uast/uast-common/src/org/jetbrains/uast/generate/UastCodeGenerationPlugin.kt index 0ad00e571956..f54f721da052 100644 --- a/uast/uast-common/src/org/jetbrains/uast/generate/UastCodeGenerationPlugin.kt +++ b/uast/uast-common/src/org/jetbrains/uast/generate/UastCodeGenerationPlugin.kt @@ -96,7 +96,7 @@ interface UastCodeGenerationPlugin { * If the parameter is from Kotlin primary constructor and the field and the parameter have different names, * Kotlin property is initialized with the parameter. */ - fun initializeField(uField: UField, uParameter: UParameter): UExpression? + fun initializeField(uField: UField, uParameter: UParameter, anchor: PsiElement? = null, addBefore: Boolean = false): UExpression? /** * Creates new return expression with changed return label for Explicit return expression (for Kotlin) diff --git a/uast/uast-java-ide/src/org/jetbrains/uast/java/generate/JavaUastCodeGenerationPlugin.kt b/uast/uast-java-ide/src/org/jetbrains/uast/java/generate/JavaUastCodeGenerationPlugin.kt index 09597b34672b..703d743d35f6 100644 --- a/uast/uast-java-ide/src/org/jetbrains/uast/java/generate/JavaUastCodeGenerationPlugin.kt +++ b/uast/uast-java-ide/src/org/jetbrains/uast/java/generate/JavaUastCodeGenerationPlugin.kt @@ -117,7 +117,7 @@ internal class JavaUastCodeGenerationPlugin : UastCodeGenerationPlugin { return ptr.element?.parent.toUElementOfType() } - override fun initializeField(uField: UField, uParameter: UParameter): UExpression? { + override fun initializeField(uField: UField, uParameter: UParameter, anchor: PsiElement?, addBefore: Boolean): UExpression? { val uMethod = uParameter.getParentOfType(UMethod::class.java, false) ?: return null val psiMethod = uMethod.sourcePsi as? PsiMethod ?: return null val body = psiMethod.body ?: return null @@ -125,12 +125,16 @@ internal class JavaUastCodeGenerationPlugin : UastCodeGenerationPlugin { val elementFactory = JavaPsiFacade.getInstance(psiMethod.project).elementFactory val prefix = if (uField.name == uParameter.name) "this." else "" val statement = elementFactory.createStatementFromText("$prefix${uField.name} = ${uParameter.name};", psiMethod) - val lastBodyElement = body.lastBodyElement - if (lastBodyElement is PsiWhiteSpace) { - lastBodyElement.replace(statement) - } - else { - body.add(statement) + if (anchor != null) { + if (addBefore) body.addBefore(statement, anchor) else body.addAfter(statement, anchor) + } else { + val lastBodyElement = body.lastBodyElement + if (lastBodyElement is PsiWhiteSpace) { + lastBodyElement.replace(statement) + } + else { + body.add(statement) + } } return statement.toUElementOfType() }