From 0d5ea1d59e3a874d85ee2220b2f0efcffd2f4f3a Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 17 Jun 2025 13:20:49 +0200 Subject: [PATCH] [java-intentions] Disable 'convert to if' when if-condition refers to the variable Also: fix for var types Fixes IDEA-374029 "Make effectively final" generates uncompilable code GitOrigin-RevId: 17932aefe036dec167aabaf2066c4cdff11947da --- .../MoveInitializerToIfBranchFixer.java | 12 ++++++- .../makeVarEffectivelyFinal/afterIfVar.java | 12 +++++++ .../beforeIfUsedInCondition.java | 31 +++++++++++++++++++ .../makeVarEffectivelyFinal/beforeIfVar.java | 10 ++++++ .../beforeIfVarNonDenotable.java | 17 ++++++++++ 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/afterIfVar.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfUsedInCondition.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfVar.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfVarNonDenotable.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/makefinal/MoveInitializerToIfBranchFixer.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/makefinal/MoveInitializerToIfBranchFixer.java index 4a1393b8e072..4550eba2a50b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/makefinal/MoveInitializerToIfBranchFixer.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/makefinal/MoveInitializerToIfBranchFixer.java @@ -6,6 +6,7 @@ import com.intellij.java.JavaBundle; import com.intellij.psi.*; import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.psiutils.ExpressionUtils; @@ -24,11 +25,16 @@ import java.util.Objects; final class MoveInitializerToIfBranchFixer implements EffectivelyFinalFixer { @Override public boolean isAvailable(@NotNull PsiLocalVariable var) { + if (var.getTypeElement().isInferredType() && + !PsiTypesUtil.isDenotableType(var.getType(), var)) { + return false; + } PsiExpression initializer = var.getInitializer(); Branched branched = extractInitMode(var); // Do not add too many branches return branched != null && branched.numberOfNonInitializedBranches() <= 3 && - canReorder(initializer, branched); + canReorder(initializer, branched) && + branched.conditions().noneMatch(cond -> VariableAccessUtils.variableIsUsed(var, cond)); } @Override @@ -38,6 +44,10 @@ final class MoveInitializerToIfBranchFixer implements EffectivelyFinalFixer { PsiStatement statement = JavaPsiFacade.getElementFactory(var.getProject()) .createStatementFromText(var.getName() + "=" + initializer.getText() + ";", null); branched.addInitializer(statement); + PsiTypeElement typeElement = var.getTypeElement(); + if (typeElement.isInferredType()) { + PsiTypesUtil.replaceWithExplicitType(typeElement); + } initializer.delete(); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/afterIfVar.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/afterIfVar.java new file mode 100644 index 000000000000..8eb34b5e108b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/afterIfVar.java @@ -0,0 +1,12 @@ +// "Make 'x' effectively final by moving initializer to the 'if' statement" "true-preview" +class Abc { + void test() { + int x; + if (Math.random() > 0.5) { + x = 10; + } else { + x = 5; + } + Runnable r = () -> System.out.println(x); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfUsedInCondition.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfUsedInCondition.java new file mode 100644 index 000000000000..9caacb51b212 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfUsedInCondition.java @@ -0,0 +1,31 @@ +// "Make 'companies' effectively final by moving initializer to the 'if' statement" "false" +import java.util.List; + +class Abc { + + private void method(X x, List list) { + var companies = x.getCompanies(); + if (companies.isEmpty()) { + companies = List.of(x.getCompanyId()); + } + list.forEach(w -> something(companies)); + } + + private void something(List companies) { + } + + static public class X { + + private int companyId; + + private List companies; + + public int getCompanyId() { + return companyId; + } + + public List getCompanies() { + return companies; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfVar.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfVar.java new file mode 100644 index 000000000000..fd741ae60dae --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfVar.java @@ -0,0 +1,10 @@ +// "Make 'x' effectively final by moving initializer to the 'if' statement" "true-preview" +class Abc { + void test() { + var x = 5; + if (Math.random() > 0.5) { + x = 10; + } + Runnable r = () -> System.out.println(x); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfVarNonDenotable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfVarNonDenotable.java new file mode 100644 index 000000000000..1d05757d934a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/makeVarEffectivelyFinal/beforeIfVarNonDenotable.java @@ -0,0 +1,17 @@ +// "Make 'x' effectively final by moving initializer to the 'if' statement" "false" +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