[java-highlighting] Migrate fallthrough-related errors

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

GitOrigin-RevId: 0a2b7e91a93e8207101368c0fcfb3cf58a793203
This commit is contained in:
Tagir Valeev
2025-02-25 15:49:35 +01:00
committed by intellij-monorepo-bot
parent 1da6f5d864
commit 6d7ef89b5a
9 changed files with 220 additions and 213 deletions

View File

@@ -0,0 +1,22 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeserver.core;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
/**
* Utilities related to Java expressions
*/
public final class JavaPsiExpressionUtil {
/**
* @param expression expression to check
* @return true if the expression is a null literal (possibly parenthesized or cast)
*/
@Contract("null -> false")
public static boolean isNullLiteral(@Nullable PsiExpression expression) {
return PsiUtil.deparenthesizeExpression(expression) instanceof PsiLiteralExpression literal && literal.getValue() == null;
}
}

View File

@@ -330,6 +330,15 @@ switch.default.label.contains.case=The label for the default case must only use
switch.label.duplicate.unconditional.pattern=Duplicate unconditional pattern
switch.label.duplicate.default=Duplicate default label
switch.label.duplicate=Duplicate label ''{0}''
switch.fallthrough.to.pattern=Illegal fall-through to a pattern
switch.multiple.labels.with.pattern.variables=Multiple switch labels are permitted for a switch labeled statement group only if none of them declare any pattern variables
switch.default.null.order=Invalid case label order: 'null' must be first and 'default' must be second
switch.default.label.not.allowed=Default label not allowed here: 'default' can only be used as a single case label or paired only with 'null'
switch.null.label.not.allowed=Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'
switch.label.combination.constants.and.patterns=Invalid case label combination: a case label must consist of either a list of case constants or a single case pattern
switch.label.combination.constants.and.patterns.unnamed=Invalid case label combination: a case label must consist of either a list of case constants or a list of case patterns
switch.label.multiple.patterns=Invalid case label combination: a case label must not consist of more than one case pattern
switch.label.multiple.patterns.unnamed=Invalid case label combination: multiple patterns are allowed only if none of them declare any pattern variables
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'

View File

@@ -647,6 +647,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) mySwitchChecker.checkSwitchSelectorType(block);
if (!hasErrorResults()) mySwitchChecker.checkLabelSelectorCompatibility(block);
if (!hasErrorResults()) mySwitchChecker.checkDuplicates(block);
if (!hasErrorResults()) mySwitchChecker.checkFallthroughLegality(block);
}
@Override

View File

@@ -2,6 +2,7 @@
package com.intellij.java.codeserver.highlighting;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.codeserver.core.JavaPsiExpressionUtil;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext;
import com.intellij.pom.java.JavaFeature;
@@ -11,12 +12,12 @@ import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.IncompleteModelUtil;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.util.*;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Map;
import java.util.*;
final class SwitchChecker {
private final @NotNull JavaErrorVisitor myVisitor;
@@ -449,4 +450,154 @@ final class SwitchChecker {
}
}
}
void checkFallthroughLegality(@NotNull PsiSwitchBlock block) {
if (!myVisitor.isApplicable(JavaFeature.PATTERNS_IN_SWITCH)) return;
PsiCodeBlock body = block.getBody();
if (body == null) return;
List<List<PsiSwitchLabelStatementBase>> elementsToCheckFallThroughLegality = new SmartList<>();
int switchBlockGroupCounter = 0;
for (PsiStatement st : body.getStatements()) {
if (!(st instanceof PsiSwitchLabelStatementBase labelStatement)) continue;
List<PsiSwitchLabelStatementBase> switchLabels;
if (switchBlockGroupCounter < elementsToCheckFallThroughLegality.size()) {
switchLabels = elementsToCheckFallThroughLegality.get(switchBlockGroupCounter);
}
else {
switchLabels = new SmartList<>();
elementsToCheckFallThroughLegality.add(switchLabels);
}
switchLabels.add(labelStatement);
if (!(PsiTreeUtil.skipWhitespacesAndCommentsForward(labelStatement) instanceof PsiSwitchLabelStatement)) {
switchBlockGroupCounter++;
}
}
Set<PsiElement> alreadyFallThroughElements = new HashSet<>();
checkFallThroughFromPatternWithSeveralLabels(elementsToCheckFallThroughLegality, alreadyFallThroughElements);
checkFallThroughToPatternPrecedingCompleteNormally(elementsToCheckFallThroughLegality, alreadyFallThroughElements);
}
private void checkFallThroughFromPatternWithSeveralLabels(@NotNull List<? extends List<PsiSwitchLabelStatementBase>> switchBlockGroup,
@NotNull Set<? super PsiElement> alreadyFallThroughElements) {
if (switchBlockGroup.isEmpty()) return;
for (List<PsiSwitchLabelStatementBase> switchLabel : switchBlockGroup) {
for (PsiSwitchLabelStatementBase switchLabelElement : switchLabel) {
PsiCaseLabelElementList labelElementList = switchLabelElement.getCaseLabelElementList();
if (labelElementList == null || labelElementList.getElementCount() == 0) continue;
if (!checkCaseLabelCombination(labelElementList)) {
PsiCaseLabelElement[] elements = labelElementList.getElements();
final PsiCaseLabelElement first = elements[0];
if (JavaPsiPatternUtil.containsNamedPatternVariable(first)) {
PsiElement nextNotLabel = PsiTreeUtil.skipSiblingsForward(switchLabelElement, PsiWhiteSpace.class, PsiComment.class,
PsiSwitchLabelStatement.class);
//there is no statement, it is allowed to go through (14.11.1 JEP 440-441)
if (!(nextNotLabel instanceof PsiStatement)) {
continue;
}
if (PsiTreeUtil.skipWhitespacesAndCommentsForward(switchLabelElement) instanceof PsiSwitchLabelStatement ||
PsiTreeUtil.skipWhitespacesAndCommentsBackward(switchLabelElement) instanceof PsiSwitchLabelStatement) {
alreadyFallThroughElements.add(first);
myVisitor.report(JavaErrorKinds.SWITCH_MULTIPLE_LABELS_WITH_PATTERN_VARIABLES.create(first));
}
}
}
}
}
}
private boolean checkCaseLabelCombination(PsiCaseLabelElementList labelElementList) {
PsiCaseLabelElement[] elements = labelElementList.getElements();
PsiCaseLabelElement firstElement = elements[0];
if (elements.length == 1) {
if (firstElement instanceof PsiDefaultCaseLabelElement defaultLabel) {
myVisitor.report(JavaErrorKinds.SWITCH_DEFAULT_LABEL_CONTAINS_CASE.create(defaultLabel, labelElementList));
return true;
}
return false;
}
if (elements.length == 2) {
if (firstElement instanceof PsiDefaultCaseLabelElement defaultLabel &&
elements[1] instanceof PsiExpression expr &&
JavaPsiExpressionUtil.isNullLiteral(expr)) {
myVisitor.report(JavaErrorKinds.SWITCH_DEFAULT_NULL_ORDER.create(defaultLabel, labelElementList));
return true;
}
if (firstElement instanceof PsiExpression expr &&
JavaPsiExpressionUtil.isNullLiteral(expr) &&
elements[1] instanceof PsiDefaultCaseLabelElement) {
return false;
}
}
boolean hasUnnamed = myVisitor.isApplicable(JavaFeature.UNNAMED_PATTERNS_AND_VARIABLES);
boolean reported = false;
for (PsiCaseLabelElement element : elements) {
if (element instanceof PsiDefaultCaseLabelElement defaultLabel) {
myVisitor.report(JavaErrorKinds.SWITCH_DEFAULT_LABEL_NOT_ALLOWED.create(defaultLabel));
reported = true;
}
else if (element instanceof PsiExpression expr && JavaPsiExpressionUtil.isNullLiteral(expr)) {
myVisitor.report(JavaErrorKinds.SWITCH_NULL_LABEL_NOT_ALLOWED.create(expr));
reported = true;
}
else if (element instanceof PsiPattern pattern && firstElement instanceof PsiExpression) {
var kind = hasUnnamed
? JavaErrorKinds.SWITCH_LABEL_COMBINATION_CONSTANTS_AND_PATTERNS_UNNAMED
: JavaErrorKinds.SWITCH_LABEL_COMBINATION_CONSTANTS_AND_PATTERNS;
myVisitor.report(kind.create(pattern));
reported = true;
}
}
if (reported) return true;
if (firstElement instanceof PsiPattern) {
PsiCaseLabelElement nonPattern = ContainerUtil.find(elements, e -> !(e instanceof PsiPattern));
if (nonPattern != null) {
var kind = hasUnnamed
? JavaErrorKinds.SWITCH_LABEL_COMBINATION_CONSTANTS_AND_PATTERNS_UNNAMED
: JavaErrorKinds.SWITCH_LABEL_COMBINATION_CONSTANTS_AND_PATTERNS;
myVisitor.report(kind.create(nonPattern));
return true;
}
if (!hasUnnamed) {
myVisitor.report(JavaErrorKinds.SWITCH_LABEL_MULTIPLE_PATTERNS.create(elements[1]));
return true;
}
PsiCaseLabelElement patternVarElement = ContainerUtil.find(elements, JavaPsiPatternUtil::containsNamedPatternVariable);
if (patternVarElement != null) {
myVisitor.report(JavaErrorKinds.SWITCH_LABEL_MULTIPLE_PATTERNS_UNNAMED.create(patternVarElement));
return true;
}
}
return false;
}
private void checkFallThroughToPatternPrecedingCompleteNormally(@NotNull List<? extends List<? extends PsiSwitchLabelStatementBase>> switchBlockGroup,
@NotNull Set<PsiElement> alreadyFallThroughElements) {
for (int i = 1; i < switchBlockGroup.size(); i++) {
List<? extends PsiSwitchLabelStatementBase> switchLabels = switchBlockGroup.get(i);
PsiSwitchLabelStatementBase firstSwitchLabelInGroup = switchLabels.get(0);
for (PsiSwitchLabelStatementBase switchLabel : switchLabels) {
if (!(switchLabel instanceof PsiSwitchLabelStatement)) {
return;
}
PsiCaseLabelElementList labelElementList = switchLabel.getCaseLabelElementList();
if (labelElementList == null) continue;
List<PsiCaseLabelElement> patternElements = ContainerUtil.filter(labelElementList.getElements(),
labelElement -> JavaPsiPatternUtil.containsNamedPatternVariable(
labelElement));
if (patternElements.isEmpty()) continue;
PsiStatement prevStatement = PsiTreeUtil.getPrevSiblingOfType(firstSwitchLabelInGroup, PsiStatement.class);
if (prevStatement == null) continue;
ControlFlow flow = ControlFlowChecker.getControlFlow(prevStatement);
if (flow != null && ControlFlowUtil.canCompleteNormally(flow, 0, flow.getSize())) {
List<PsiCaseLabelElement> elements =
ContainerUtil.filter(patternElements, patternElement -> !alreadyFallThroughElements.contains(patternElement));
for (PsiCaseLabelElement patternElement : elements) {
myVisitor.report(JavaErrorKinds.SWITCH_FALLTHROUGH_TO_PATTERN.create(patternElement));
}
}
}
}
}
}

View File

@@ -994,6 +994,23 @@ public final class JavaErrorKinds {
return message("switch.label.duplicate", value);
}
});
public static final Simple<PsiCaseLabelElement> SWITCH_FALLTHROUGH_TO_PATTERN = error("switch.fallthrough.to.pattern");
public static final Simple<PsiCaseLabelElement> SWITCH_MULTIPLE_LABELS_WITH_PATTERN_VARIABLES =
error("switch.multiple.labels.with.pattern.variables");
public static final Parameterized<PsiDefaultCaseLabelElement, PsiCaseLabelElementList> SWITCH_DEFAULT_NULL_ORDER =
parameterized("switch.default.null.order");
public static final Simple<PsiDefaultCaseLabelElement> SWITCH_DEFAULT_LABEL_NOT_ALLOWED =
error("switch.default.label.not.allowed");
public static final Simple<PsiExpression> SWITCH_NULL_LABEL_NOT_ALLOWED =
error("switch.null.label.not.allowed");
public static final Simple<PsiCaseLabelElement> SWITCH_LABEL_COMBINATION_CONSTANTS_AND_PATTERNS =
error("switch.label.combination.constants.and.patterns");
public static final Simple<PsiCaseLabelElement> SWITCH_LABEL_COMBINATION_CONSTANTS_AND_PATTERNS_UNNAMED =
error("switch.label.combination.constants.and.patterns.unnamed");
public static final Simple<PsiCaseLabelElement> SWITCH_LABEL_MULTIPLE_PATTERNS =
error("switch.label.multiple.patterns");
public static final Simple<PsiCaseLabelElement> SWITCH_LABEL_MULTIPLE_PATTERNS_UNNAMED =
error("switch.label.multiple.patterns.unnamed");
public static final Simple<PsiReferenceExpression> EXPRESSION_EXPECTED = error("expression.expected");
public static final Parameterized<PsiReferenceExpression, PsiSuperExpression> EXPRESSION_SUPER_UNQUALIFIED_DEFAULT_METHOD =

View File

@@ -734,6 +734,13 @@ final class JavaErrorFixProvider {
fix(SWITCH_LABEL_QUALIFIED_ENUM, error -> myFactory.createDeleteFix(
requireNonNull(error.psi().getQualifier()), JavaErrorBundle.message("qualified.enum.constant.in.switch.remove.fix")));
fix(SWITCH_DEFAULT_LABEL_CONTAINS_CASE, error -> myFactory.createReplaceCaseDefaultWithDefaultFix(error.context()));
JavaFixProvider<PsiCaseLabelElement, Void> splitCase = error -> myFactory.createSplitSwitchBranchWithSeveralCaseValuesAction();
fix(SWITCH_MULTIPLE_LABELS_WITH_PATTERN_VARIABLES, splitCase);
fix(SWITCH_LABEL_COMBINATION_CONSTANTS_AND_PATTERNS, splitCase);
fix(SWITCH_LABEL_COMBINATION_CONSTANTS_AND_PATTERNS_UNNAMED, splitCase);
fix(SWITCH_LABEL_MULTIPLE_PATTERNS, splitCase);
fix(SWITCH_LABEL_MULTIPLE_PATTERNS_UNNAMED, splitCase);
fix(SWITCH_DEFAULT_NULL_ORDER, error -> myFactory.createReverseCaseDefaultNullFixFix(error.context()));
}
private void createAccessFixes() {

View File

@@ -6,8 +6,6 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.java.codeserver.core.JavaPsiSealedUtil;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.util.*;
@@ -17,14 +15,12 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.SmartHashSet;
import com.siyeh.ig.fixes.MakeDefaultLastCaseFix;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.SwitchUtils;
import com.siyeh.ig.psiutils.TypeUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -57,16 +53,10 @@ public class PatternsInSwitchBlockHighlightingModel extends SwitchBlockHighlight
PsiCodeBlock body = myBlock.getBody();
if (body == null) return;
List<List<PsiSwitchLabelStatementBase>> elementsToCheckFallThroughLegality = new SmartList<>();
List<PsiElement> elementsToCheckDominance = new ArrayList<>();
List<PsiCaseLabelElement> elementsToCheckCompleteness = new ArrayList<>();
int switchBlockGroupCounter = 0;
for (PsiStatement st : body.getStatements()) {
if (!(st instanceof PsiSwitchLabelStatementBase labelStatement)) continue;
fillElementsToCheckFallThroughLegality(elementsToCheckFallThroughLegality, labelStatement, switchBlockGroupCounter);
if (!(PsiTreeUtil.skipWhitespacesAndCommentsForward(labelStatement) instanceof PsiSwitchLabelStatement)) {
switchBlockGroupCounter++;
}
if (labelStatement.isDefaultCase()) {
elementsToCheckDominance.add(requireNonNull(labelStatement.getFirstChild()));
continue;
@@ -78,14 +68,6 @@ public class PatternsInSwitchBlockHighlightingModel extends SwitchBlockHighlight
elementsToCheckCompleteness.add(labelElement);
}
}
Set<PsiElement> alreadyFallThroughElements = new HashSet<>();
boolean reported =
checkFallThroughFromPatternWithSeveralLabels(elementsToCheckFallThroughLegality, alreadyFallThroughElements, errorSink);
reported |= checkFallThroughToPatternPrecedingCompleteNormally(elementsToCheckFallThroughLegality, alreadyFallThroughElements, errorSink);
if (reported) {
return;
}
if (checkDominance(elementsToCheckDominance, errorSink)) {
return;
}
@@ -95,20 +77,6 @@ public class PatternsInSwitchBlockHighlightingModel extends SwitchBlockHighlight
}
}
private static void fillElementsToCheckFallThroughLegality(@NotNull List<List<PsiSwitchLabelStatementBase>> elements,
@NotNull PsiSwitchLabelStatementBase labelStatement,
int switchBlockGroupCounter) {
List<PsiSwitchLabelStatementBase> switchLabels;
if (switchBlockGroupCounter < elements.size()) {
switchLabels = elements.get(switchBlockGroupCounter);
}
else {
switchLabels = new SmartList<>();
elements.add(switchLabels);
}
switchLabels.add(labelStatement);
}
@NotNull Map<PsiCaseLabelElement, PsiElement> findDominatedLabels(@NotNull List<? extends PsiElement> switchLabels) {
Map<PsiCaseLabelElement, PsiElement> result = new HashMap<>();
for (int i = 0; i < switchLabels.size() - 1; i++) {
@@ -175,174 +143,6 @@ public class PatternsInSwitchBlockHighlightingModel extends SwitchBlockHighlight
elements[1] instanceof PsiDefaultCaseLabelElement;
}
private static boolean checkFallThroughFromPatternWithSeveralLabels(@NotNull List<? extends List<PsiSwitchLabelStatementBase>> switchBlockGroup,
@NotNull Set<? super PsiElement> alreadyFallThroughElements,
@NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
if (switchBlockGroup.isEmpty()) return false;
boolean reported = false;
for (List<PsiSwitchLabelStatementBase> switchLabel : switchBlockGroup) {
for (PsiSwitchLabelStatementBase switchLabelElement : switchLabel) {
PsiCaseLabelElementList labelElementList = switchLabelElement.getCaseLabelElementList();
if (labelElementList == null || labelElementList.getElementCount() == 0) continue;
CaseLabelCombinationProblem problem = checkCaseLabelCombination(labelElementList);
PsiCaseLabelElement[] elements = labelElementList.getElements();
final PsiCaseLabelElement first = elements[0];
if (problem != null) {
HighlightInfo.Builder info =
addIllegalFallThroughError(problem.element(), problem.message(), problem.customAction(), alreadyFallThroughElements);
errorSink.accept(info);
reported = true;
}
else {
if (JavaPsiPatternUtil.containsNamedPatternVariable(first)) {
PsiElement nextNotLabel = PsiTreeUtil.skipSiblingsForward(switchLabelElement, PsiWhiteSpace.class, PsiComment.class,
PsiSwitchLabelStatement.class);
//there is no statement, it is allowed to go through (14.11.1 JEP 440-441)
if (!(nextNotLabel instanceof PsiStatement)) {
continue;
}
if (PsiTreeUtil.skipWhitespacesAndCommentsForward(switchLabelElement) instanceof PsiSwitchLabelStatement ||
PsiTreeUtil.skipWhitespacesAndCommentsBackward(switchLabelElement) instanceof PsiSwitchLabelStatement) {
HighlightInfo.Builder info = addIllegalFallThroughError(first, "multiple.switch.labels", null, alreadyFallThroughElements);
errorSink.accept(info);
reported = true;
}
}
}
}
}
return reported;
}
private record CaseLabelCombinationProblem(@NotNull PsiCaseLabelElement element,
@NotNull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String message,
@Nullable ModCommandAction customAction) {
}
private static @Nullable CaseLabelCombinationProblem checkCaseLabelCombination(PsiCaseLabelElementList labelElementList) {
PsiCaseLabelElement[] elements = labelElementList.getElements();
PsiCaseLabelElement firstElement = elements[0];
if (elements.length == 1) {
if (firstElement instanceof PsiDefaultCaseLabelElement) {
ModCommandAction fix = getFixFactory().createReplaceCaseDefaultWithDefaultFix(labelElementList);
return new CaseLabelCombinationProblem(firstElement, "default.label.must.not.contains.case.keyword", fix);
}
return null;
}
if (elements.length == 2) {
if (firstElement instanceof PsiDefaultCaseLabelElement &&
elements[1] instanceof PsiExpression expr &&
ExpressionUtils.isNullLiteral(expr)) {
ModCommandAction fix = getFixFactory().createReverseCaseDefaultNullFixFix(labelElementList);
return new CaseLabelCombinationProblem(firstElement, "invalid.default.and.null.order", fix);
}
if (firstElement instanceof PsiExpression expr &&
ExpressionUtils.isNullLiteral(expr) &&
elements[1] instanceof PsiDefaultCaseLabelElement) {
return null;
}
}
int defaultIndex = -1;
int nullIndex = -1;
int patternIndex = -1;
for (int i = 0; i < elements.length; i++) {
if (elements[i] instanceof PsiDefaultCaseLabelElement) {
defaultIndex = i;
break;
}
else if (elements[i] instanceof PsiExpression expr && ExpressionUtils.isNullLiteral(expr)) {
nullIndex = i;
break;
}
else if (elements[i] instanceof PsiPattern) {
patternIndex = i;
}
}
if (defaultIndex != -1) {
return new CaseLabelCombinationProblem(elements[defaultIndex], "default.label.not.allowed.here", null);
}
if (nullIndex != -1) {
return new CaseLabelCombinationProblem(elements[nullIndex], "null.label.not.allowed.here", null);
}
if (firstElement instanceof PsiExpression && patternIndex != -1) {
return getPatternConstantCombinationProblem(elements[patternIndex]);
}
else if (firstElement instanceof PsiPattern) {
PsiCaseLabelElement nonPattern = ContainerUtil.find(elements, e -> !(e instanceof PsiPattern));
if (nonPattern != null) {
return getPatternConstantCombinationProblem(nonPattern);
}
if (PsiUtil.isAvailable(JavaFeature.UNNAMED_PATTERNS_AND_VARIABLES, firstElement)) {
PsiCaseLabelElement patternVarElement = ContainerUtil.find(elements, JavaPsiPatternUtil::containsNamedPatternVariable);
if (patternVarElement != null) {
return new CaseLabelCombinationProblem(patternVarElement, "invalid.case.label.combination.several.patterns.unnamed", null);
}
}
else {
return new CaseLabelCombinationProblem(elements[1], "invalid.case.label.combination.several.patterns", null);
}
}
return null;
}
private static @NotNull CaseLabelCombinationProblem getPatternConstantCombinationProblem(PsiCaseLabelElement anchor) {
if (PsiUtil.isAvailable(JavaFeature.UNNAMED_PATTERNS_AND_VARIABLES, anchor)) {
return new CaseLabelCombinationProblem(anchor, "invalid.case.label.combination.constants.and.patterns.unnamed", null);
}
else {
return new CaseLabelCombinationProblem(anchor, "invalid.case.label.combination.constants.and.patterns", null);
}
}
private static HighlightInfo.@NotNull Builder addIllegalFallThroughError(@NotNull PsiElement element,
@NotNull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String key,
@Nullable ModCommandAction customAction,
@NotNull Set<? super PsiElement> alreadyFallThroughElements) {
alreadyFallThroughElements.add(element);
HighlightInfo.Builder info = createError(element, JavaErrorBundle.message(key));
IntentionAction action = getFixFactory().createSplitSwitchBranchWithSeveralCaseValuesAction();
info.registerFix(action, null, null, null, null);
if (customAction != null) {
info.registerFix(customAction, null, null, null, null);
}
return info;
}
private static boolean checkFallThroughToPatternPrecedingCompleteNormally(@NotNull List<? extends List<? extends PsiSwitchLabelStatementBase>> switchBlockGroup,
@NotNull Set<PsiElement> alreadyFallThroughElements,
@NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
boolean reported = false;
for (int i = 1; i < switchBlockGroup.size(); i++) {
List<? extends PsiSwitchLabelStatementBase> switchLabels = switchBlockGroup.get(i);
PsiSwitchLabelStatementBase firstSwitchLabelInGroup = switchLabels.get(0);
for (PsiSwitchLabelStatementBase switchLabel : switchLabels) {
if (!(switchLabel instanceof PsiSwitchLabelStatement)) {
return reported;
}
PsiCaseLabelElementList labelElementList = switchLabel.getCaseLabelElementList();
if (labelElementList == null) continue;
List<PsiCaseLabelElement> patternElements = ContainerUtil.filter(labelElementList.getElements(),
labelElement -> JavaPsiPatternUtil.containsNamedPatternVariable(
labelElement));
if (patternElements.isEmpty()) continue;
PsiStatement prevStatement = PsiTreeUtil.getPrevSiblingOfType(firstSwitchLabelInGroup, PsiStatement.class);
if (prevStatement == null) continue;
if (ControlFlowUtils.statementMayCompleteNormally(prevStatement)) {
List<PsiCaseLabelElement> elements =
ContainerUtil.filter(patternElements, patternElement -> !alreadyFallThroughElements.contains(patternElement));
for (PsiCaseLabelElement patternElement : elements) {
errorSink.accept(createError(patternElement, JavaErrorBundle.message("switch.illegal.fall.through.to")));
reported = true;
}
}
}
}
return reported;
}
/**
* 14.11.1 Switch Blocks
* To ensure the absence of unreachable statements, domination rules provide a possible order

View File

@@ -118,7 +118,7 @@ public class Main {
void test11(Integer integer) {
switch (integer) {
case 1, 2:
case <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, Integer i when i == 42:
case <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a list of case patterns">Integer i</error> when i == 42:
System.out.println("blah blah blah");
break;
default: System.out.println("null");
@@ -144,7 +144,7 @@ public class Main {
void test14(Object obj) {
switch (obj) {
case null, String s when s.isEmpty()<error descr="':' or '->' expected"><error descr="Unexpected token">,</error></error> Integer i<error descr="';' expected"> </error><error descr="Cannot resolve symbol 'when'" textAttributesKey="WRONG_REFERENCES_ATTRIBUTES">when</error> <error descr="Variable 'i' is already defined in the scope">i</error><error descr="';' expected"> </error><error descr="Unexpected token">==</error> <error descr="Not a statement">42</error> <error descr="Unexpected token">-></error> {}
case null, String s when s.isEmpty()<error descr="':' or '->' expected"><error descr="Unexpected token">,</error></error> Integer i<error descr="';' expected"> </error><error descr="Cannot resolve symbol 'when'">when</error> <error descr="Variable 'i' is already defined in the scope">i</error><error descr="';' expected"> </error><error descr="Unexpected token">==</error> <error descr="Not a statement">42</error> <error descr="Unexpected token">-></error> {}
default -> {}
}
}
@@ -172,20 +172,20 @@ public class Main {
void test18(String s) {
switch (s) {
case "hello", "world", null, String str when <error descr="Cannot resolve symbol 'str'" textAttributesKey="WRONG_REFERENCES_ATTRIBUTES">str</error>.isEmpty() -> {}
case "hello", "world", null, String str when <error descr="Cannot resolve symbol 'str'">str</error>.isEmpty() -> {}
default -> {}
}
}
void test19(String s) {
switch (s) {
case "hello", "world", <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, String str -> {}
case "hello", "world", <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a list of case patterns">String str</error> -> {}
}
}
void test20(Object obj) {
switch (obj) {
case <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, S(), R() -> {}
case <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a list of case patterns">S()</error>, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a list of case patterns">R()</error> -> {}
default -> {}
}
}
@@ -213,13 +213,13 @@ public class Main {
void test24(String s) {
switch (s) {
case "hello", "world", <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, String str -> {}
case "hello", "world", <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a list of case patterns">String str</error> -> {}
}
}
void test25(String s) {
switch (s) {
case "hello", "world", String str, <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error> -> {}
case "hello", "world", <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a list of case patterns">String str</error>, <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error> -> {}
}
}

View File

@@ -40,7 +40,7 @@ class Foo {
case Triangle t when t.calculateArea() > 100:
System.out.println("Large triangle");
break;
case <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, Triangle t:
case <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a single case pattern">Triangle t</error>:
System.out.println("Small triangle or null");
break;
case Color c:
@@ -59,7 +59,7 @@ class Foo {
int foo3(Integer i) {
return switch (i) {
case 1, 2, 3, 4, 5, <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, default -> 42;
case 1, 2, 3, 4, 5, <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, <error descr="Default label not allowed here: 'default' can only be used as a single case label or paired only with 'null'">default</error> -> 42;
case 42 -> 666;
};
}
@@ -121,7 +121,7 @@ class Bar extends Foo {
case Triangle t when t.calculateArea() > 100:
System.out.println("Large triangle");
break;
case <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, Triangle t:
case <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a single case pattern">Triangle t</error>:
System.out.println("Small triangle or null");
break;
case Color c:
@@ -141,7 +141,7 @@ class Bar extends Foo {
@Override
int <warning descr="Method 'foo3()' is identical to its super method">foo3</warning>(Integer i) {
return switch (i) {
case <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, 4, default, 1, 5, 3, 2 -> 42;
case <error descr="Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'">null</error>, 4, <error descr="Default label not allowed here: 'default' can only be used as a single case label or paired only with 'null'">default</error>, 1, 5, 3, 2 -> 42;
case 42 -> 666;
};
}