From 05e9197580daeb9abc76e09a68d511a6096535f1 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 17 Jun 2025 13:32:54 +0200 Subject: [PATCH] [java-intentions] PsiElementFactoryImpl.createVariableDeclarationStatement: use 'var' if non-denotable type is requested Fixes IDEA-374601 Copy 'x' to effectively final variable breaks code when type is non-denotable GitOrigin-RevId: 3b2d15f24b182ac3948ae3b5066c5e68c1c0bf5c --- .../psi/impl/PsiElementFactoryImpl.java | 9 +++++++-- .../afterIfVarNonDenotable2.java | 18 ++++++++++++++++++ .../beforeIfVarNonDenotable2.java | 17 +++++++++++++++++ ...ntroduceVariableForSideEffectQualifier.java | 2 +- 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinalJava10/afterIfVarNonDenotable2.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinalJava10/beforeIfVarNonDenotable2.java diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiElementFactoryImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiElementFactoryImpl.java index b2f74d4b506e..ea6d53e2edb2 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiElementFactoryImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiElementFactoryImpl.java @@ -23,6 +23,7 @@ import com.intellij.psi.impl.source.tree.TreeElement; import com.intellij.psi.javadoc.PsiDocTag; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.IncorrectOperationException; @@ -592,11 +593,15 @@ public final class PsiElementFactoryImpl extends PsiJavaParserFacadeImpl impleme throw new IncorrectOperationException("Cannot create variable with type \"null\"."); } - String text = "X " + name + (initializer != null ? " = x" : "") + ";"; + String text = "var " + name + (initializer != null ? " = x" : "") + ";"; PsiDeclarationStatement statement = (PsiDeclarationStatement)createStatementFromText(text, context); PsiVariable variable = (PsiVariable)statement.getDeclaredElements()[0]; - replace(variable.getTypeElement(), createTypeElement(GenericsUtil.getVariableTypeByExpressionType(type)), text); + boolean generateVar = !PsiTypesUtil.isDenotableType(type, variable) && initializer != null && + PsiUtil.isAvailable(JavaFeature.LVTI, initializer); + if (!generateVar) { + replace(variable.getTypeElement(), createTypeElement(GenericsUtil.getVariableTypeByExpressionType(type)), text); + } boolean generateFinalLocals = context != null && JavaFileCodeStyleFacade.forContext(context.getContainingFile()).isGenerateFinalLocals(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinalJava10/afterIfVarNonDenotable2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinalJava10/afterIfVarNonDenotable2.java new file mode 100644 index 000000000000..8f2ab00ed274 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinalJava10/afterIfVarNonDenotable2.java @@ -0,0 +1,18 @@ +// "Copy 'x' to effectively final temp variable" "true-preview" +class Abc { + interface A{} + interface B{ + void m(); + } + + void test(Object obj) { + var x = (A & B) obj; + if (Math.random() > 0.5) { + x = null; + } + var finalX = x; + Runnable r = () -> { + if (finalX != null) finalX.m(); + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinalJava10/beforeIfVarNonDenotable2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinalJava10/beforeIfVarNonDenotable2.java new file mode 100644 index 000000000000..1f4c94733b8b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinalJava10/beforeIfVarNonDenotable2.java @@ -0,0 +1,17 @@ +// "Copy 'x' to effectively final temp variable" "true-preview" +class Abc { + interface A{} + interface B{ + void m(); + } + + void test(Object obj) { + var x = (A & B) obj; + if (Math.random() > 0.5) { + x = null; + } + Runnable r = () -> { + if (x != null) x.m(); + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/ig/com/siyeh/igfixes/style/methodRefs2lambda/afterIntroduceVariableForSideEffectQualifier.java b/java/java-tests/testData/ig/com/siyeh/igfixes/style/methodRefs2lambda/afterIntroduceVariableForSideEffectQualifier.java index d9843198f8bd..b09a6931a0f4 100644 --- a/java/java-tests/testData/ig/com/siyeh/igfixes/style/methodRefs2lambda/afterIntroduceVariableForSideEffectQualifier.java +++ b/java/java-tests/testData/ig/com/siyeh/igfixes/style/methodRefs2lambda/afterIntroduceVariableForSideEffectQualifier.java @@ -1,7 +1,7 @@ // "Replace method reference with lambda" "true-preview" class Test { { - Runnable runnable1 = new Runnable() { + var runnable1 = new Runnable() { { }