mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-inspections] DuplicateBranchesInSwitch: adapt the inspection to Java 20
IDEA-310260 GitOrigin-RevId: ce07977550e3a5c15d63f0a6eeed1d7341c74164
This commit is contained in:
committed by
intellij-monorepo-bot
parent
13b5f3d866
commit
e008c6522f
+2
-24
@@ -837,7 +837,7 @@ public class SwitchBlockHighlightingModel {
|
||||
addIllegalFallThroughError(currentElement, "invalid.case.label.combination", holder, alreadyFallThroughElements);
|
||||
break;
|
||||
}
|
||||
if (containsPatternVariable(currentElement)) {
|
||||
if (JavaPsiPatternUtil.containsPatternVariable(currentElement)) {
|
||||
if (existPattern || PsiTreeUtil.skipWhitespacesAndCommentsForward(switchLabelElement) instanceof PsiSwitchLabelStatement) {
|
||||
addIllegalFallThroughError(currentElement, "switch.illegal.fall.through.from", holder, alreadyFallThroughElements);
|
||||
break;
|
||||
@@ -884,7 +884,7 @@ public class SwitchBlockHighlightingModel {
|
||||
PsiCaseLabelElementList labelElementList = switchLabel.getCaseLabelElementList();
|
||||
if (labelElementList == null) continue;
|
||||
List<PsiCaseLabelElement> patternElements = ContainerUtil.filter(labelElementList.getElements(),
|
||||
labelElement -> containsPatternVariable(labelElement));
|
||||
labelElement -> JavaPsiPatternUtil.containsPatternVariable(labelElement));
|
||||
if (patternElements.isEmpty()) continue;
|
||||
PsiStatement prevStatement = PsiTreeUtil.getPrevSiblingOfType(firstSwitchLabelInGroup, PsiStatement.class);
|
||||
if (prevStatement == null) continue;
|
||||
@@ -896,28 +896,6 @@ public class SwitchBlockHighlightingModel {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean containsPatternVariable(@NotNull PsiCaseLabelElement element) {
|
||||
if (element instanceof PsiPatternGuard patternGuard) {
|
||||
return containsPatternVariable(patternGuard.getPattern());
|
||||
}
|
||||
else if (element instanceof PsiGuardedPattern guardedPattern) {
|
||||
return containsPatternVariable(guardedPattern.getPrimaryPattern());
|
||||
}
|
||||
else if (element instanceof PsiTypeTestPattern typeTestPattern) {
|
||||
return typeTestPattern.getPatternVariable() != null;
|
||||
}
|
||||
else if (element instanceof PsiParenthesizedPattern parenthesizedPattern) {
|
||||
PsiPattern pattern = parenthesizedPattern.getPattern();
|
||||
return pattern != null && containsPatternVariable(pattern);
|
||||
}
|
||||
else if (element instanceof PsiDeconstructionPattern deconstructionPattern) {
|
||||
return deconstructionPattern.getPatternVariable() != null ||
|
||||
ContainerUtil.exists(deconstructionPattern.getDeconstructionList().getDeconstructionComponents(),
|
||||
pattern -> containsPatternVariable(pattern));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 14.11.1 Switch Blocks
|
||||
* To ensure the absence of unreachable statements, domination rules provide a possible order
|
||||
|
||||
+223
-96
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInspection.util.InspectionMessage;
|
||||
@@ -6,10 +6,12 @@ import com.intellij.java.JavaBundle;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.JavaPsiPatternUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
@@ -95,7 +97,7 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
for (int index = 0; index < size; index++) {
|
||||
if (index != defaultIndex) {
|
||||
BranchBase<?> branch = branches.get(index);
|
||||
if (areDuplicates(defaultBranch, branch) && branch.myBranchType != BranchType.HAS_NULL) {
|
||||
if (areDuplicates(defaultBranch, branch)) {
|
||||
isDuplicate[index] = isDuplicate[defaultIndex] = true;
|
||||
highlightDefaultDuplicate(branch);
|
||||
}
|
||||
@@ -115,8 +117,9 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
|
||||
if (areDuplicates(branch, otherBranch)) {
|
||||
isDuplicate[otherIndex] = true;
|
||||
if (canMerge(otherBranch, branch)) {
|
||||
registerProblem(otherBranch, otherBranch.getCaseBranchMessage(), branch.newMergeCasesFix());
|
||||
LocalQuickFix fix = branch.newMergeCasesFix(otherBranch);
|
||||
if (fix != null) {
|
||||
registerProblem(otherBranch, otherBranch.getCaseBranchMessage(), fix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,21 +127,16 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canMerge(@NotNull BranchBase<?> duplicate, @NotNull BranchBase<?> original) {
|
||||
if (duplicate.myBranchType == BranchType.UNMERGEABLE || original.myBranchType == BranchType.UNMERGEABLE) {
|
||||
return false;
|
||||
}
|
||||
if (duplicate.myBranchType == BranchType.TYPE_TEST_PATTERN ^ original.myBranchType == BranchType.TYPE_TEST_PATTERN) {
|
||||
return true;
|
||||
}
|
||||
return duplicate.myBranchType != BranchType.TYPE_TEST_PATTERN;
|
||||
private void highlightDefaultDuplicate(@NotNull BranchBase<?> branch) {
|
||||
LocalQuickFix deleteCaseFix = branch.newDeleteCaseFix();
|
||||
LocalQuickFix mergeWithDefaultFix = branch.newMergeWithDefaultFix();
|
||||
if (deleteCaseFix == null && mergeWithDefaultFix == null) return;
|
||||
registerProblem(branch, branch.getDefaultBranchMessage(), deleteCaseFix, mergeWithDefaultFix);
|
||||
}
|
||||
|
||||
private void highlightDefaultDuplicate(@NotNull BranchBase branch) {
|
||||
registerProblem(branch, branch.getDefaultBranchMessage(), branch.newDeleteCaseFix(), branch.newMergeWithDefaultFix());
|
||||
}
|
||||
|
||||
private void registerProblem(@NotNull BranchBase duplicate, @NotNull @InspectionMessage String message, LocalQuickFix @NotNull ... fixes) {
|
||||
private void registerProblem(@NotNull BranchBase<?> duplicate,
|
||||
@NotNull @InspectionMessage String message,
|
||||
LocalQuickFix @NotNull ... fixes) {
|
||||
ProblemDescriptor descriptor = InspectionManager.getInstance(myHolder.getProject())
|
||||
.createProblemDescriptor(duplicate.myStatements[0], duplicate.myStatements[duplicate.myStatements.length - 1],
|
||||
message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
@@ -252,7 +250,7 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
return branch;
|
||||
}
|
||||
|
||||
static boolean areDuplicates(@NotNull BranchBase branch, @NotNull BranchBase otherBranch) {
|
||||
static boolean areDuplicates(@NotNull BranchBase<?> branch, @NotNull BranchBase<?> otherBranch) {
|
||||
if (branch.isSimpleExit() != otherBranch.isSimpleExit() ||
|
||||
branch.canFallThrough() != otherBranch.canFallThrough() ||
|
||||
branch.effectiveLength() != otherBranch.effectiveLength()) {
|
||||
@@ -346,6 +344,21 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to merge 'case null -> ...' with 'default -> ...' to get as a result 'case null, default -> ...'
|
||||
*/
|
||||
private static class MergeWithDefaultRuleFix extends MergeWithDefaultBranchFix {
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
RuleFixContext context = new RuleFixContext();
|
||||
if (context.prepare(descriptor.getStartElement(), BranchBase::isDefault)) {
|
||||
context.copyCaseValues(true);
|
||||
context.deleteRedundantComments();
|
||||
context.deleteRule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DeleteRedundantBranchFix implements LocalQuickFix {
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@@ -365,9 +378,19 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
BranchFixContext context = new BranchFixContext();
|
||||
if (context.prepare(descriptor.getStartElement(), Branch::isDefault)) {
|
||||
if (context.prepare(descriptor.getStartElement(), BranchBase::isDefault)) {
|
||||
context.deleteBranchLabel();
|
||||
context.deleteStatements();
|
||||
if (!context.myBranchToDelete.hasSingleNullCase()) {
|
||||
// case R():
|
||||
// case null:
|
||||
// case S():
|
||||
// return 42;
|
||||
//
|
||||
// The 'default' case does not handle null values, so we cannot delete
|
||||
// the 'case null:' and the 'return 42;' statement. However,
|
||||
// we can delete the 'case R():' and 'case S():' statements."
|
||||
context.deleteStatements();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -423,23 +446,34 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
PsiElement prevElement = PsiTreeUtil.skipWhitespacesAndCommentsBackward(myLabelToMergeWith);
|
||||
if (prevElement != null) moveTarget = prevElement;
|
||||
}
|
||||
if (PsiUtil.isLanguageLevel14OrHigher(moveTarget) && moveTarget instanceof PsiSwitchLabelStatement) {
|
||||
final PsiSwitchLabelStatement labelStatement = (PsiSwitchLabelStatement)moveTarget;
|
||||
final PsiCaseLabelElementList caseLabelElementList = labelStatement.getCaseLabelElementList();
|
||||
assert caseLabelElementList != null;
|
||||
if (PsiUtil.isLanguageLevel14OrHigher(moveTarget) && moveTarget instanceof PsiSwitchLabelStatement labelStatement &&
|
||||
myBranchToDelete.canCopyCaseValues() && !SwitchUtils.isCaseNull(labelStatement)) {
|
||||
for (PsiElement element : myBranchPrefixToMove) {
|
||||
if (element instanceof PsiSwitchLabelStatement) {
|
||||
final PsiSwitchLabelStatement statement = (PsiSwitchLabelStatement)element;
|
||||
final PsiCaseLabelElementList list1 = RuleFixContext.getCaseLabelElementList(statement);
|
||||
assert list1 != null;
|
||||
for (PsiCaseLabelElement labelElement : list1.getElements()) {
|
||||
caseLabelElementList.addAfter(labelElement, caseLabelElementList.getLastChild());
|
||||
if (element instanceof PsiSwitchLabelStatement statement) {
|
||||
if (SwitchUtils.isCaseNull(statement) && myLabelToMergeWith.isDefaultCase()) {
|
||||
copyCaseValues(statement, myLabelToMergeWith, true);
|
||||
}
|
||||
else {
|
||||
copyCaseValues(statement, labelStatement, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
moveTarget.getParent().addRangeAfter(firstElementToMove, lastElementToMove, moveTarget);
|
||||
if (myBranchToMergeWith.isDefault()) {
|
||||
for (PsiElement current = lastElementToMove; current != firstElementToMove; current = current.getPrevSibling()) {
|
||||
if (current instanceof PsiWhiteSpace) continue;
|
||||
if (current instanceof PsiSwitchLabelStatement labelStatement && SwitchUtils.isCaseNull(labelStatement)) {
|
||||
copyCaseValues(labelStatement, myLabelToMergeWith, true);
|
||||
}
|
||||
else {
|
||||
moveTarget.getParent().addAfter(current, moveTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
moveTarget.getParent().addRangeAfter(firstElementToMove, lastElementToMove, moveTarget);
|
||||
}
|
||||
}
|
||||
firstElementToMove.getParent().deleteChildRange(firstElementToMove, lastElementToMove);
|
||||
}
|
||||
@@ -495,18 +529,51 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
int size = toDelete.size();
|
||||
if (size != 0) {
|
||||
for (int i = 0; i < size - 1; i++) {
|
||||
tracker.delete(toDelete.get(i));
|
||||
deleteKeepingCaseNull(toDelete.get(i), tracker, false);
|
||||
}
|
||||
tracker.deleteAndRestoreComments(toDelete.get(size - 1));
|
||||
deleteKeepingCaseNull(toDelete.get(size - 1), tracker, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum BranchType {
|
||||
UNMERGEABLE,
|
||||
TYPE_TEST_PATTERN,
|
||||
HAS_NULL,
|
||||
CONSTANT
|
||||
/**
|
||||
* Deletes the given PsiElement if it's not 'case null:'. The 'default' label does not handle null values,
|
||||
* so 'case null' is not redundant and cannot be removed.
|
||||
* <p>
|
||||
* We cannot get labels like 'case String s, null' or 'case "hello", null' here.
|
||||
*
|
||||
* @param element element to delete
|
||||
* @param ct CommentTracker to use
|
||||
* @param needRestoreComments if {@code true} {@link CommentTracker#deleteAndRestoreComments(PsiElement)} is called,
|
||||
* {@link CommentTracker#delete(PsiElement)} otherwise
|
||||
*/
|
||||
private static void deleteKeepingCaseNull(@NotNull PsiElement element, @NotNull CommentTracker ct, boolean needRestoreComments) {
|
||||
if (!(element instanceof PsiSwitchLabelStatement labelStatement) || !SwitchUtils.isCaseNull(labelStatement)) {
|
||||
if (needRestoreComments) {
|
||||
ct.delete(element);
|
||||
}
|
||||
else {
|
||||
new CommentTracker().deleteAndRestoreComments(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable PsiCaseLabelElementList getCaseLabelElementList(@NotNull PsiSwitchLabelStatement label) {
|
||||
if (label.isDefaultCase()) {
|
||||
PsiElementFactory factory = PsiElementFactory.getInstance(label.getProject());
|
||||
PsiSwitchLabelStatement labelStatement = (PsiSwitchLabelStatement)factory.createStatementFromText("case default:", null);
|
||||
PsiSwitchLabelStatement newLabelStatement = (PsiSwitchLabelStatement)label.replace(labelStatement);
|
||||
return newLabelStatement.getCaseLabelElementList();
|
||||
}
|
||||
return label.getCaseLabelElementList();
|
||||
}
|
||||
|
||||
private static void copyCaseValues(@NotNull PsiSwitchLabelStatement from,
|
||||
@NotNull PsiSwitchLabelStatement to,
|
||||
boolean mergeWithDefault) {
|
||||
@Nullable PsiCaseLabelElementList fromCaseLabelElementList = getCaseLabelElementList(from);
|
||||
@Nullable PsiCaseLabelElementList toCaseLabelElementList = getCaseLabelElementList(to);
|
||||
DuplicateBranchesInSwitchInspection.copyCaseValues(fromCaseLabelElementList, toCaseLabelElementList, mergeWithDefault);
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class BranchBase<T extends PsiSwitchLabelStatementBase> {
|
||||
@@ -514,8 +581,8 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
protected final PsiStatement @NotNull [] myStatements;
|
||||
protected final String @NotNull [] myCommentTexts;
|
||||
private final boolean myIsDefault;
|
||||
private final @NotNull BranchType myBranchType;
|
||||
private DuplicatesFinder myFinder;
|
||||
protected final boolean myCanDeleteRedundantBranch;
|
||||
|
||||
BranchBase(T @NotNull [] labels, PsiStatement @NotNull [] statements, String @NotNull [] commentTexts) {
|
||||
LOG.assertTrue(labels.length != 0, "labels.length");
|
||||
@@ -524,37 +591,14 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
myLabel = labels[0];
|
||||
myStatements = statements;
|
||||
myCommentTexts = commentTexts;
|
||||
myIsDefault = ContainerUtil.exists(labels, SwitchUtils::hasOnlyDefaultCase);
|
||||
myBranchType = getBranchType(labels);
|
||||
}
|
||||
|
||||
BranchType getBranchType(PsiSwitchLabelStatementBase @NotNull [] labels) {
|
||||
boolean hasTypeTestPattern = false;
|
||||
boolean hasNull = false;
|
||||
for (PsiSwitchLabelStatementBase label : labels) {
|
||||
PsiCaseLabelElementList labelElementList = label.getCaseLabelElementList();
|
||||
if (labelElementList == null) continue;
|
||||
PsiCaseLabelElement[] elements = labelElementList.getElements();
|
||||
for (PsiCaseLabelElement element : elements) {
|
||||
if (PsiTreeUtil.instanceOf(element, PsiGuardedPattern.class, PsiPatternGuard.class, PsiParenthesizedPattern.class,
|
||||
PsiDeconstructionPattern.class)) {
|
||||
return BranchType.UNMERGEABLE;
|
||||
}
|
||||
else if (element instanceof PsiTypeTestPattern) {
|
||||
hasTypeTestPattern = true;
|
||||
}
|
||||
else if (ExpressionUtils.isNullLiteral(ObjectUtils.tryCast(element, PsiExpression.class))) {
|
||||
hasNull = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasTypeTestPattern) {
|
||||
return BranchType.TYPE_TEST_PATTERN;
|
||||
}
|
||||
else if (hasNull) {
|
||||
return BranchType.HAS_NULL;
|
||||
}
|
||||
return BranchType.CONSTANT;
|
||||
myIsDefault = ContainerUtil.exists(labels, label -> label.isDefaultCase() || SwitchUtils.isCaseNullDefault(label));
|
||||
myCanDeleteRedundantBranch = labels.length > 1 ||
|
||||
!ContainerUtil.exists(labels, label -> {
|
||||
PsiCaseLabelElementList list = label.getCaseLabelElementList();
|
||||
if (list == null) return false;
|
||||
return ContainerUtil.exists(list.getElements(), element -> element instanceof PsiExpression expr &&
|
||||
ExpressionUtils.isNullLiteral(expr));
|
||||
});
|
||||
}
|
||||
|
||||
boolean isDefault() {
|
||||
@@ -570,6 +614,8 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
|
||||
abstract boolean canFallThrough();
|
||||
|
||||
abstract boolean canMergeBranch();
|
||||
|
||||
int effectiveLength() {
|
||||
if (myStatements.length == 1 && myStatements[0] instanceof PsiBlockStatement) {
|
||||
return ((PsiBlockStatement)myStatements[0]).getCodeBlock().getStatementCount();
|
||||
@@ -582,10 +628,12 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
}
|
||||
|
||||
@Nullable
|
||||
abstract LocalQuickFix newMergeCasesFix();
|
||||
abstract LocalQuickFix newMergeCasesFix(BranchBase<?> otherBranch);
|
||||
|
||||
@Nullable
|
||||
abstract LocalQuickFix newDeleteCaseFix();
|
||||
|
||||
@Nullable
|
||||
abstract LocalQuickFix newMergeWithDefaultFix();
|
||||
|
||||
@Nullable
|
||||
@@ -714,14 +762,23 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
private static final PsiSwitchLabelStatement[] EMPTY_LABELS_ARRAY = new PsiSwitchLabelStatement[0];
|
||||
private final boolean myIsSimpleExit;
|
||||
private final boolean myCanFallThrough;
|
||||
private final boolean myCanMergeBranch;
|
||||
private final boolean myHasSingleNullCase;
|
||||
private final boolean myCanCopyCaseValues;
|
||||
|
||||
Branch(PsiSwitchLabelStatement @NotNull [] labels, @NotNull List<PsiStatement> statementList, boolean hasImplicitBreak, String @NotNull [] comments) {
|
||||
Branch(PsiSwitchLabelStatement @NotNull [] labels,
|
||||
@NotNull List<PsiStatement> statementList,
|
||||
boolean hasImplicitBreak,
|
||||
String @NotNull [] comments) {
|
||||
super(labels, statementsWithoutTrailingBreak(statementList), comments);
|
||||
|
||||
int lastIndex = statementList.size() - 1;
|
||||
PsiStatement lastStatement = statementList.get(lastIndex);
|
||||
myCanFallThrough = !hasImplicitBreak && ControlFlowUtils.statementMayCompleteNormally(lastStatement);
|
||||
myIsSimpleExit = lastIndex == 0 && isSimpleExit(lastStatement);
|
||||
myCanMergeBranch = calculateCanMergeBranches(labels);
|
||||
myHasSingleNullCase = ContainerUtil.exists(labels, SwitchUtils::isCaseNull);
|
||||
myCanCopyCaseValues = ContainerUtil.all(labels, label -> Rule.calculateCanMergeBranches(label));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -729,6 +786,35 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
return myCanFallThrough;
|
||||
}
|
||||
|
||||
private static boolean calculateCanMergeBranches(PsiSwitchLabelStatement @NotNull [] labels) {
|
||||
boolean java20plus = PsiUtil.getLanguageLevel(labels[0]).isAtLeast(LanguageLevel.JDK_20_PREVIEW);
|
||||
for (PsiSwitchLabelStatement label : labels) {
|
||||
PsiCaseLabelElementList labelElementList = label.getCaseLabelElementList();
|
||||
if (labelElementList == null) continue;
|
||||
PsiCaseLabelElement[] elements = labelElementList.getElements();
|
||||
for (PsiCaseLabelElement element : elements) {
|
||||
if (PsiTreeUtil.instanceOf(element, PsiGuardedPattern.class, PsiPatternGuard.class) ||
|
||||
element instanceof PsiPattern && (!java20plus || JavaPsiPatternUtil.containsPatternVariable(element))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean canMergeBranch() {
|
||||
return myCanMergeBranch;
|
||||
}
|
||||
|
||||
boolean canCopyCaseValues() {
|
||||
return myCanCopyCaseValues;
|
||||
}
|
||||
|
||||
boolean hasSingleNullCase() {
|
||||
return myHasSingleNullCase;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSimpleExit() {
|
||||
return myIsSimpleExit;
|
||||
@@ -736,19 +822,23 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
LocalQuickFix newMergeCasesFix() {
|
||||
LocalQuickFix newMergeCasesFix(BranchBase<?> otherBranch) {
|
||||
if (!otherBranch.canMergeBranch()) return null;
|
||||
String switchLabelText = getSwitchLabelText();
|
||||
return switchLabelText != null ? new MergeBranchesFix(switchLabelText) : null;
|
||||
if (switchLabelText == null) return null;
|
||||
return myCanMergeBranch ? new MergeBranchesFix(switchLabelText) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
LocalQuickFix newMergeWithDefaultFix() {
|
||||
return new MergeWithDefaultBranchFix();
|
||||
return myCanMergeBranch ? new MergeWithDefaultBranchFix() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
LocalQuickFix newDeleteCaseFix() {
|
||||
return new DeleteRedundantBranchFix();
|
||||
return myCanDeleteRedundantBranch ? new DeleteRedundantBranchFix() : null;
|
||||
}
|
||||
|
||||
// 'switch' labels with comments and spaces
|
||||
@@ -855,10 +945,28 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
|
||||
private static class Rule extends BranchBase<PsiSwitchLabeledRuleStatement> {
|
||||
private final boolean myIsSimpleExit;
|
||||
private final boolean myCanMergeBranches;
|
||||
private final boolean myCanMergeWithDefaultBranch;
|
||||
|
||||
Rule(@NotNull PsiSwitchLabeledRuleStatement rule, @NotNull PsiStatement body, String @NotNull [] commentTexts) {
|
||||
super(new PsiSwitchLabeledRuleStatement[]{rule}, new PsiStatement[]{body}, commentTexts);
|
||||
myIsSimpleExit = body instanceof PsiExpressionStatement || body instanceof PsiThrowStatement;
|
||||
myCanMergeBranches = calculateCanMergeBranches(rule);
|
||||
myCanMergeWithDefaultBranch = SwitchUtils.isCaseNull(rule);
|
||||
}
|
||||
|
||||
public static boolean calculateCanMergeBranches(@NotNull PsiSwitchLabelStatementBase rule) {
|
||||
PsiCaseLabelElementList labelElementList = rule.getCaseLabelElementList();
|
||||
if (labelElementList == null) return false;
|
||||
PsiCaseLabelElement[] elements = labelElementList.getElements();
|
||||
return !ContainerUtil.exists(elements, element -> element instanceof PsiPattern ||
|
||||
element instanceof PsiPatternGuard ||
|
||||
element instanceof PsiExpression expr && ExpressionUtils.isNullLiteral(expr));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean canMergeBranch() {
|
||||
return myCanMergeBranches;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -873,19 +981,23 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
LocalQuickFix newMergeCasesFix() {
|
||||
LocalQuickFix newMergeCasesFix(BranchBase<?> otherBranch) {
|
||||
if (!otherBranch.canMergeBranch()) return null;
|
||||
String switchLabelText = getSwitchLabelText();
|
||||
return switchLabelText != null ? new MergeRulesFix(switchLabelText) : null;
|
||||
if (switchLabelText == null) return null;
|
||||
return myCanMergeBranches ? new MergeRulesFix(switchLabelText) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
LocalQuickFix newMergeWithDefaultFix() {
|
||||
return null;
|
||||
return myCanMergeWithDefaultBranch ? new MergeWithDefaultRuleFix() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
LocalQuickFix newDeleteCaseFix() {
|
||||
return new DeleteRedundantRuleFix();
|
||||
return myCanDeleteRedundantBranch ? new DeleteRedundantRuleFix() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -914,7 +1026,7 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
RuleFixContext context = new RuleFixContext();
|
||||
if (context.prepare(descriptor.getStartElement(), rule -> mySwitchLabelText.equals(rule.getSwitchLabelText()))) {
|
||||
context.copyCaseValues();
|
||||
context.copyCaseValues(false);
|
||||
context.deleteRedundantComments();
|
||||
context.deleteRule();
|
||||
}
|
||||
@@ -1023,24 +1135,25 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
redundantComments.forEach(PsiElement::delete);
|
||||
}
|
||||
|
||||
private static @Nullable PsiCaseLabelElementList getCaseLabelElementList(@NotNull PsiSwitchLabelStatementBase label) {
|
||||
if (label.isDefaultCase()) {
|
||||
PsiElementFactory factory = PsiElementFactory.getInstance(label.getProject());
|
||||
PsiSwitchLabelStatement labelStatement = (PsiSwitchLabelStatement)factory.createStatementFromText("case default", null);
|
||||
PsiSwitchLabelStatement newLabelStatement = (PsiSwitchLabelStatement)label.getFirstChild().replace(labelStatement);
|
||||
return newLabelStatement.getCaseLabelElementList();
|
||||
@Nullable PsiCaseLabelElementList getCaseLabelElementList(@NotNull PsiSwitchLabeledRuleStatement ruleStatement) {
|
||||
if (ruleStatement.isDefaultCase()) {
|
||||
PsiElementFactory factory = PsiElementFactory.getInstance(ruleStatement.getProject());
|
||||
PsiSwitchLabeledRuleStatement labelStatement =
|
||||
(PsiSwitchLabeledRuleStatement)factory.createStatementFromText("case default->{}", null);
|
||||
PsiStatement body = ruleStatement.getBody();
|
||||
if (body != null) {
|
||||
Objects.requireNonNull(labelStatement.getBody()).replace(body);
|
||||
}
|
||||
PsiSwitchLabeledRuleStatement newLabelStatement = (PsiSwitchLabeledRuleStatement)ruleStatement.replace(labelStatement);
|
||||
return newLabelStatement.getCaseLabelElementList();
|
||||
}
|
||||
return ruleStatement.getCaseLabelElementList();
|
||||
}
|
||||
return label.getCaseLabelElementList();
|
||||
}
|
||||
|
||||
void copyCaseValues() {
|
||||
void copyCaseValues(boolean mergeWithDefault) {
|
||||
@Nullable PsiCaseLabelElementList caseValuesToMergeWith = getCaseLabelElementList(myRuleToMergeWith.myLabel);
|
||||
@Nullable PsiCaseLabelElementList caseValuesToDelete = getCaseLabelElementList(myRuleToDelete.myLabel);
|
||||
if (caseValuesToDelete != null && caseValuesToMergeWith != null) {
|
||||
for (PsiCaseLabelElement caseValue : caseValuesToDelete.getElements()) {
|
||||
caseValuesToMergeWith.addAfter(caseValue, caseValuesToMergeWith.getLastChild());
|
||||
}
|
||||
}
|
||||
DuplicateBranchesInSwitchInspection.copyCaseValues(caseValuesToDelete, caseValuesToMergeWith, mergeWithDefault);
|
||||
}
|
||||
|
||||
void deleteRule() {
|
||||
@@ -1055,4 +1168,18 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
tracker.deleteAndRestoreComments(myRuleToDelete.myLabel);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyCaseValues(@Nullable PsiCaseLabelElementList from,
|
||||
@Nullable PsiCaseLabelElementList to,
|
||||
boolean mergeWithDefault) {
|
||||
if (from == null || to == null) return;
|
||||
for (PsiCaseLabelElement caseValue : from.getElements()) {
|
||||
if (mergeWithDefault) {
|
||||
to.addBefore(caseValue, to.getFirstChild());
|
||||
}
|
||||
else {
|
||||
to.addAfter(caseValue, to.getLastChild());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.util;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -9,6 +9,7 @@ import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -160,6 +161,35 @@ public final class JavaPsiPatternUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the pattern declares one or more pattern variables
|
||||
*
|
||||
* @param pattern pattern to check
|
||||
* @return {@code true} if the pattern declares one or more pattern variables, {@code false} otherwise.
|
||||
*/
|
||||
@Contract(value = "null -> false", pure = true)
|
||||
public static boolean containsPatternVariable(@Nullable PsiCaseLabelElement pattern) {
|
||||
if (pattern instanceof PsiPatternGuard) {
|
||||
return containsPatternVariable(((PsiPatternGuard)pattern).getPattern());
|
||||
}
|
||||
else if (pattern instanceof PsiGuardedPattern) {
|
||||
return containsPatternVariable(((PsiGuardedPattern)pattern).getPrimaryPattern());
|
||||
}
|
||||
else if (pattern instanceof PsiTypeTestPattern) {
|
||||
return ((PsiTypeTestPattern)pattern).getPatternVariable() != null;
|
||||
}
|
||||
else if (pattern instanceof PsiParenthesizedPattern) {
|
||||
return containsPatternVariable(((PsiParenthesizedPattern)pattern).getPattern());
|
||||
}
|
||||
else if (pattern instanceof PsiDeconstructionPattern) {
|
||||
PsiDeconstructionPattern deconstructionPattern = (PsiDeconstructionPattern)pattern;
|
||||
return deconstructionPattern.getPatternVariable() != null ||
|
||||
ContainerUtil.exists(deconstructionPattern.getDeconstructionList().getDeconstructionComponents(),
|
||||
component -> containsPatternVariable(component));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return type of variable in pattern, or null if pattern is incomplete
|
||||
*/
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
class C {
|
||||
void foo(String s) {
|
||||
switch (s) {
|
||||
case "blah blah blah", null -> bar("A");
|
||||
default -> bar("A");
|
||||
}
|
||||
}
|
||||
void bar(String s){}
|
||||
}
|
||||
+1
-2
@@ -1,10 +1,9 @@
|
||||
// "Merge with 'case Number n'" "GENERIC_ERROR_OR_WARNING"
|
||||
class C {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case Number n -> bar("A");
|
||||
case String s -> bar("B");
|
||||
case null -> <caret>bar("A");
|
||||
case null -> bar("A");
|
||||
default -> bar("C");
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -1,10 +1,9 @@
|
||||
// "Merge with 'case null'" "GENERIC_ERROR_OR_WARNING"
|
||||
class C {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null -> bar("A");
|
||||
case String s -> bar("B");
|
||||
case Number n -> <caret>bar("A");
|
||||
case Number n -> bar("A");
|
||||
default -> bar("C");
|
||||
}
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
// "Merge with 'case null'" "GENERIC_ERROR_OR_WARNING"
|
||||
class C {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null, default -> bar("A");
|
||||
case String s -> bar("B");
|
||||
}
|
||||
}
|
||||
void bar(String s){}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case String s -> {}
|
||||
case null, default -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// "Merge with 'case default'" "true"
|
||||
class Test {
|
||||
void foo(String s) {
|
||||
switch (s) {
|
||||
case default, "hello", null, "42" -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// "Merge with 'default'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case default, null -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// "Merge with 'case null'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null, default -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// "Merge with 'case null'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null, default -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
// "Merge with 'case default'" "GENERIC_ERROR_OR_WARNING"
|
||||
class C {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case default, null -> bar("A");
|
||||
case String s -> bar("B");
|
||||
}
|
||||
}
|
||||
void bar(String s){}
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// "Merge with 'case Number n'" "GENERIC_ERROR_OR_WARNING"
|
||||
class C {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case Number n, null -> bar("A");
|
||||
case String s -> bar("B");
|
||||
default -> bar("C");
|
||||
}
|
||||
}
|
||||
void bar(String s){}
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// "Merge with 'case null'" "GENERIC_ERROR_OR_WARNING"
|
||||
class C {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null, Number n -> bar("A");
|
||||
case String s -> bar("B");
|
||||
default -> bar("C");
|
||||
}
|
||||
}
|
||||
void bar(String s){}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Delete redundant 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(Stirng s) {
|
||||
switch (s) {
|
||||
case null -> System.out.println(<caret>42);
|
||||
default -> System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Merge with the default 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case String s -> System.out.println(<caret>"hello");
|
||||
default -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Merge with the default 'switch' branch" "false"
|
||||
class Test {
|
||||
record R() {}
|
||||
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case R() -> System.out.println(<caret>"hello");
|
||||
default -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Merge with the default 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(String s) {
|
||||
switch (s) {
|
||||
case "blah blah blah" -> System.out.println(<caret>"hello");
|
||||
default -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Merge with the default 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(String s) {
|
||||
switch (s) {
|
||||
case "blah blah blah" -> System.out.println(<caret>"hello");
|
||||
case null, default -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// "Merge with 'case null'" "GENERIC_ERROR_OR_WARNING"
|
||||
class C {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null -> bar("A");
|
||||
case String s -> bar("B");
|
||||
case default -> <caret>bar("A");
|
||||
}
|
||||
}
|
||||
void bar(String s){}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null -> System.out.println(<caret>"hello");
|
||||
case String s -> {}
|
||||
default -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// "Delete redundant 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(String s) {
|
||||
switch (s) {
|
||||
case default -> System.out.println("hello");
|
||||
case "hello", null, "42" -> System.out<caret>.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// "Merge with 'case default'" "true"
|
||||
class Test {
|
||||
void foo(String s) {
|
||||
switch (s) {
|
||||
case default -> System.out.println("hello");
|
||||
case "hello", null, "42" -> System.out.println<caret>("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// "Delete redundant 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
default -> System.out.println("hello");
|
||||
case null -> System.ou<caret>t.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// "Merge with 'default'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
default -> System.out.println("hello");
|
||||
case null -> System.out.println<caret>("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// "Delete redundant 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null -> System.out<caret>.println("hello");
|
||||
case default -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// "Merge with 'case null'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null -> System.out.println("hello");
|
||||
case default -> System.out.<caret>println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// "Delete redundant 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null -> System.out.println<caret>("hello");
|
||||
default -> System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// "Merge with 'case null'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null -> System.out.println("hello");
|
||||
default -> System.out.println(<caret>"hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// "Merge with 'case default'" "GENERIC_ERROR_OR_WARNING"
|
||||
class C {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case default -> bar("A");
|
||||
case String s -> bar("B");
|
||||
case null -> <caret>bar("A");
|
||||
}
|
||||
}
|
||||
void bar(String s){}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
class C {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case Number n:
|
||||
bar("A");
|
||||
break;
|
||||
case String s:
|
||||
bar("B");
|
||||
break;
|
||||
case null:
|
||||
bar("A");
|
||||
break;
|
||||
default:
|
||||
bar("C");
|
||||
break;
|
||||
}
|
||||
}
|
||||
void bar(String s){}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
class C {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null:
|
||||
bar("A");
|
||||
break;
|
||||
case String s:
|
||||
bar("B");
|
||||
break;
|
||||
case Number n:
|
||||
bar("A");
|
||||
break;
|
||||
default:
|
||||
bar("C");
|
||||
break;
|
||||
}
|
||||
}
|
||||
void bar(String s){}
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
// "Merge with 'case null'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null, default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
// "Merge with 'case null'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null, default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// "Merge with 'case "hello"'" "true"
|
||||
class Test {
|
||||
void foo(String o) {
|
||||
switch (o) {
|
||||
case "hello":
|
||||
case null, default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(String o) {
|
||||
switch (o) {
|
||||
case null:
|
||||
System.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(String str) {
|
||||
switch (str) {
|
||||
case null:
|
||||
System.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
record R() {}
|
||||
record S() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case null:
|
||||
System.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
record R() {}
|
||||
record S() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(String o) {
|
||||
switch (o) {
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
record R() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(String str) {
|
||||
switch (str) {
|
||||
case "hello":
|
||||
case null, default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(String str) {
|
||||
switch (str) {
|
||||
case "hello":
|
||||
case "blah blah blah":
|
||||
case null, default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
record R() {}
|
||||
record S() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case R():
|
||||
case S():
|
||||
case null, default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null, default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
record R() {}
|
||||
record S() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case R():
|
||||
case S():
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
// "Merge with 'case default'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case default, null:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// "Merge with 'default'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null:
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// "Merge with 'default'" "true"
|
||||
class Test {
|
||||
void foo(String o) {
|
||||
switch (o) {
|
||||
case null:
|
||||
case "hello":
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Merge with the default 'switch' branch" "false"
|
||||
class Test {
|
||||
record R() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case R r:
|
||||
System<caret>.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Merge with the default 'switch' branch" "false"
|
||||
class Test {
|
||||
record R() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case R() when true:
|
||||
System<caret>.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// "Merge with 'case null'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null:
|
||||
System.out.println(42);
|
||||
break;
|
||||
case default:
|
||||
System.out.p<caret>rintln(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// "Merge with 'case null'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null:
|
||||
System.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.pri<caret>ntln(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
// "Merge with 'case "hello"'" "true"
|
||||
class Test {
|
||||
void foo(String o) {
|
||||
switch (o) {
|
||||
case "hello":
|
||||
case null:
|
||||
System.out.println(42);
|
||||
break;
|
||||
default:
|
||||
S<caret>ystem.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(String o) {
|
||||
switch (o) {
|
||||
case "hello":
|
||||
case null:
|
||||
System.out.println(42<caret>);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(String str) {
|
||||
switch (str) {
|
||||
case null:
|
||||
case "hello":
|
||||
case "blah blah blah":
|
||||
System<caret>.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
record R() {}
|
||||
record S() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case R():
|
||||
case S():
|
||||
case null:
|
||||
System<caret>.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
record R() {}
|
||||
record S() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case R():
|
||||
case S():
|
||||
System<caret>.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(String o) {
|
||||
switch (o) {
|
||||
case "hello":
|
||||
case "bye":
|
||||
System.out.println(42<caret>);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
record R() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case R r:
|
||||
System<caret>.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(String str) {
|
||||
switch (str) {
|
||||
case null:
|
||||
case "hello":
|
||||
System<caret>.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(String str) {
|
||||
switch (str) {
|
||||
case null:
|
||||
case "hello":
|
||||
case "blah blah blah":
|
||||
System<caret>.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
record R() {}
|
||||
record S() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case R():
|
||||
case S():
|
||||
case null:
|
||||
System<caret>.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null:
|
||||
System.out.printl<caret>n(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
|
||||
class Test {
|
||||
record R() {}
|
||||
record S() {}
|
||||
|
||||
void foo(Object obj) {
|
||||
switch (obj) {
|
||||
case R():
|
||||
case S():
|
||||
System<caret>.out.println(42);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// "Delete redundant 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
case null:
|
||||
System.out.println(42)<caret>;
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// "Delete redundant 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
case null:
|
||||
System.out.println(42)<caret>;
|
||||
}
|
||||
}
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
// "Delete redundant 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(String o) {
|
||||
switch (o) {
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
case null:
|
||||
case "hello":
|
||||
System.out.println(42<caret>);
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// "Delete redundant 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case null:
|
||||
System.out.println(42)<caret>;
|
||||
break;
|
||||
case default:
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
// "Delete redundant 'switch' branch" "false"
|
||||
class Test {
|
||||
void foo(String o) {
|
||||
switch (o) {
|
||||
case "hello":
|
||||
case null:
|
||||
System.out.println(42<caret>);
|
||||
break;
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// "Merge with 'case default'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
case default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
case null:
|
||||
System.out.<caret>println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// "Merge with 'default'" "true"
|
||||
class Test {
|
||||
void foo(Object o) {
|
||||
switch (o) {
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
case null:
|
||||
Syste<caret>m.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
// "Merge with 'default'" "true"
|
||||
class Test {
|
||||
void foo(String o) {
|
||||
switch (o) {
|
||||
default:
|
||||
System.out.println(42);
|
||||
break;
|
||||
case null:
|
||||
case "hello":
|
||||
System<caret>.out.println(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.codeInspection
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase
|
||||
@@ -15,5 +15,5 @@ class DuplicateBranchesInEnhancedSwitchFixTest : LightQuickFixParameterizedTestC
|
||||
|
||||
override fun getBasePath() = "/inspection/duplicateBranchesInEnhancedSwitchFix"
|
||||
|
||||
override fun getProjectDescriptor() = LightJavaCodeInsightFixtureTestCase.JAVA_17
|
||||
override fun getProjectDescriptor() = LightJavaCodeInsightFixtureTestCase.JAVA_20
|
||||
}
|
||||
+4
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.codeInspection
|
||||
|
||||
import com.intellij.JavaTestUtil
|
||||
@@ -37,6 +37,9 @@ class DuplicateBranchesInEnhancedSwitchTest : LightJavaCodeInsightFixtureTestCas
|
||||
fun testWhenClause1() = IdeaTestUtil.withLevel(module, LanguageLevel.JDK_19_PREVIEW) { doTest() }
|
||||
fun testWhenClause2() = IdeaTestUtil.withLevel(module, LanguageLevel.JDK_19_PREVIEW) { doTest() }
|
||||
fun testExpressionsWithComments() = doTest()
|
||||
fun testNullDuplicatesPattern() = IdeaTestUtil.withLevel(module, LanguageLevel.JDK_20_PREVIEW) { doTest() }
|
||||
fun testPatternDuplicatesNull() = IdeaTestUtil.withLevel(module, LanguageLevel.JDK_20_PREVIEW) { doTest() }
|
||||
fun testNullDuplicatesDefault() = IdeaTestUtil.withLevel(module, LanguageLevel.JDK_19_PREVIEW) { doTest() }
|
||||
|
||||
private fun doTest() {
|
||||
myFixture.enableInspections(DuplicateBranchesInSwitchInspection())
|
||||
|
||||
+4
-1
@@ -1,9 +1,10 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.codeInspection
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase
|
||||
import com.intellij.codeInspection.DuplicateBranchesInSwitchInspection
|
||||
import com.intellij.codeInspection.LocalInspectionTool
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase.JAVA_20
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
@@ -13,4 +14,6 @@ class DuplicateBranchesInSwitchFixTest : LightQuickFixParameterizedTestCase() {
|
||||
override fun configureLocalInspectionTools(): Array<LocalInspectionTool> = arrayOf(DuplicateBranchesInSwitchInspection())
|
||||
|
||||
override fun getBasePath() = "/inspection/duplicateBranchesInSwitchFix"
|
||||
|
||||
override fun getProjectDescriptor() = JAVA_20
|
||||
}
|
||||
+5
-1
@@ -1,8 +1,10 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.codeInspection
|
||||
|
||||
import com.intellij.JavaTestUtil
|
||||
import com.intellij.codeInspection.DuplicateBranchesInSwitchInspection
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import com.intellij.testFramework.IdeaTestUtil
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
|
||||
|
||||
/**
|
||||
@@ -43,6 +45,8 @@ class DuplicateBranchesInSwitchTest : LightJavaCodeInsightFixtureTestCase() {
|
||||
fun testAssignment() = doTest()
|
||||
fun testNoExceptionWhenFirstLabelIsMissing() = doTest()
|
||||
fun testUnresolvedQualifier() = doTest()
|
||||
fun testNullDuplicatesPattern() = IdeaTestUtil.withLevel(module, LanguageLevel.JDK_20_PREVIEW) { doTest() }
|
||||
fun testPatternDuplicatesNull() = IdeaTestUtil.withLevel(module, LanguageLevel.JDK_20_PREVIEW) { doTest() }
|
||||
|
||||
private fun doTest() {
|
||||
myFixture.testHighlighting("${getTestName(false)}.java")
|
||||
|
||||
+29
@@ -572,6 +572,35 @@ public final class SwitchUtils {
|
||||
labelElementList.getElements()[0] instanceof PsiDefaultCaseLabelElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the label has the following form {@code 'case null'}
|
||||
*
|
||||
* @param label label to check
|
||||
* @return {@code true} if the label has the following form {@code 'case null'}, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isCaseNull(@Nullable PsiSwitchLabelStatementBase label) {
|
||||
if (label == null) return false;
|
||||
PsiCaseLabelElementList labelElementList = label.getCaseLabelElementList();
|
||||
return labelElementList != null &&
|
||||
labelElementList.getElementCount() == 1 &&
|
||||
labelElementList.getElements()[0] instanceof PsiExpression expr && ExpressionUtils.isNullLiteral(expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the label has the following form {@code 'case null, default'}
|
||||
*
|
||||
* @param label label to check
|
||||
* @return {@code true} if the label has the following form {@code 'case null, default'}, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isCaseNullDefault(@Nullable PsiSwitchLabelStatementBase label) {
|
||||
if (label == null) return false;
|
||||
PsiCaseLabelElementList labelElementList = label.getCaseLabelElementList();
|
||||
return labelElementList != null &&
|
||||
labelElementList.getElementCount() == 2 &&
|
||||
labelElementList.getElements()[0] instanceof PsiExpression expr && ExpressionUtils.isNullLiteral(expr) &&
|
||||
labelElementList.getElements()[1] instanceof PsiDefaultCaseLabelElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given switch label statement contains a {@code default} case or a total pattern
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user