mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 11:50:54 +07:00
[java-highlighting] IDEA-324652 Unreachable branch quickfix produces incorrect code
GitOrigin-RevId: 3c00dae40f890c0a5bfda425f9c30d479f4d40b3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9081464e94
commit
830275db3c
@@ -862,33 +862,44 @@ public class SwitchBlockHighlightingModel {
|
||||
for (int j = i + 1; j < switchLabels.size(); j++) {
|
||||
PsiElement next = switchLabels.get(j);
|
||||
if (!(next instanceof PsiCaseLabelElement nextElement)) continue;
|
||||
if (!JavaPsiPatternUtil.isUnconditionalForType(nextElement, mySelectorType) &&
|
||||
((!(next instanceof PsiExpression expression) || ExpressionUtils.isNullLiteral(expression)) &&
|
||||
current instanceof PsiKeyword &&
|
||||
PsiKeyword.DEFAULT.equals(current.getText()) || isInCaseNullDefaultLabel(current))) {
|
||||
// JEP 440-441
|
||||
// A 'default' label dominates a case label with a case pattern,
|
||||
// and it also dominates a case label with a null case constant.
|
||||
// A 'case null, default' label dominates all other switch labels.
|
||||
boolean dominated = isDominated(nextElement, current, mySelectorType);
|
||||
if (dominated) {
|
||||
result.put(nextElement, current);
|
||||
}
|
||||
else if (current instanceof PsiCaseLabelElement currentElement) {
|
||||
if (isConstantLabelElement(nextElement)) {
|
||||
PsiExpression constExpr = ObjectUtils.tryCast(nextElement, PsiExpression.class);
|
||||
assert constExpr != null;
|
||||
if (JavaPsiPatternUtil.dominatesOverConstant(currentElement, constExpr.getType())) {
|
||||
result.put(nextElement, current);
|
||||
}
|
||||
}
|
||||
else if (JavaPsiPatternUtil.dominates(currentElement, nextElement)) {
|
||||
result.put(nextElement, current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isDominated(@NotNull PsiCaseLabelElement overWhom,
|
||||
@NotNull PsiElement who,
|
||||
@NotNull PsiType selectorType) {
|
||||
boolean dominated = false;
|
||||
if (!JavaPsiPatternUtil.isUnconditionalForType(overWhom, selectorType) &&
|
||||
((!(overWhom instanceof PsiExpression expression) || ExpressionUtils.isNullLiteral(expression)) &&
|
||||
who instanceof PsiKeyword &&
|
||||
PsiKeyword.DEFAULT.equals(who.getText()) || isInCaseNullDefaultLabel(who))) {
|
||||
// JEP 440-441
|
||||
// A 'default' label dominates a case label with a case pattern,
|
||||
// and it also dominates a case label with a null case constant.
|
||||
// A 'case null, default' label dominates all other switch labels.
|
||||
dominated =true;
|
||||
}
|
||||
else if (who instanceof PsiCaseLabelElement currentElement) {
|
||||
if (isConstantLabelElement(overWhom)) {
|
||||
PsiExpression constExpr = ObjectUtils.tryCast(overWhom, PsiExpression.class);
|
||||
assert constExpr != null;
|
||||
if (JavaPsiPatternUtil.dominatesOverConstant(currentElement, constExpr.getType())) {
|
||||
dominated =true;
|
||||
}
|
||||
}
|
||||
else if (JavaPsiPatternUtil.dominates(currentElement, overWhom)) {
|
||||
dominated =true;
|
||||
}
|
||||
}
|
||||
return dominated;
|
||||
}
|
||||
|
||||
private static boolean isInCaseNullDefaultLabel(@NotNull PsiElement element) {
|
||||
PsiCaseLabelElementList list = ObjectUtils.tryCast(element.getParent(), PsiCaseLabelElementList.class);
|
||||
if (list == null || list.getElementCount() != 2) return false;
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.jetbrains.annotations.PropertyKey;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.intellij.codeInsight.daemon.impl.analysis.SwitchBlockHighlightingModel.PatternsInSwitchBlockHighlightingModel;
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool {
|
||||
@@ -58,7 +59,8 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
|
||||
@Override
|
||||
public void writeSettings(@NotNull Element node) throws WriteExternalException {
|
||||
node.addContent(new Element("option").setAttribute("name", "SUGGEST_NULLABLE_ANNOTATIONS").setAttribute("value", String.valueOf(SUGGEST_NULLABLE_ANNOTATIONS)));
|
||||
node.addContent(new Element("option").setAttribute("name", "SUGGEST_NULLABLE_ANNOTATIONS")
|
||||
.setAttribute("value", String.valueOf(SUGGEST_NULLABLE_ANNOTATIONS)));
|
||||
// Preserved for serialization compatibility
|
||||
node.addContent(new Element("option").setAttribute("name", "DONT_REPORT_TRUE_ASSERT_STATEMENTS").setAttribute("value", "false"));
|
||||
if (IGNORE_ASSERT_STATEMENTS) {
|
||||
@@ -68,10 +70,12 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
node.addContent(new Element("option").setAttribute("name", "TREAT_UNKNOWN_MEMBERS_AS_NULLABLE").setAttribute("value", "true"));
|
||||
}
|
||||
if (!REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER) {
|
||||
node.addContent(new Element("option").setAttribute("name", "REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER").setAttribute("value", "false"));
|
||||
node.addContent(
|
||||
new Element("option").setAttribute("name", "REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER").setAttribute("value", "false"));
|
||||
}
|
||||
if (!REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL) {
|
||||
node.addContent(new Element("option").setAttribute("name", "REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL").setAttribute("value", "false"));
|
||||
node.addContent(
|
||||
new Element("option").setAttribute("name", "REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL").setAttribute("value", "false"));
|
||||
}
|
||||
if (!REPORT_UNSOUND_WARNINGS) {
|
||||
node.addContent(new Element("option").setAttribute("name", "REPORT_UNSOUND_WARNINGS").setAttribute("value", "false"));
|
||||
@@ -98,9 +102,11 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
}
|
||||
List<DfaMemoryState> initialStates;
|
||||
PsiMethodCallExpression call = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(method);
|
||||
if (JavaPsiConstructorUtil.isChainedConstructorCall(call) || (call == null && DfaUtil.hasImplicitImpureSuperCall(aClass, method))) {
|
||||
if (JavaPsiConstructorUtil.isChainedConstructorCall(call) ||
|
||||
(call == null && DfaUtil.hasImplicitImpureSuperCall(aClass, method))) {
|
||||
initialStates = Collections.singletonList(runner.createMemoryState());
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
initialStates = ContainerUtil.map(states, DfaMemoryState::createCopy);
|
||||
}
|
||||
analyzeMethod(method, runner, initialStates);
|
||||
@@ -118,7 +124,11 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
PsiCodeBlock scope = method.getBody();
|
||||
if (scope == null) return;
|
||||
PsiClass containingClass = PsiTreeUtil.getParentOfType(method, PsiClass.class);
|
||||
if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass) && !(containingClass instanceof PsiEnumConstantInitializer)) return;
|
||||
if (containingClass != null &&
|
||||
PsiUtil.isLocalOrAnonymousClass(containingClass) &&
|
||||
!(containingClass instanceof PsiEnumConstantInitializer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
analyzeDfaWithNestedClosures(scope, holder, runner, initialStates);
|
||||
analyzeNullLiteralMethodArguments(method, holder);
|
||||
@@ -181,13 +191,16 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
return visitor;
|
||||
}
|
||||
|
||||
private static void reportAnalysisQualityProblem(ProblemsHolder holder, PsiElement scope, @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String problemKey) {
|
||||
private static void reportAnalysisQualityProblem(ProblemsHolder holder,
|
||||
PsiElement scope,
|
||||
@PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String problemKey) {
|
||||
PsiIdentifier name = null;
|
||||
String message = null;
|
||||
if(scope.getParent() instanceof PsiMethod) {
|
||||
if (scope.getParent() instanceof PsiMethod) {
|
||||
name = ((PsiMethod)scope.getParent()).getNameIdentifier();
|
||||
message = JavaAnalysisBundle.message(problemKey, "Method <code>#ref</code>");
|
||||
} else if(scope instanceof PsiClass) {
|
||||
}
|
||||
else if (scope instanceof PsiClass) {
|
||||
name = ((PsiClass)scope).getNameIdentifier();
|
||||
message = JavaAnalysisBundle.message(problemKey, "Class initializer");
|
||||
}
|
||||
@@ -210,7 +223,9 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
protected @NotNull List<@NotNull LocalQuickFix> createUnboxingNullableFixes(@NotNull PsiExpression qualifier, PsiElement anchor, boolean onTheFly) {
|
||||
protected @NotNull List<@NotNull LocalQuickFix> createUnboxingNullableFixes(@NotNull PsiExpression qualifier,
|
||||
PsiElement anchor,
|
||||
boolean onTheFly) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -292,7 +307,8 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
PsiCaseLabelElement label = entry.getKey();
|
||||
PsiSwitchLabelStatementBase labelStatement = Objects.requireNonNull(PsiImplUtil.getSwitchLabel(label));
|
||||
PsiSwitchBlock switchBlock = labelStatement.getEnclosingSwitchBlock();
|
||||
if (switchBlock == null || !canRemoveUnreachableBranches(labelStatement, label, switchBlock)) continue;
|
||||
if (switchBlock == null) continue;
|
||||
if (findRemovableUnreachableBranches(label, switchBlock).isEmpty()) continue;
|
||||
if (!canRemoveTheOnlyReachableLabel(label, switchBlock)) continue;
|
||||
if (!StreamEx.iterate(labelStatement, Objects::nonNull, l -> PsiTreeUtil.getPrevSiblingOfType(l, PsiSwitchLabelStatementBase.class))
|
||||
.skip(1).map(PsiSwitchLabelStatementBase::getCaseLabelElementList)
|
||||
@@ -355,7 +371,7 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
}
|
||||
if (labelStatement instanceof PsiSwitchLabelStatement) {
|
||||
PsiElement cur = labelStatement;
|
||||
while(true) {
|
||||
while (true) {
|
||||
PsiElement next = cur.getNextSibling();
|
||||
if (!(next instanceof PsiComment) && !(next instanceof PsiWhiteSpace) && !(next instanceof PsiSwitchLabelStatement)) {
|
||||
return next instanceof PsiThrowStatement;
|
||||
@@ -366,24 +382,74 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean canRemoveUnreachableBranches(PsiSwitchLabelStatementBase labelStatement,
|
||||
PsiCaseLabelElement label,
|
||||
PsiSwitchBlock statement) {
|
||||
PsiCaseLabelElementList labelElementList = Objects.requireNonNull(labelStatement.getCaseLabelElementList());
|
||||
if (labelElementList.getElementCount() != 1 &&
|
||||
!ContainerUtil.and(labelElementList.getElements(), element -> element == label || element instanceof PsiDefaultCaseLabelElement)) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Finds the removable unreachable branches in a switch statement.
|
||||
* Default case is not included in this list and should be checked separately
|
||||
*
|
||||
* @param reachableLabel The label that is reachable.
|
||||
* @param statement The switch statement to analyze.
|
||||
* @return A list of unreachable branches that can be safely removed.
|
||||
*/
|
||||
public static List<PsiCaseLabelElement> findRemovableUnreachableBranches(PsiCaseLabelElement reachableLabel,
|
||||
PsiSwitchBlock statement) {
|
||||
PsiSwitchLabelStatementBase labelStatement = PsiTreeUtil.getParentOfType(reachableLabel, PsiSwitchLabelStatementBase.class);
|
||||
List<PsiSwitchLabelStatementBase> allBranches =
|
||||
PsiTreeUtil.getChildrenOfTypeAsList(statement.getBody(), PsiSwitchLabelStatementBase.class);
|
||||
if (statement instanceof PsiSwitchStatement) {
|
||||
// Cannot do anything if we have already single branch, and we cannot restore flow due to non-terminal breaks
|
||||
return allBranches.size() != 1 || BreakConverter.from(statement) != null;
|
||||
boolean hasDefault = false;
|
||||
List<PsiCaseLabelElement> unreachableElements = new ArrayList<>();
|
||||
for (PsiSwitchLabelStatementBase branch : allBranches) {
|
||||
if (branch.isDefaultCase()) {
|
||||
hasDefault = true;
|
||||
continue;
|
||||
}
|
||||
PsiCaseLabelElementList elementList = branch.getCaseLabelElementList();
|
||||
if (elementList == null) {
|
||||
continue;
|
||||
}
|
||||
PsiCaseLabelElement[] elements = elementList.getElements();
|
||||
unreachableElements.addAll(Arrays.asList(elements));
|
||||
}
|
||||
// Expression switch: if we cannot unwrap existing branch and the other one is default case, we cannot kill it either
|
||||
return !ContainerUtil.and(allBranches, branch -> branch == labelStatement || SwitchUtils.hasOnlyDefaultCase(branch)) ||
|
||||
(labelStatement instanceof PsiSwitchLabeledRuleStatement &&
|
||||
((PsiSwitchLabeledRuleStatement)labelStatement).getBody() instanceof PsiExpressionStatement);
|
||||
unreachableElements.remove(reachableLabel);
|
||||
boolean canUnwrap = (statement instanceof PsiSwitchStatement && BreakConverter.from(statement) != null) ||
|
||||
(!(statement instanceof PsiSwitchStatement) &&
|
||||
labelStatement instanceof PsiSwitchLabeledRuleStatement ruleStatement &&
|
||||
ruleStatement.getBody() instanceof PsiExpressionStatement);
|
||||
if (canUnwrap) {
|
||||
return unreachableElements;
|
||||
}
|
||||
|
||||
if (unreachableElements.isEmpty() || hasDefault) {
|
||||
return unreachableElements;
|
||||
}
|
||||
boolean isEnhancedSwitch = JavaPsiSwitchUtil.isEnhancedSwitchStatement(statement) || statement instanceof PsiSwitchExpression;
|
||||
if (isEnhancedSwitch) {
|
||||
PsiExpression expression = statement.getExpression();
|
||||
if (expression == null) {
|
||||
return List.of();
|
||||
}
|
||||
PsiType selectorType = expression.getType();
|
||||
if (selectorType == null) {
|
||||
return List.of();
|
||||
}
|
||||
List<PsiCaseLabelElement> toDelete = new ArrayList<>();
|
||||
for (int i = 0; i < unreachableElements.size(); i++) {
|
||||
PsiCaseLabelElement currentElement = unreachableElements.get(i);
|
||||
boolean isDominated = false;
|
||||
for (int j = i + 1; j < unreachableElements.size(); j++) {
|
||||
PsiCaseLabelElement nextElement = unreachableElements.get(j);
|
||||
isDominated = PatternsInSwitchBlockHighlightingModel.isDominated(currentElement, nextElement, selectorType);
|
||||
if (!isDominated) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDominated) {
|
||||
toDelete.add(currentElement);
|
||||
}
|
||||
}
|
||||
unreachableElements.removeAll(toDelete);
|
||||
}
|
||||
return unreachableElements;
|
||||
}
|
||||
|
||||
private static boolean canRemoveTheOnlyReachableLabel(@NotNull PsiCaseLabelElement label, @NotNull PsiSwitchBlock switchBlock) {
|
||||
@@ -654,7 +720,8 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
PsiParameter parameter = parameters[0];
|
||||
if (!BaseIntentionAction.canModify(parameter) || !AnnotationUtil.isAnnotatingApplicable(parameter)) return;
|
||||
reporter.registerProblem(methodRef, problem.getMessage(IGNORE_ASSERT_STATEMENTS),
|
||||
LocalQuickFix.notNullElements(parameters.length == 1 ? AddAnnotationPsiFix.createAddNullableFix(parameter) : null));
|
||||
LocalQuickFix.notNullElements(
|
||||
parameters.length == 1 ? AddAnnotationPsiFix.createAddNullableFix(parameter) : null));
|
||||
}
|
||||
|
||||
private void reportNullableArgumentsPassedToNonAnnotated(ProblemReporter reporter,
|
||||
@@ -695,7 +762,8 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
private void reportCallMayProduceNpe(ProblemReporter reporter, @InspectionMessage String message, PsiMethodCallExpression callExpression,
|
||||
boolean alwaysNull) {
|
||||
PsiReferenceExpression methodExpression = callExpression.getMethodExpression();
|
||||
List<LocalQuickFix> fixes = createNPEFixes(methodExpression.getQualifierExpression(), callExpression, reporter.isOnTheFly(), alwaysNull);
|
||||
List<LocalQuickFix> fixes =
|
||||
createNPEFixes(methodExpression.getQualifierExpression(), callExpression, reporter.isOnTheFly(), alwaysNull);
|
||||
if (!alwaysNull) {
|
||||
ContainerUtil.addIfNotNull(fixes, ReplaceWithObjectsEqualsFix.createFix(callExpression, methodExpression));
|
||||
}
|
||||
@@ -750,7 +818,9 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
private static @Nullable PsiMethod getScopeMethod(PsiElement block) {
|
||||
PsiElement parent = block.getParent();
|
||||
if (parent instanceof PsiMethod) return (PsiMethod)parent;
|
||||
if (parent instanceof PsiLambdaExpression) return LambdaUtil.getFunctionalInterfaceMethod(((PsiLambdaExpression)parent).getFunctionalInterfaceType());
|
||||
if (parent instanceof PsiLambdaExpression) {
|
||||
return LambdaUtil.getFunctionalInterfaceMethod(((PsiLambdaExpression)parent).getFunctionalInterfaceType());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -774,12 +844,15 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
if (nullability != Nullability.NOT_NULL && (!SUGGEST_NULLABLE_ANNOTATIONS || block.getParent() instanceof PsiLambdaExpression)) return;
|
||||
|
||||
// no warnings in void lambdas, where the expression is not returned anyway
|
||||
if (block instanceof PsiExpression && block.getParent() instanceof PsiLambdaExpression && PsiTypes.voidType().equals(returnType)) return;
|
||||
if (block instanceof PsiExpression && block.getParent() instanceof PsiLambdaExpression && PsiTypes.voidType().equals(returnType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no warnings for Void methods, where only null can be possibly returned
|
||||
if (returnType == null || returnType.equalsToText(CommonClassNames.JAVA_LANG_VOID)) return;
|
||||
|
||||
for (NullabilityProblem<PsiExpression> problem : StreamEx.of(problems).map(NullabilityProblemKind.nullableReturn::asMyProblem).nonNull()) {
|
||||
for (NullabilityProblem<PsiExpression> problem : StreamEx.of(problems).map(NullabilityProblemKind.nullableReturn::asMyProblem)
|
||||
.nonNull()) {
|
||||
final PsiExpression anchor = problem.getAnchor();
|
||||
PsiExpression expr = problem.getDereferencedExpression();
|
||||
|
||||
@@ -790,7 +863,8 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
final String text = exactlyNull
|
||||
? JavaAnalysisBundle.message("dataflow.message.return.null.from.notnull", presentable)
|
||||
: JavaAnalysisBundle.message("dataflow.message.return.nullable.from.notnull", presentable);
|
||||
reporter.registerProblem(expr, text, createNPEFixes(expr, expr, reporter.isOnTheFly(), exactlyNull).toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
reporter.registerProblem(expr, text,
|
||||
createNPEFixes(expr, expr, reporter.isOnTheFly(), exactlyNull).toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
else if (AnnotationUtil.isAnnotatingApplicable(anchor)) {
|
||||
final String defaultNullable = manager.getDefaultNullable();
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
import com.intellij.codeInsight.BlockUtils;
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInspection.PsiUpdateModCommandQuickFix;
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowInspectionBase;
|
||||
import com.intellij.codeInspection.dataFlow.fix.DeleteSwitchLabelFix;
|
||||
import com.intellij.modcommand.ModPsiUpdater;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -44,9 +45,16 @@ public class UnwrapSwitchLabelFix extends PsiUpdateModCommandQuickFix {
|
||||
boolean shouldKeepDefault = block instanceof PsiSwitchExpression &&
|
||||
!(labelStatement instanceof PsiSwitchLabeledRuleStatement ruleStatement &&
|
||||
ruleStatement.getBody() instanceof PsiExpressionStatement);
|
||||
Set<PsiCaseLabelElement> removableUnreachableBranches = new HashSet<>(DataFlowInspectionBase.findRemovableUnreachableBranches(label, block));
|
||||
for (PsiSwitchLabelStatementBase otherLabel : labels) {
|
||||
if (otherLabel == labelStatement) continue;
|
||||
if (!shouldKeepDefault || !SwitchUtils.isDefaultLabel(otherLabel)) {
|
||||
boolean isDefault = SwitchUtils.isDefaultLabel(otherLabel);
|
||||
PsiCaseLabelElementList otherElementList = otherLabel.getCaseLabelElementList();
|
||||
if (otherElementList != null) {
|
||||
PsiCaseLabelElement[] otherElements = otherElementList.getElements();
|
||||
if(!removableUnreachableBranches.containsAll(Set.of(otherElements)) && !isDefault) continue;
|
||||
}
|
||||
if (!shouldKeepDefault || !isDefault) {
|
||||
DeleteSwitchLabelFix.deleteLabel(otherLabel);
|
||||
}
|
||||
else {
|
||||
@@ -54,7 +62,9 @@ public class UnwrapSwitchLabelFix extends PsiUpdateModCommandQuickFix {
|
||||
}
|
||||
}
|
||||
for (PsiCaseLabelElement labelElement : Objects.requireNonNull(labelStatement.getCaseLabelElementList()).getElements()) {
|
||||
if (labelElement != label && !(shouldKeepDefault && labelElement instanceof PsiDefaultCaseLabelElement)) {
|
||||
boolean isDefault = labelElement instanceof PsiDefaultCaseLabelElement;
|
||||
if(!removableUnreachableBranches.contains(labelElement) && !isDefault) continue;
|
||||
if (labelElement != label && !(shouldKeepDefault && isDefault)) {
|
||||
new CommentTracker().deleteAndRestoreComments(labelElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,14 @@ class Main {
|
||||
// 5
|
||||
// 6
|
||||
// 7
|
||||
case 42, default -> {
|
||||
case 42 -> {
|
||||
System.out.println("something");
|
||||
yield "Six by nine"; // 42
|
||||
}
|
||||
default -> {
|
||||
System.out.println("something");
|
||||
yield "many";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Remove unreachable branches" "false"
|
||||
|
||||
public class Switches {
|
||||
sealed interface I2{}
|
||||
enum En implements I2{A, B,}
|
||||
void foo(I2 x) {
|
||||
if (x == En.A) {
|
||||
System.out.println(switch (x) {
|
||||
case En.A<caret> -> {
|
||||
System.out.println("something");
|
||||
yield "1";
|
||||
}
|
||||
case En.B ->{
|
||||
yield "2";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// "Remove unreachable branches" "false"
|
||||
|
||||
public class Switches {
|
||||
sealed interface I2{}
|
||||
record A() implements I2{}
|
||||
record B() implements I2{}
|
||||
|
||||
void foo(I2 x) {
|
||||
if (x instanceof A) {
|
||||
System.out.println(switch (x) {
|
||||
case A <caret>a -> {
|
||||
System.out.println("something");
|
||||
yield "1";
|
||||
}
|
||||
case B b -> {
|
||||
System.out.println("something");
|
||||
yield "2";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// "Remove unreachable branches" "false"
|
||||
|
||||
public class Switches {
|
||||
sealed interface I2{}
|
||||
record A(I2 x) implements I2{}
|
||||
record B() implements I2{}
|
||||
|
||||
void foo(I2 x) {
|
||||
if (x instanceof B) {
|
||||
System.out.println(switch (x) {
|
||||
case A(A a) -> {
|
||||
System.out.println("something");
|
||||
yield "1";
|
||||
}
|
||||
case A(B a) -> {
|
||||
System.out.println("something");
|
||||
yield "1";
|
||||
}
|
||||
case B <caret>b -> {
|
||||
System.out.println("something");
|
||||
yield "1";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,14 @@ class Main {
|
||||
case 5 -> "five"; // 5
|
||||
case 6 -> "six"; // 6
|
||||
case 7 -> "seven"; // 7
|
||||
case 42<caret>, default -> {
|
||||
case 42<caret> -> {
|
||||
System.out.println("something");
|
||||
yield "Six by nine"; // 42
|
||||
}
|
||||
default -> {
|
||||
System.out.println("something");
|
||||
yield "many";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof Rect)) return;
|
||||
System.out.println(42);
|
||||
}
|
||||
|
||||
record Point(double x, double y) {}
|
||||
record Rect(@NotNull Point point1, @NotNull Point point2) {}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof Rect)) return;
|
||||
System.out.println(42);
|
||||
}
|
||||
|
||||
record Point(double x, double y) {}
|
||||
record Rect(@NotNull Point point1, @NotNull Point point2) {}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
class Test {
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case X x -> { }
|
||||
default -> { return; }
|
||||
}
|
||||
|
||||
X x1 = (X) obj;
|
||||
System.out.println(x1.x().x().x().x());
|
||||
}
|
||||
}
|
||||
|
||||
record X(@NotNull X x) { }
|
||||
@@ -1,18 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
class Test {
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case X x -> { }
|
||||
default -> { return; }
|
||||
}
|
||||
|
||||
X x1 = (X) obj;
|
||||
System.out.println(x1);
|
||||
System.out.println(x1.x().x());
|
||||
System.out.println(x1.x().x().x().x());
|
||||
}
|
||||
}
|
||||
|
||||
record X(@NotNull X x) { }
|
||||
@@ -1,8 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(EmptyBox box) {
|
||||
System.out.println("Fill it up and send it back");
|
||||
}
|
||||
|
||||
record EmptyBox() {}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(EmptyBox box) {
|
||||
System.out.println(box);
|
||||
}
|
||||
|
||||
record EmptyBox() {}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(EmptyBox box) {
|
||||
System.out.println("Fill it up and send it back");
|
||||
}
|
||||
|
||||
record EmptyBox() {}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void foo(Rec rec) {
|
||||
switch (rec) {
|
||||
case Rec(A a) -> a.doA();
|
||||
default -> { return; }
|
||||
}
|
||||
|
||||
((A) rec.i()).doA();
|
||||
}
|
||||
}
|
||||
|
||||
record Rec(I i) {}
|
||||
|
||||
interface I {}
|
||||
|
||||
class A implements I {
|
||||
void doA() {}
|
||||
}
|
||||
|
||||
class B implements I {
|
||||
void doB() {}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(R r) {
|
||||
System.out.println(r.s() + r.i());
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof R)) return;
|
||||
R rec = (R) obj;
|
||||
System.out.println(rec.s() + rec.i());
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof R)) return;
|
||||
R rec = (R) obj;
|
||||
rec = new R(42, "hello");
|
||||
System.out.println(rec.s() + rec.i());
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(R r) {
|
||||
R rec = r;
|
||||
rec = new R(42, "hello");
|
||||
System.out.println(rec.s() + rec.i());
|
||||
System.out.println(rec);
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(R r) {
|
||||
System.out.println(r.s() + r.i());
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof R)) return;
|
||||
R rec = (R) obj;
|
||||
System.out.println(rec.s() + rec.i());
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(R r) {
|
||||
System.out.println(r.s());
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof R)) return;
|
||||
R r = (R) obj;
|
||||
System.out.println(r.i() + r.s());
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof Rect)) return;
|
||||
switch (obj) {
|
||||
case Rect(Point(double x1, double y1), Point(double x2, double y2)) rec<caret>:
|
||||
System.out.println(42);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record Point(double x, double y) {}
|
||||
record Rect(@NotNull Point point1, @NotNull Point point2) {}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof Rect)) return;
|
||||
switch (obj) {
|
||||
case Rect(Point(double x1, double y1) point1, Point(double x2, double y2))<caret>:
|
||||
System.out.println(42);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record Point(double x, double y) {}
|
||||
record Rect(@NotNull Point point1, @NotNull Point point2) {}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
class Test {
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case X x -> { }
|
||||
default -> { return; }
|
||||
}
|
||||
|
||||
switch (obj) {
|
||||
case X(X(X(X(X x)<caret>))) -> System.out.println(x);
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record X(@NotNull X x) { }
|
||||
@@ -1,22 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
class Test {
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case X x -> { }
|
||||
default -> { return; }
|
||||
}
|
||||
|
||||
switch (obj) {
|
||||
case X<caret>(X(X(X(X x5)) x3)) x1 -> {
|
||||
System.out.println(x1);
|
||||
System.out.println(x3);
|
||||
System.out.println(x5);
|
||||
}
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record X(@NotNull X x) { }
|
||||
@@ -1,12 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(EmptyBox box) {
|
||||
switch (box) {
|
||||
case Empty<caret>Box() when true -> {
|
||||
System.out.println("Fill it up and send it back");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record EmptyBox() {}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(EmptyBox box) {
|
||||
switch (box) {
|
||||
case Empt<caret>yBox() emptyBox when true -> {
|
||||
System.out.println(emptyBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record EmptyBox() {}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(EmptyBox box) {
|
||||
switch (box) {
|
||||
case Empty<caret>Box() emptyBox when true -> {
|
||||
System.out.println("Fill it up and send it back");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record EmptyBox() {}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void foo(Rec rec) {
|
||||
switch (rec) {
|
||||
case Rec(A a) -> a.doA();
|
||||
default -> { return; }
|
||||
}
|
||||
|
||||
switch (rec) {
|
||||
case R<caret>ec(A a) when true -> a.doA();
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record Rec(I i) {}
|
||||
|
||||
interface I {}
|
||||
|
||||
class A implements I {
|
||||
void doA() {}
|
||||
}
|
||||
|
||||
class B implements I {
|
||||
void doB() {}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(R r) {
|
||||
switch (r) {
|
||||
case R(int i, String s)<caret> rec when true:
|
||||
System.out.println(s + i);
|
||||
System.out.println(rec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof R)) return;
|
||||
switch (obj) {
|
||||
case R(int i, String s)<caret> rec when true:
|
||||
System.out.println(s + i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof R)) return;
|
||||
switch (obj) {
|
||||
case R(int i, String s)<caret> rec when true:
|
||||
rec = new R(42, "hello");
|
||||
System.out.println(s + i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(R r) {
|
||||
switch (r) {
|
||||
case R(int i, String s)<caret> rec when true:
|
||||
rec = new R(42, "hello");
|
||||
System.out.println(s + i);
|
||||
System.out.println(rec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(R r) {
|
||||
switch (r) {
|
||||
case R(int i, String s)<caret> rec when true:
|
||||
System.out.println(s + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof R)) return;
|
||||
switch (obj) {
|
||||
case R(int i, String s)<caret> rec when true:
|
||||
System.out.println(s + i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(R r) {
|
||||
switch (r) {
|
||||
case R(int i, String s)<caret> when true:
|
||||
System.out.println(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// "Remove unreachable branches" "true"
|
||||
class Test {
|
||||
void test(Object obj) {
|
||||
if (!(obj instanceof R)) return;
|
||||
switch (obj) {
|
||||
case R(int i, String s)<caret> when true:
|
||||
System.out.println(i + s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record R(int i, String s) {}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2000-2022 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.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DeleteSwitchLabelFix19Test extends LightQuickFixParameterizedTestCase {
|
||||
@Override
|
||||
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new DataFlowInspection()};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return LightJavaCodeInsightFixtureTestCase.JAVA_19;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel19";
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -14,6 +15,11 @@ public class DeleteSwitchLabelFixTest extends LightQuickFixParameterizedTestCase
|
||||
return new LocalInspectionTool[]{new DataFlowInspection()};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LanguageLevel getLanguageLevel() {
|
||||
return LanguageLevel.JDK_21;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
|
||||
Reference in New Issue
Block a user