[java-inspections] IDEA-357323 Propose case null during adding all cases

GitOrigin-RevId: 6d7ffccc28d64ed0f52db0235c41796d1d81284e
This commit is contained in:
Mikhail Pyltsin
2024-08-09 11:10:54 +02:00
committed by intellij-monorepo-bot
parent 0f5aa949cb
commit cb37003541
14 changed files with 223 additions and 5 deletions

View File

@@ -473,9 +473,17 @@ public abstract class QuickFixFactory {
@NotNull Map<PsiType, Set<List<PsiType>>> branches,
@NotNull List<? extends PsiCaseLabelElement> elements);
@Nullable
public abstract IntentionAction createAddMissingSealedClassBranchesFixWithNull(@NotNull PsiSwitchBlock switchBlock,
@NotNull Set<String> missingCases,
@NotNull List<String> allNames);
@Nullable
public abstract IntentionAction createAddMissingBooleanPrimitiveBranchesFix(@NotNull PsiSwitchBlock block);
@Nullable
public abstract IntentionAction createAddMissingBooleanPrimitiveBranchesFixWithNull(@NotNull PsiSwitchBlock block);
@NotNull
public abstract IntentionAction createAddSwitchDefaultFix(@NotNull PsiSwitchBlock switchBlock, @Nullable String message);

View File

@@ -2431,6 +2431,7 @@ 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.branches.with.null.branch.fix.family.name=Create missing switch branches with null branch
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
return.of.collection.field.fix.family.name=Make return collection 'unmodifiable'

View File

@@ -743,6 +743,10 @@ public class PatternsInSwitchBlockHighlightingModel extends SwitchBlockHighlight
IntentionAction fix = getFixFactory().createAddMissingBooleanPrimitiveBranchesFix(myBlock);
if (fix != null) {
completenessInfoForSwitch.registerFix(fix, null, null, null, null);
IntentionAction fixWithNull = getFixFactory().createAddMissingBooleanPrimitiveBranchesFixWithNull(myBlock);
if (fixWithNull != null) {
completenessInfoForSwitch.registerFix(fixWithNull, null, null, null, null);
}
}
}
errorSink.accept(completenessInfoForSwitch);
@@ -868,6 +872,10 @@ public class PatternsInSwitchBlockHighlightingModel extends SwitchBlockHighlight
Set<String> missingCases = ContainerUtil.map2LinkedSet(missedClasses, PsiClass::getQualifiedName);
IntentionAction fix = getFixFactory().createAddMissingSealedClassBranchesFix(myBlock, missingCases, allNames);
info.registerFix(fix, null, null, null, null);
IntentionAction fixWithNull = getFixFactory().createAddMissingSealedClassBranchesFixWithNull(myBlock, missingCases, allNames);
if (fixWithNull != null) {
info.registerFix(fixWithNull, null, null, null, null);
}
return info;
}

View File

@@ -5,7 +5,10 @@ import com.intellij.codeInsight.CodeInsightWorkspaceSettings;
import com.intellij.codeInsight.actions.OptimizeImportsProcessor;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.*;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
import com.intellij.codeInsight.daemon.impl.DaemonListeners;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.SilentChangeVetoer;
import com.intellij.codeInsight.daemon.impl.analysis.IncreaseLanguageLevelFix;
import com.intellij.codeInsight.daemon.impl.analysis.UpgradeSdkFix;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
@@ -31,6 +34,7 @@ import com.intellij.lang.java.request.CreateMethodFromUsage;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.modcommand.Presentation;
import com.intellij.modcommand.PsiBasedModCommandAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
@@ -874,6 +878,18 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
return new CreateSealedClassMissingSwitchBranchesFix(switchBlock, missingCases, allNames).asIntention();
}
@Override
public @Nullable IntentionAction createAddMissingSealedClassBranchesFixWithNull(@NotNull PsiSwitchBlock switchBlock,
@NotNull Set<String> missingCases,
@NotNull List<String> allNames) {
PsiBasedModCommandAction<PsiSwitchBlock> withNull =
CreateSealedClassMissingSwitchBranchesFix.createWithNull(switchBlock, missingCases, allNames);
if (withNull == null) {
return null;
}
return withNull.asIntention();
}
@Override
public @Nullable IntentionAction createAddMissingBooleanPrimitiveBranchesFix(@NotNull PsiSwitchBlock block) {
CreateMissingBooleanPrimitiveBranchesFix fix = CreateMissingBooleanPrimitiveBranchesFix.createFix(block);
@@ -881,6 +897,13 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
return fix.asIntention();
}
@Override
public @Nullable IntentionAction createAddMissingBooleanPrimitiveBranchesFixWithNull(@NotNull PsiSwitchBlock block) {
@Nullable PsiBasedModCommandAction<PsiSwitchBlock> fix = CreateMissingBooleanPrimitiveBranchesFix.createWithNull(block);
if (fix == null) return null;
return fix.asIntention();
}
@Override
public @Nullable IntentionAction createAddMissingRecordClassBranchesFix(@NotNull PsiSwitchBlock switchBlock,
@NotNull PsiClass selectorType,

View File

@@ -26,8 +26,15 @@ import java.util.Objects;
public final class CreateNullBranchFix extends BaseSwitchFix {
private final boolean myStartTemplate;
public CreateNullBranchFix(@NotNull PsiSwitchBlock block) {
this(block, true);
}
public CreateNullBranchFix(@NotNull PsiSwitchBlock block, boolean startTemplate) {
super(block);
myStartTemplate = startTemplate;
}
@Override
@@ -61,7 +68,9 @@ public final class CreateNullBranchFix extends BaseSwitchFix {
.stream()
.map(text -> factory.createStatementFromText(text, body))
.forEach(statement -> body.addBefore(statement, anchor));
CreateDefaultBranchFix.startTemplateOnStatement(PsiTreeUtil.getPrevSiblingOfType(anchor, PsiStatement.class), updater);
if (myStartTemplate) {
CreateDefaultBranchFix.startTemplateOnStatement(PsiTreeUtil.getPrevSiblingOfType(anchor, PsiStatement.class), updater);
}
}
private static @Nullable PsiElement findUnconditionalLabel(@NotNull PsiSwitchBlock switchBlock) {

View File

@@ -1,15 +1,25 @@
// Copyright 2000-2023 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.codeInsight.Nullability;
import com.intellij.codeInspection.dataFlow.NullabilityUtil;
import com.intellij.codeInspection.util.IntentionName;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.Presentation;
import com.intellij.modcommand.PsiUpdateModCommandAction;
import com.intellij.modcommand.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiSwitchBlock;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.dataflow.CreateNullBranchFix;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.SwitchUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Supplier;
public abstract class BaseSwitchFix extends PsiUpdateModCommandAction<PsiSwitchBlock> {
public BaseSwitchFix(@NotNull PsiSwitchBlock block) {
super(block);
@@ -25,4 +35,35 @@ public abstract class BaseSwitchFix extends PsiUpdateModCommandAction<PsiSwitchB
protected @IntentionName String getText(@NotNull PsiSwitchBlock switchBlock) {
return getFamilyName();
}
protected static @Nullable PsiBasedModCommandAction<PsiSwitchBlock> createWithNull(@NotNull PsiSwitchBlock block,
@NotNull Supplier<? extends @Nullable BaseSwitchFix> producer) {
PsiExpression expression = block.getExpression();
Nullability nullability = NullabilityUtil.getExpressionNullability(expression, true);
if (nullability != Nullability.NULLABLE) return null;
List<PsiElement> branches = SwitchUtils.getSwitchBranches(block);
if (ContainerUtil.or(branches,
branch -> branch instanceof PsiExpression psiExpression && ExpressionUtils.isNullLiteral(psiExpression))) {
return null;
}
BaseSwitchFix action = producer.get();
if (action == null) return null;
BaseSwitchFix nullBranchFix = new CreateNullBranchFix(block, false);
return new PsiBasedModCommandAction<>(block) {
@Override
public @NotNull String getFamilyName() {
return InspectionGadgetsBundle.message("create.missing.branches.with.null.branch.fix.family.name");
}
@Override
protected @NotNull ModCommand perform(@NotNull ActionContext context, @NotNull PsiSwitchBlock element) {
return ModCommand.psiUpdate(element, (e, upd) -> {
action.invoke(context, e, upd);
nullBranchFix.invoke(context, e, upd);
});
}
};
}
}

View File

@@ -1,6 +1,7 @@
// 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.modcommand.PsiBasedModCommandAction;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.InspectionGadgetsBundle;
@@ -55,6 +56,11 @@ public final class CreateMissingBooleanPrimitiveBranchesFix extends CreateMissin
return new CreateMissingBooleanPrimitiveBranchesFix(block, new HashSet<>(missed));
}
@Nullable
public static PsiBasedModCommandAction<PsiSwitchBlock> createWithNull(@NotNull PsiSwitchBlock block) {
return createWithNull(block, () -> createFix(block));
}
@Override
public @Nls(capitalization = Nls.Capitalization.Sentence) @NotNull String getFamilyName() {
return InspectionGadgetsBundle.message("create.missing.boolean.switch.branches.fix.family.name");

View File

@@ -1,6 +1,7 @@
// 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.modcommand.PsiBasedModCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
@@ -9,6 +10,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.siyeh.InspectionGadgetsBundle;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Function;
@@ -90,4 +92,11 @@ public class CreateSealedClassMissingSwitchBranchesFix extends CreateMissingSwit
return ContainerUtil.map(list.getElements(), PsiCaseLabelElement::getText);
};
}
@Nullable
public static PsiBasedModCommandAction<PsiSwitchBlock> createWithNull(@NotNull PsiSwitchBlock block,
@NotNull Set<String> cases,
@NotNull List<String> names) {
return createWithNull(block, () -> new CreateSealedClassMissingSwitchBranchesFix(block, cases, names));
}
}

View File

@@ -0,0 +1,20 @@
// "Create missing switch branches with null branch" "true-preview"
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class Test {
public static void main(String[] args) {
test(true);
}
public void test(@Nullable Boolean b) {
switch (b) {
case true -> {
}
case false -> {
}
case null -> {
}
}
}
}

View File

@@ -0,0 +1,15 @@
// "Create missing switch branches with null branch" "true-preview"
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class Test {
public static void main(String[] args) {
test(true);
}
public void test(@Nullable Boolean b) {
switch (b<caret>) {
}
}
}

View File

@@ -0,0 +1,15 @@
// "Create missing switch branches with null branch" "false"
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class Test {
public static void main(String[] args) {
test(true);
}
public void test(@Nullable Boolean b) {
switch (b<caret>) {
case null -> System.out.println();
}
}
}

View File

@@ -0,0 +1,25 @@
// "Create missing switch branches with null branch" "true-preview"
import org.jetbrains.annotations.Nullable;
class Test {
sealed interface SA permits R1, R2 {
}
record R1() implements SA {
}
record R2() implements SA {
}
public void test(@Nullable SA sa) {
switch (sa) {
case R1 r1 -> {
}
case R2 r2 -> {
}
case null -> {
}
}
}
}

View File

@@ -0,0 +1,19 @@
// "Create missing switch branches with null branch" "false"
import org.jetbrains.annotations.NotNull;
class Test {
sealed interface SA permits R1, R2 {
}
record R1() implements SA {
}
record R2() implements SA {
}
public void test(@NotNull SA sa) {
switch (sa<caret>) {
}
}
}

View File

@@ -0,0 +1,19 @@
// "Create missing switch branches with null branch" "true-preview"
import org.jetbrains.annotations.Nullable;
class Test {
sealed interface SA permits R1, R2 {
}
record R1() implements SA {
}
record R2() implements SA {
}
public void test(@Nullable SA sa) {
switch (sa<caret>) {
}
}
}