[java switch resolve] IDEA-277110 Compilation error not highlighted when using Pattern Matching for switch

Fallthrough to default is not acceptable according to JEP 406. This patch eliminates the default cases from a special case rules and leaves only `case null` as the only special case rule.

GitOrigin-RevId: 06c865f92fed01a41c5c87e1aa0a852acb3e7ee0
This commit is contained in:
Nikita Eshkeev
2021-08-31 20:52:55 +00:00
committed by intellij-monorepo-bot
parent d280ec0766
commit ee954bc169
11 changed files with 137 additions and 30 deletions
@@ -107,7 +107,7 @@ public class PsiSwitchLabelStatementImpl extends PsiSwitchLabelStatementBaseImpl
immediateSwitchLabel = PsiTreeUtil.getPrevSiblingOfType(currentScope, PsiSwitchLabelStatementBase.class);
}
while (immediateSwitchLabel != null && isFallthrough(immediateSwitchLabel) && isSpecialCaseLabel(immediateSwitchLabel)) {
while (immediateSwitchLabel != null && isFallthrough(immediateSwitchLabel) && isCaseNull(immediateSwitchLabel)) {
immediateSwitchLabel = PsiTreeUtil.getPrevSiblingOfType(immediateSwitchLabel, PsiSwitchLabelStatementBase.class);
}
@@ -136,18 +136,12 @@ public class PsiSwitchLabelStatementImpl extends PsiSwitchLabelStatementBaseImpl
}
}
private static boolean isSpecialCaseLabel(@NotNull PsiSwitchLabelStatementBase switchCaseLabel) {
return isCase(switchCaseLabel, JavaTokenType.NULL_KEYWORD) ||
isCase(switchCaseLabel, JavaTokenType.DEFAULT_KEYWORD) ||
switchCaseLabel.isDefaultCase();
}
private static boolean isCaseNull(@NotNull PsiSwitchLabelStatementBase switchCaseLabel) {
if (switchCaseLabel.getCaseLabelElementList() == null) return false;
private static boolean isCase(@NotNull PsiSwitchLabelStatementBase item, @NotNull IElementType keyword) {
if (item.getCaseLabelElementList() == null) return false;
final PsiCaseLabelElement[] elements = item.getCaseLabelElementList().getElements();
final PsiCaseLabelElement[] elements = switchCaseLabel.getCaseLabelElementList().getElements();
if (elements.length != 1) return false;
return elements[0].getNode().getFirstChildNode().getElementType() == keyword;
return elements[0].getNode().getFirstChildNode().getElementType() == JavaTokenType.NULL_KEYWORD;
}
}
@@ -5,7 +5,9 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PsiSwitchLabeledRuleStatementImpl extends PsiSwitchLabelStatementBaseImpl implements PsiSwitchLabeledRuleStatement {
private static final TokenSet BODY_STATEMENTS =
@@ -47,8 +49,37 @@ public class PsiSwitchLabeledRuleStatementImpl extends PsiSwitchLabelStatementBa
// Do not resolve references that come from the list of elements in this case rule
if (lastParent instanceof PsiCaseLabelElementList) return true;
if (!shouldProcess()) return true;
final PsiCaseLabelElementList patternsInCaseLabel = getCaseLabelElementList();
if (patternsInCaseLabel == null) return true;
return patternsInCaseLabel.processDeclarations(processor, state, null, place);
}
private boolean shouldProcess() {
final PsiCaseLabelElementList elementList = getCaseLabelElementList();
if (elementList == null) return false;
final PsiCaseLabelElement[] elements = elementList.getElements();
if (elements.length == 1) return true;
else if (elements.length > 2 || elements.length == 0) return false;
final PsiElement first = stripParensIfNecessary(elements[0]);
final PsiElement second = stripParensIfNecessary(elements[1]);
if (first == null || second == null) return true;
return firstPatternVariableSecondNull(first, second) || firstPatternVariableSecondNull(second, first);
}
private static boolean firstPatternVariableSecondNull(PsiElement first, @NotNull PsiElement second) {
return first instanceof PsiTypeTestPattern &&
second.getNode().getFirstChildNode().getElementType() == JavaTokenType.NULL_KEYWORD;
}
private static @Nullable PsiElement stripParensIfNecessary(@NotNull PsiCaseLabelElement element) {
return element instanceof PsiExpression
? PsiUtil.skipParenthesizedExprDown((PsiExpression)element)
: element;
}
}
@@ -4,9 +4,9 @@ class Main {
switch (o) {
case Integer i :
System.out.println();
case default:
case null:
System.out.println(i);
case default:
};
}
@@ -0,0 +1,38 @@
class Main {
void f(Object o) {
switch (o) {
case Integer i :
System.out.println(i);
break;
case String s:
default:
System.out.println(<error descr="Cannot resolve symbol 's'">s</error>);
}
}
void g(Object o) {
switch (o) {
case Integer i :
System.out.println(i);
break;
case String s:
case default:
System.out.println(<error descr="Cannot resolve symbol 's'">s</error>);
}
}
void ff(Object o) {
switch (o) {
case Integer i -> System.out.println(i);
case String s, default -> System.out.println(<error descr="Cannot resolve symbol 's'">s</error>);
}
}
void gg(Object o) {
switch (o) {
case Integer i -> System.out.println(i);
case default, String s -> System.out.println(<error descr="Cannot resolve symbol 's'">s</error>);
}
}
}
@@ -21,17 +21,17 @@ class Main {
};
int i4 = switch(o) {
case String s, <error descr="Illegal fall-through to a pattern">X x</error> -> x.f();
case String s, X x -> <error descr="Cannot resolve symbol 'x'">x</error>.f();
default -> 1;
};
int i5 = switch(o) {
case String s, <error descr="Illegal fall-through to a pattern">X x</error> -> x.f();
case String s, X x -> <error descr="Cannot resolve symbol 'x'">x</error>.f();
default -> 1;
};
int i6 = switch(o) {
case X x, <error descr="Illegal fall-through to a pattern">String s</error> -> x.f();
case X x, String s -> <error descr="Cannot resolve symbol 'x'">x</error>.f();
default -> 1;
};
return i1 + i2 + i3 + i4 + i5 + i6;
@@ -4,9 +4,6 @@ class Main {
switch (o) {
case Integer i :
System.out.println();
case null:
default: {}
case default:
case null:
System.out.println(i<caret>);
};
@@ -0,0 +1,8 @@
class Main {
private static final int i = 0;
private void f(Object o) {
switch (o) {
case Integer i, null -> System.out.println(i<caret>);
};
}
}
@@ -0,0 +1,11 @@
class Main {
private static final int i = 0;
private void f(Object o) {
switch (o) {
case Integer i :
System.out.println();
default:
System.out.println(i<caret>);
};
}
}
@@ -0,0 +1,8 @@
class Main {
private static final int i = 0;
private void f(Object o) {
switch (o) {
case Integer i, default -> System.out.println(i<caret>);
};
}
}
@@ -99,6 +99,10 @@ public class LightPatternsForSwitchHighlightingTest extends LightJavaCodeInsight
doTest();
}
public void testFallthroughDefault() {
doTest();
}
public void testUnusedPatternVariable() {
myFixture.enableInspections(new UnusedDeclarationInspection());
doTest();
@@ -121,38 +121,47 @@ public class GotoDeclarationTest extends LightJavaCodeInsightTestCase {
}
public void testPatternMatchingGuardInSwitchExpression() {
doTestPatternMatchingGuard();
doTestGoToField();
}
public void testPatternMatchingGuardInSwitchStatement() {
doTestPatternMatchingGuard();
doTestGoToField();
}
public void testPatternMatchingWithParensAroundReference() {
doTestPatternMatchingGuard();
doTestGoToField();
}
public void testReferenceFieldInPatternMatchingInSwitchStatement() {
doTestPatternMatchingGuard();
doTestGoToField();
}
public void testCaseNullDefaultAfterPatternMatching() {
configure();
final PsiElement patternVariable = PsiTreeUtil.findChildOfType(getFile(), PsiPatternVariable.class);
final PsiElement element = GotoDeclarationAction.findTargetElement(getProject(), getEditor(), getEditor().getCaretModel().getOffset());
assertThat(element).isEqualTo(patternVariable);
public void testCaseNullAfterPatternMatching() {
doTestGoToPatternVariable();
}
public void testCaseNullAfterPatternMatchingExpr() {
doTestGoToPatternVariable();
}
public void testDefaultAfterPatternMatching() {
doTestGoToField();
}
public void testDefaultAfterPatternMatchingExpr() {
doTestGoToField();
}
public void testGuardWithInstanceOfPatternMatchingInIf() {
doTestGoToPatternVariable();
doTestGoToSecondPatternVariable();
}
public void testGuardWithInstanceOfPatternMatchingInSwitch() {
doTestGoToPatternVariable();
doTestGoToSecondPatternVariable();
}
private void doTestPatternMatchingGuard() {
private void doTestGoToField() {
configure();
final PsiField field = PsiTreeUtil.findChildOfType(getFile(), PsiField.class);
final PsiElement element = GotoDeclarationAction.findTargetElement(getProject(), getEditor(), getEditor().getCaretModel().getOffset());
@@ -160,6 +169,13 @@ public class GotoDeclarationTest extends LightJavaCodeInsightTestCase {
}
private void doTestGoToPatternVariable() {
configure();
final PsiPatternVariable patternVariable = PsiTreeUtil.findChildOfType(getFile(), PsiPatternVariable.class);
final PsiElement element = GotoDeclarationAction.findTargetElement(getProject(), getEditor(), getEditor().getCaretModel().getOffset());
assertThat(element).isEqualTo(patternVariable);
}
private void doTestGoToSecondPatternVariable() {
configure();
final Iterator<PsiPatternVariable> iterator = PsiTreeUtil.findChildrenOfType(getFile(), PsiPatternVariable.class).iterator();
iterator.next();