diff --git a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java index 37e47512d5f7..f05b46098daa 100644 --- a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java +++ b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java @@ -473,6 +473,9 @@ public abstract class QuickFixFactory { @NotNull Map>> branches, @NotNull List elements); + @Nullable + public abstract IntentionAction createAddMissingBooleanPrimitiveBranchesFix(@NotNull PsiSwitchBlock block); + @NotNull public abstract IntentionAction createAddSwitchDefaultFix(@NotNull PsiSwitchBlock switchBlock, @Nullable String message); diff --git a/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties b/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties index a9cd2058f8fc..dfb6a642f822 100644 --- a/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties +++ b/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties @@ -2426,6 +2426,7 @@ replace.case.default.null.with.null.default=Replace 'case default, null' with 'c create.default.branch.fix.family.name=Insert 'default' branch create.null.branch.fix.family.name=Insert 'null' branch create.missing.enum.switch.branches.fix.family.name=Create missing enum switch branches +create.missing.boolean.switch.branches.fix.family.name=Create missing boolean switch branches create.missing.sealed.class.switch.branches.fix.family.name=Create missing sealed class switch branches create.missing.record.deconstructions.switch.branches.fix.family.name=Create missing record deconstruction switch branches unnecessary.fully.qualified.name.fix.family.name=Replace fully qualified name diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PatternsInSwitchBlockHighlightingModel.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PatternsInSwitchBlockHighlightingModel.java index e7f89eba2d8c..a55665e51c81 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PatternsInSwitchBlockHighlightingModel.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PatternsInSwitchBlockHighlightingModel.java @@ -738,7 +738,14 @@ public class PatternsInSwitchBlockHighlightingModel extends SwitchBlockHighlight } } else { - errorSink.accept(createCompletenessInfoForSwitch(!elements.isEmpty())); + HighlightInfo.Builder completenessInfoForSwitch = createCompletenessInfoForSwitch(!elements.isEmpty()); + if (mySelectorKind == SelectorKind.BOOLEAN) { + IntentionAction fix = getFixFactory().createAddMissingBooleanPrimitiveBranchesFix(myBlock); + if (fix != null) { + completenessInfoForSwitch.registerFix(fix, null, null, null, null); + } + } + errorSink.accept(completenessInfoForSwitch); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java index 9ddb81fdfe91..e157f9d46892 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java @@ -874,6 +874,13 @@ public final class QuickFixFactoryImpl extends QuickFixFactory { return new CreateSealedClassMissingSwitchBranchesFix(switchBlock, missingCases, allNames).asIntention(); } + @Override + public @Nullable IntentionAction createAddMissingBooleanPrimitiveBranchesFix(@NotNull PsiSwitchBlock block) { + CreateMissingBooleanPrimitiveBranchesFix fix = CreateMissingBooleanPrimitiveBranchesFix.createFix(block); + if (fix == null) return null; + return fix.asIntention(); + } + @Override public @Nullable IntentionAction createAddMissingRecordClassBranchesFix(@NotNull PsiSwitchBlock switchBlock, @NotNull PsiClass selectorType, diff --git a/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingBooleanPrimitiveBranchesFix.java b/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingBooleanPrimitiveBranchesFix.java new file mode 100644 index 000000000000..d0acc42f01cf --- /dev/null +++ b/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingBooleanPrimitiveBranchesFix.java @@ -0,0 +1,75 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.siyeh.ig.fixes; + +import com.intellij.psi.*; +import com.intellij.util.containers.ContainerUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.psiutils.SwitchUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Function; + +public final class CreateMissingBooleanPrimitiveBranchesFix extends CreateMissingSwitchBranchesFix { + private static final List ALL_BOOLEAN_BRANCHES = List.of(PsiKeyword.TRUE, PsiKeyword.FALSE); + + private CreateMissingBooleanPrimitiveBranchesFix(@NotNull PsiSwitchBlock block, @NotNull Set names) { + super(block, names); + } + + @Nullable + public static CreateMissingBooleanPrimitiveBranchesFix createFix(@NotNull PsiSwitchBlock block) { + PsiExpression selectorExpression = block.getExpression(); + if (selectorExpression == null) return null; + PsiType selectorExpressionType = selectorExpression.getType(); + if (selectorExpressionType == null) return null; + PsiPrimitiveType selectorPrimitiveType = PsiPrimitiveType.getOptionallyUnboxedType(selectorExpressionType); + if (selectorPrimitiveType == null) return null; + if (!PsiTypes.booleanType().equals(selectorPrimitiveType)) return null; + if (SwitchUtils.findDefaultElement(block) != null) return null; + List branches = SwitchUtils.getSwitchBranches(block); + PsiClassType boxedBooleanType = selectorPrimitiveType.getBoxedType(block); + if (boxedBooleanType == null) return null; + Set existed = new HashSet<>(); + for (PsiElement branch : branches) { + if (branch instanceof PsiTypeTestPattern testPattern) { + PsiPatternVariable patternVariable = testPattern.getPatternVariable(); + if (patternVariable == null) continue; + PsiType type = patternVariable.getType(); + if (type.isAssignableFrom(boxedBooleanType)) return null; + } + if (branch instanceof PsiLiteralExpression literal && literal.getValue() instanceof Boolean) { + existed.add(literal.getText()); + } + } + List missed = new ArrayList<>(); + for (String branch : ALL_BOOLEAN_BRANCHES) { + if (!existed.contains(branch)) { + missed.add(branch); + } + } + if (missed.isEmpty()) return null; + return new CreateMissingBooleanPrimitiveBranchesFix(block, new HashSet<>(missed)); + } + + @Override + public @Nls(capitalization = Nls.Capitalization.Sentence) @NotNull String getFamilyName() { + return InspectionGadgetsBundle.message("create.missing.boolean.switch.branches.fix.family.name"); + } + + @Override + protected @NotNull List getAllNames(@NotNull PsiClass aClass, @NotNull PsiSwitchBlock switchBlock) { + return ALL_BOOLEAN_BRANCHES; + } + + @Override + protected @NotNull Function> getCaseExtractor() { + return label -> { + PsiCaseLabelElementList list = label.getCaseLabelElementList(); + if (list == null) return Collections.emptyList(); + return ContainerUtil.map(list.getElements(), PsiCaseLabelElement::getText); + }; + } +} diff --git a/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingSwitchBranchesFix.java b/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingSwitchBranchesFix.java index 970c455e56d3..ec3d28091ba6 100644 --- a/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingSwitchBranchesFix.java +++ b/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingSwitchBranchesFix.java @@ -28,7 +28,11 @@ public abstract class CreateMissingSwitchBranchesFix extends BaseSwitchFix { protected void invoke(@NotNull ActionContext context, @NotNull PsiSwitchBlock switchBlock, @NotNull ModPsiUpdater updater) { final PsiExpression selector = switchBlock.getExpression(); if (selector == null) return; - final PsiClassType switchType = (PsiClassType)selector.getType(); + PsiType selectorType = selector.getType(); + if(selectorType instanceof PsiPrimitiveType primitiveType){ + selectorType = primitiveType.getBoxedType(selector); + } + final PsiClassType switchType = (PsiClassType)selectorType; if (switchType == null) return; final PsiClass psiClass = switchType.resolve(); if (psiClass == null) return; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterAllBooleans.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterAllBooleans.java new file mode 100644 index 000000000000..504b9cd8f469 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterAllBooleans.java @@ -0,0 +1,17 @@ +// "Create missing branches: 'false', and 'true'" "true-preview" +import java.util.List; + +class Test { + public static void main(String[] args) { + test(true); + } + + public static void test(boolean b) { + switch (b){ + case true -> { + } + case false -> { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterAllBoxedBooleans.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterAllBoxedBooleans.java new file mode 100644 index 000000000000..31c562066598 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterAllBoxedBooleans.java @@ -0,0 +1,17 @@ +// "Create missing branches: 'false', and 'true'" "true-preview" +import java.util.List; + +class Test { + public static void main(String[] args) { + test(true); + } + + public static void test(Boolean b) { + switch (b){ + case true -> { + } + case false -> { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterMissedFalse.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterMissedFalse.java new file mode 100644 index 000000000000..e57331e10340 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterMissedFalse.java @@ -0,0 +1,17 @@ +// "Create missing switch branch 'true'" "true-preview" +import java.util.List; + +class Test { + public static void main(String[] args) { + test(true); + } + + public static void test(boolean b) { + switch (b){ + case true -> { + } + case false -> { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterMissedTrue.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterMissedTrue.java new file mode 100644 index 000000000000..210e93ea8e20 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/afterMissedTrue.java @@ -0,0 +1,17 @@ +// "Create missing switch branch 'false'" "true-preview" +import java.util.List; + +class Test { + public static void main(String[] args) { + test(true); + } + + public static void test(boolean b) { + switch (b){ + case true -> { + } + case false -> { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeAllBooleans.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeAllBooleans.java new file mode 100644 index 000000000000..f711f78f5517 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeAllBooleans.java @@ -0,0 +1,14 @@ +// "Create missing branches: 'false', and 'true'" "true-preview" +import java.util.List; + +class Test { + public static void main(String[] args) { + test(true); + } + + public static void test(boolean b) { + switch (b){ + + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeAllBoxedBooleans.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeAllBoxedBooleans.java new file mode 100644 index 000000000000..c3e7cce4c522 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeAllBoxedBooleans.java @@ -0,0 +1,14 @@ +// "Create missing branches: 'false', and 'true'" "true-preview" +import java.util.List; + +class Test { + public static void main(String[] args) { + test(true); + } + + public static void test(Boolean b) { + switch (b){ + + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeMissedFalse.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeMissedFalse.java new file mode 100644 index 000000000000..af7a9830bd07 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeMissedFalse.java @@ -0,0 +1,15 @@ +// "Create missing switch branch 'true'" "true-preview" +import java.util.List; + +class Test { + public static void main(String[] args) { + test(true); + } + + public static void test(boolean b) { + switch (b){ + case false -> { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeMissedTrue.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeMissedTrue.java new file mode 100644 index 000000000000..90ca32a819de --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches/beforeMissedTrue.java @@ -0,0 +1,15 @@ +// "Create missing switch branch 'false'" "true-preview" +import java.util.List; + +class Test { + public static void main(String[] args) { + test(true); + } + + public static void test(boolean b) { + switch (b){ + case true -> { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/CreateMissingBooleanPrimitiveBranchesFixTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/CreateMissingBooleanPrimitiveBranchesFixTest.java new file mode 100644 index 000000000000..ccca42e311ae --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/CreateMissingBooleanPrimitiveBranchesFixTest.java @@ -0,0 +1,26 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.codeInsight.daemon.impl.quickfix; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; +import com.intellij.pom.java.JavaFeature; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; + +public class CreateMissingBooleanPrimitiveBranchesFixTest extends LightQuickFixParameterizedTestCase { + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/createMissingBooleanPrimitiveBranches"; + } + + @Override + protected LanguageLevel getLanguageLevel() { + return JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS.getMinimumLevel(); + } + + @Override + protected @NotNull LightProjectDescriptor getProjectDescriptor() { + return LightJavaCodeInsightFixtureTestCase.JAVA_23; + } +}