mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
[java-highlighting] IDEA-326939 Support multi-pattern switch labels that define no variables
GitOrigin-RevId: fb0360f00314417c17566637db6bbda4c21e6faa
This commit is contained in:
committed by
intellij-monorepo-bot
parent
fdcb966cf3
commit
5618627fa4
@@ -949,18 +949,30 @@ public class SwitchBlockHighlightingModel {
|
||||
if (problem != null) {
|
||||
addIllegalFallThroughError(problem.element(), problem.message(), holder, alreadyFallThroughElements);
|
||||
}
|
||||
else if (JavaPsiPatternUtil.containsPatternVariable(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;
|
||||
else {
|
||||
if (elements.length > 1) {
|
||||
for (int i = 0; i < elements.length - 1; i++) {
|
||||
if (elements[i] instanceof PsiPatternGuard guard) {
|
||||
PsiExpression expression = guard.getGuardingExpression();
|
||||
if (expression != null) {
|
||||
holder.add(createError(expression, "Guard expression is allowed only after the last label element").create());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (PsiTreeUtil.skipWhitespacesAndCommentsForward(switchLabelElement) instanceof PsiSwitchLabelStatement) {
|
||||
addIllegalFallThroughError(first, "multiple.switch.labels", holder, alreadyFallThroughElements);
|
||||
}
|
||||
else if (PsiTreeUtil.skipWhitespacesAndCommentsBackward(switchLabelElement) instanceof PsiSwitchLabelStatement) {
|
||||
addIllegalFallThroughError(first, "multiple.switch.labels", holder, alreadyFallThroughElements);
|
||||
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) {
|
||||
addIllegalFallThroughError(first, "multiple.switch.labels", holder, alreadyFallThroughElements);
|
||||
}
|
||||
else if (PsiTreeUtil.skipWhitespacesAndCommentsBackward(switchLabelElement) instanceof PsiSwitchLabelStatement) {
|
||||
addIllegalFallThroughError(first, "multiple.switch.labels", holder, alreadyFallThroughElements);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1021,9 +1033,15 @@ public class SwitchBlockHighlightingModel {
|
||||
}
|
||||
else if (firstElement instanceof PsiPattern || firstElement instanceof PsiPatternGuard) {
|
||||
if (elements[1] instanceof PsiPattern || elements[1] instanceof PsiPatternGuard) {
|
||||
return new CaseLabelCombinationProblem(elements[1], "invalid.case.label.combination.several.patterns");
|
||||
if (ContainerUtil.exists(elements, JavaPsiPatternUtil::containsNamedPatternVariable)) {
|
||||
String messageKey = HighlightingFeature.UNNAMED_PATTERNS_AND_VARIABLES.isAvailable(firstElement)
|
||||
? "invalid.case.label.combination.several.patterns.unnamed"
|
||||
: "invalid.case.label.combination.several.patterns";
|
||||
return new CaseLabelCombinationProblem(elements[1], messageKey);
|
||||
}
|
||||
} else {
|
||||
return new CaseLabelCombinationProblem(elements[1], "invalid.case.label.combination.constants.and.patterns");
|
||||
}
|
||||
return new CaseLabelCombinationProblem(elements[1], "invalid.case.label.combination.constants.and.patterns");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1050,7 +1068,7 @@ public class SwitchBlockHighlightingModel {
|
||||
PsiCaseLabelElementList labelElementList = switchLabel.getCaseLabelElementList();
|
||||
if (labelElementList == null) continue;
|
||||
List<PsiCaseLabelElement> patternElements = ContainerUtil.filter(labelElementList.getElements(),
|
||||
labelElement -> JavaPsiPatternUtil.containsPatternVariable(labelElement));
|
||||
labelElement -> JavaPsiPatternUtil.containsNamedPatternVariable(labelElement));
|
||||
if (patternElements.isEmpty()) continue;
|
||||
PsiStatement prevStatement = PsiTreeUtil.getPrevSiblingOfType(firstSwitchLabelInGroup, PsiStatement.class);
|
||||
if (prevStatement == null) continue;
|
||||
|
||||
@@ -807,7 +807,7 @@ public final class DuplicateBranchesInSwitchInspection extends LocalInspectionTo
|
||||
PsiCaseLabelElement[] elements = labelElementList.getElements();
|
||||
for (PsiCaseLabelElement element : elements) {
|
||||
if (element instanceof PsiPatternGuard ||
|
||||
element instanceof PsiPattern && (!java20plus || JavaPsiPatternUtil.containsPatternVariable(element))) {
|
||||
element instanceof PsiPattern && (!java20plus || JavaPsiPatternUtil.containsNamedPatternVariable(element))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,21 +165,22 @@ public final class JavaPsiPatternUtil {
|
||||
* @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) {
|
||||
public static boolean containsNamedPatternVariable(@Nullable PsiCaseLabelElement pattern) {
|
||||
if (pattern instanceof PsiPatternGuard) {
|
||||
return containsPatternVariable(((PsiPatternGuard)pattern).getPattern());
|
||||
return containsNamedPatternVariable(((PsiPatternGuard)pattern).getPattern());
|
||||
}
|
||||
else if (pattern instanceof PsiTypeTestPattern) {
|
||||
return ((PsiTypeTestPattern)pattern).getPatternVariable() != null;
|
||||
PsiPatternVariable variable = ((PsiTypeTestPattern)pattern).getPatternVariable();
|
||||
return variable != null && !variable.isUnnamed();
|
||||
}
|
||||
else if (pattern instanceof PsiParenthesizedPattern) {
|
||||
return containsPatternVariable(((PsiParenthesizedPattern)pattern).getPattern());
|
||||
return containsNamedPatternVariable(((PsiParenthesizedPattern)pattern).getPattern());
|
||||
}
|
||||
else if (pattern instanceof PsiDeconstructionPattern) {
|
||||
PsiDeconstructionPattern deconstructionPattern = (PsiDeconstructionPattern)pattern;
|
||||
return deconstructionPattern.getPatternVariable() != null ||
|
||||
ContainerUtil.exists(deconstructionPattern.getDeconstructionList().getDeconstructionComponents(),
|
||||
component -> containsPatternVariable(component));
|
||||
component -> containsNamedPatternVariable(component));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -239,7 +239,8 @@ valid.switch.selector.types=byte, char, short or int
|
||||
valid.switch.1_7.selector.types=char, byte, short, int, Character, Byte, Short, Integer, String, or an enum
|
||||
switch.illegal.fall.through.from=Illegal fall-through from a pattern
|
||||
switch.illegal.fall.through.to=Illegal fall-through to a pattern
|
||||
invalid.case.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
|
||||
invalid.case.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
|
||||
invalid.case.label.combination.several.patterns.unnamed=Invalid case label combination: Multiple patterns are allowed only if none of them declare any pattern variables
|
||||
invalid.case.label.combination.several.patterns=Invalid case label combination: A case label must not consist of more than one case pattern
|
||||
null.label.not.allowed.here=Invalid case label combination: 'null' can only be used as a single case label or paired only with 'default'
|
||||
default.label.must.not.contains.case.keyword=The label for the default case must only use the 'default' keyword, without 'case'
|
||||
|
||||
@@ -127,7 +127,7 @@ public class Main {
|
||||
|
||||
void test12(Integer integer) {
|
||||
switch (integer) {
|
||||
case 1, 2, <error descr="Invalid case label combination: A case label must consist of either a list of case constants or a single case pattern">Integer i1 when i1 > 5</error>:
|
||||
case 1, 2, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a single case pattern">Integer i1 when i1 > 5</error>:
|
||||
case null:
|
||||
System.out.println("blah blah blah");
|
||||
break;
|
||||
@@ -253,7 +253,7 @@ public class Main {
|
||||
|
||||
void test28(String s) {
|
||||
switch (s) {
|
||||
case String str, <error descr="Invalid case label combination: A case label must consist of either a list of case constants or a single case pattern">"hello"</error>, "world" -> {}
|
||||
case String str, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a single case pattern">"hello"</error>, "world" -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
class Test {
|
||||
enum X{A, B}
|
||||
|
||||
record R(int x, int y) {}
|
||||
record R1(int z) {}
|
||||
|
||||
void test(Object obj) {
|
||||
switch (obj) {
|
||||
case Integer _, String _ -> System.out.println("string or int");
|
||||
case R(_, _), R1 _ -> System.out.println("R or R1");
|
||||
default -> System.out.println("other");
|
||||
}
|
||||
}
|
||||
|
||||
void testRepeat(Object obj) {
|
||||
switch (obj) {
|
||||
case Integer _, String _ -> System.out.println("string or int");
|
||||
case Double _, <error descr="Label is dominated by a preceding case label 'Integer _'">Integer _</error> -> System.out.println("double or int");
|
||||
default -> System.out.println("other");
|
||||
}
|
||||
}
|
||||
|
||||
void testEnum(Object obj) {
|
||||
switch (obj) {
|
||||
case X.A, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a single case pattern">String _</error> -> System.out.println("string or int");
|
||||
case Integer _, <error descr="Invalid case label combination: a case label must consist of either a list of case constants or a single case pattern">X.B</error> -> System.out.println("string or int");
|
||||
default -> System.out.println("other");
|
||||
}
|
||||
}
|
||||
|
||||
void test2(Object obj) {
|
||||
switch (obj) {
|
||||
case Integer x, <error descr="Invalid case label combination: Multiple patterns are allowed only if none of them declare any pattern variables">String _</error> -> System.out.println("string or int");
|
||||
case R(_, var i), <error descr="Invalid case label combination: Multiple patterns are allowed only if none of them declare any pattern variables">R1 _</error> -> System.out.println("R or R1");
|
||||
default -> System.out.println("other");
|
||||
}
|
||||
}
|
||||
|
||||
void testGuards(Object obj) {
|
||||
switch (obj) {
|
||||
case Integer _ when <error descr="Guard expression is allowed only after the last label element">((Integer)obj) > 0</error>,
|
||||
String _ when !((String)obj).isEmpty() -> System.out.println("Positive integer or non-empty string");
|
||||
default -> System.out.println("other");
|
||||
}
|
||||
}
|
||||
|
||||
void testFallthrough(Object obj) {
|
||||
switch (obj) {
|
||||
case Integer _:
|
||||
case String _:
|
||||
System.out.println("Number or string");
|
||||
break;
|
||||
case Double _:
|
||||
case <error descr="Multiple switch labels are permitted for a switch labeled statement group only if none of them declare any pattern variables">Float f</error>:
|
||||
System.out.println("double or float");
|
||||
break;
|
||||
default:
|
||||
System.out.println("other");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,10 @@ public class LightPatternsForSwitchHighlightingTest extends LightJavaCodeInsight
|
||||
public void testSwitchExhaustivenessWithGenericsIn21Java() {
|
||||
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_21, this::doTest);
|
||||
}
|
||||
|
||||
public void testSwitchSeveralPatternsUnnamed() {
|
||||
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_21_PREVIEW, this::doTest);
|
||||
}
|
||||
|
||||
public void testSwitchDominanceIn21Java() {
|
||||
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_21, this::doTest);
|
||||
|
||||
Reference in New Issue
Block a user