[java-highlighting] SwitchChecker.checkDuplicates migrated

Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only)

GitOrigin-RevId: 009041ed8f87c4b2af89849393f00388e912d95a
This commit is contained in:
Tagir Valeev
2025-02-19 20:42:37 +00:00
committed by intellij-monorepo-bot
parent b9999a124d
commit 9c4282bb5f
7 changed files with 46 additions and 61 deletions
@@ -324,6 +324,9 @@ switch.label.constant.expected=Constant expression required
switch.label.pattern.expected=Pattern expected for switch selector type ''{0}''
switch.label.unexpected=Constant expression, pattern or null is required
switch.default.label.contains.case=The label for the default case must only use the 'default' keyword, without 'case'
switch.label.duplicate.unconditional.pattern=Duplicate unconditional pattern
switch.label.duplicate.default=Duplicate default label
switch.label.duplicate=Duplicate label ''{0}''
guard.misplaced=Guard is allowed after patterns only
guard.evaluated.to.false=Case label has a guard that is a constant expression with value 'false'
@@ -642,6 +642,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) mySwitchChecker.checkSwitchBlockStatements(block);
if (!hasErrorResults()) mySwitchChecker.checkSwitchSelectorType(block);
if (!hasErrorResults()) mySwitchChecker.checkLabelSelectorCompatibility(block);
if (!hasErrorResults()) mySwitchChecker.checkDuplicates(block);
}
@Override
@@ -15,6 +15,9 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Map;
final class SwitchChecker {
private final @NotNull JavaErrorVisitor myVisitor;
@@ -436,4 +439,14 @@ final class SwitchChecker {
}
myVisitor.report(JavaErrorKinds.SWITCH_LABEL_UNEXPECTED.create(label));
}
void checkDuplicates(@NotNull PsiSwitchBlock block) {
for (Map.Entry<Object, Collection<PsiElement>> entry : JavaPsiSwitchUtil.getValuesAndLabels(block).entrySet()) {
if (entry.getValue().size() <= 1) continue;
Object duplicateKey = entry.getKey();
for (PsiElement duplicateElement : entry.getValue()) {
myVisitor.report(JavaErrorKinds.SWITCH_LABEL_DUPLICATE.create(duplicateElement, duplicateKey));
}
}
}
}
@@ -955,6 +955,26 @@ public final class JavaErrorKinds {
public static final Parameterized<PsiExpression, PsiType> SWITCH_LABEL_PATTERN_EXPECTED =
parameterized(PsiExpression.class, PsiType.class, "switch.label.pattern.expected")
.withRawDescription((expr, type) -> message("switch.label.pattern.expected", formatType(type)));
public static final Parameterized<PsiElement, Object> SWITCH_LABEL_DUPLICATE =
parameterized(PsiElement.class, Object.class, "switch.label.duplicate")
.withRawDescription((label, value) -> {
if (value instanceof JavaPsiSwitchUtil.SwitchSpecialValue specialValue) {
return switch (specialValue) {
case UNCONDITIONAL_PATTERN -> message("switch.label.duplicate.unconditional.pattern");
case DEFAULT_VALUE -> message("switch.label.duplicate.default");
case NULL_VALUE -> message("switch.label.duplicate", PsiKeyword.NULL);
};
}
else if (value instanceof PsiEnumConstant constant) {
return message("switch.label.duplicate", constant.getName());
}
else if (label instanceof PsiLiteralExpression literalExpression) {
return message("switch.label.duplicate", literalExpression.getValue());
}
else {
return message("switch.label.duplicate", value);
}
});
public static final Simple<PsiReferenceExpression> EXPRESSION_EXPECTED = error("expression.expected");
public static final Parameterized<PsiReferenceExpression, PsiSuperExpression> EXPRESSION_SUPER_UNQUALIFIED_DEFAULT_METHOD =
@@ -235,6 +235,15 @@ final class JavaErrorFixProvider {
HighlightFixUtil.getIncreaseLanguageLevelFixes(error.psi(), feature).forEach(sink);
}
});
fix(SWITCH_LABEL_DUPLICATE, error -> {
if (error.psi() instanceof PsiCaseLabelElement caseLabel) {
return myFactory.createDeleteSwitchLabelFix(caseLabel);
}
else if (error.context() == JavaPsiSwitchUtil.SwitchSpecialValue.DEFAULT_VALUE) {
return myFactory.createDeleteDefaultFix(null, error.psi());
}
return null;
});
}
private void createMethodFixes() {
@@ -57,12 +57,6 @@ public class PatternsInSwitchBlockHighlightingModel extends SwitchBlockHighlight
PsiCodeBlock body = myBlock.getBody();
if (body == null) return;
MultiMap<Object, PsiElement> elementsToCheckDuplicates = JavaPsiSwitchUtil.getValuesAndLabels(myBlock);
if (checkDuplicates(elementsToCheckDuplicates, errorSink)) {
return;
}
List<List<PsiSwitchLabelStatementBase>> elementsToCheckFallThroughLegality = new SmartList<>();
List<PsiElement> elementsToCheckDominance = new ArrayList<>();
List<PsiCaseLabelElement> elementsToCheckCompleteness = new ArrayList<>();
@@ -21,7 +21,6 @@ import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.SmartHashSet;
import com.siyeh.ig.psiutils.SwitchUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
@@ -86,12 +85,6 @@ public class SwitchBlockHighlightingModel {
PsiCodeBlock body = myBlock.getBody();
if (body == null) return;
MultiMap<Object, PsiElement> elementsToCheckDuplicates = JavaPsiSwitchUtil.getValuesAndLabels(myBlock);
if (checkDuplicates(elementsToCheckDuplicates, errorSink)) {
return;
}
// todo replace with needToCheckCompleteness
if (myBlock instanceof PsiSwitchExpression && !hasDefaultCase(body)) {
PsiClass selectorClass = PsiUtil.resolveClassInClassTypeOnly(mySelectorType);
@@ -131,35 +124,6 @@ public class SwitchBlockHighlightingModel {
return JavaPsiFacade.getInstance(constant.getProject()).getConstantEvaluationHelper().computeConstantExpression(constant, false);
}
static boolean checkDuplicates(@NotNull MultiMap<Object, PsiElement> values, Consumer<? super HighlightInfo.Builder> errorSink) {
boolean reported = false;
for (Map.Entry<Object, Collection<PsiElement>> entry : values.entrySet()) {
if (entry.getValue().size() <= 1) continue;
Object duplicateKey = entry.getKey();
for (PsiElement duplicateElement : entry.getValue()) {
HighlightInfo.Builder info = createDuplicateInfo(duplicateKey, duplicateElement);
errorSink.accept(info);
reported = true;
}
}
return reported;
}
@NotNull
private static HighlightInfo.Builder createDuplicateInfo(@Nullable Object duplicateKey, @NotNull PsiElement duplicateElement) {
String description = createDuplicateDescription(duplicateKey, duplicateElement);
HighlightInfo.Builder info = createError(duplicateElement, description);
if (duplicateElement instanceof PsiCaseLabelElement caseLabel) {
IntentionAction action = getFixFactory().createDeleteSwitchLabelFix(caseLabel);
info.registerFix(action, null, null, null, null);
}
else if (duplicateKey == JavaPsiSwitchUtil.SwitchSpecialValue.DEFAULT_VALUE) {
IntentionAction action = getFixFactory().createDeleteDefaultFix(null, duplicateElement);
info.registerFix(action, null, null, null, null);
}
return info;
}
boolean needToCheckCompleteness(@NotNull List<? extends PsiCaseLabelElement> elements) {
return myBlock instanceof PsiSwitchExpression || myBlock instanceof PsiSwitchStatement && isEnhancedSwitch(elements);
}
@@ -259,23 +223,4 @@ public class SwitchBlockHighlightingModel {
return StreamEx.ofKeys(patternInSwitchModel.findDominatedLabels(dominanceCheckingCandidates), value -> value instanceof PsiPattern)
.into(result);
}
private static @NotNull @Nls String createDuplicateDescription(@Nullable Object duplicateKey, @NotNull PsiElement duplicateElement) {
if (duplicateKey instanceof JavaPsiSwitchUtil.SwitchSpecialValue specialValue) {
return switch (specialValue) {
case UNCONDITIONAL_PATTERN -> JavaErrorBundle.message("duplicate.unconditional.pattern.label");
case DEFAULT_VALUE -> JavaErrorBundle.message("duplicate.default.switch.label");
case NULL_VALUE -> JavaErrorBundle.message("duplicate.switch.label", PsiKeyword.NULL);
};
}
else if (duplicateKey instanceof PsiEnumConstant constant) {
return JavaErrorBundle.message("duplicate.switch.label", constant.getName());
}
else if (duplicateElement instanceof PsiLiteralExpression literalExpression) {
return JavaErrorBundle.message("duplicate.switch.label", literalExpression.getValue());
}
else {
return JavaErrorBundle.message("duplicate.switch.label", duplicateKey);
}
}
}