[java-inspections] DuplicateBranchesInSwitch: adapt the inspection to Java 20

IDEA-310260

GitOrigin-RevId: ce07977550e3a5c15d63f0a6eeed1d7341c74164
This commit is contained in:
Andrey Cherkasov
2023-01-15 05:39:32 +00:00
committed by intellij-monorepo-bot
parent 13b5f3d866
commit e008c6522f
80 changed files with 735 additions and 500 deletions
@@ -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
@@ -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
*/
@@ -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,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,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");
}
}
@@ -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){}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -1,8 +0,0 @@
// "Merge with 'default'" "true"
class Test {
void foo(Object o) {
switch (o) {
case default, null -> System.out.println("hello");
}
}
}
@@ -1,8 +0,0 @@
// "Merge with 'case null'" "true"
class Test {
void foo(Object o) {
switch (o) {
case null, default -> System.out.println("hello");
}
}
}
@@ -1,8 +0,0 @@
// "Merge with 'case null'" "true"
class Test {
void foo(Object o) {
switch (o) {
case null, default -> System.out.println("hello");
}
}
}
@@ -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){}
}
@@ -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){}
}
@@ -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){}
}
@@ -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);
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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){}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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");
}
}
}
@@ -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){}
}
@@ -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){}
}
@@ -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){}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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;
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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;
}
}
}
@@ -1,11 +0,0 @@
// "Merge with 'default'" "true"
class Test {
void foo(Object o) {
switch (o) {
case null:
default:
System.out.println(42);
break;
}
}
}
@@ -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;
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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;
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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>;
}
}
}
@@ -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>;
}
}
}
@@ -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>);
}
}
}
@@ -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);
}
}
}
@@ -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;
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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
}
@@ -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())
@@ -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
}
@@ -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")
@@ -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
*