mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java] switch expressions: basic highlighting (IDEA-196643)
This commit is contained in:
+47
-15
@@ -1353,8 +1353,8 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
|
||||
|
||||
@Nullable
|
||||
static HighlightInfo checkSwitchSelectorType(@NotNull PsiSwitchStatement statement, @NotNull LanguageLevel level) {
|
||||
PsiExpression expression = statement.getExpression();
|
||||
static HighlightInfo checkSwitchSelectorType(@NotNull PsiSwitchBlock switchBlock, @NotNull LanguageLevel level) {
|
||||
PsiExpression expression = switchBlock.getExpression();
|
||||
if (expression == null) return null;
|
||||
PsiType type = expression.getType();
|
||||
if (type == null) return null;
|
||||
@@ -1371,7 +1371,9 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
String expected = JavaErrorMessages.message(is7 ? "valid.switch.17.selector.types" : "valid.switch.selector.types");
|
||||
String message = JavaErrorMessages.message("incompatible.types", expected, JavaHighlightUtil.formatType(type));
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createConvertSwitchToIfIntention(statement));
|
||||
if (switchBlock instanceof PsiSwitchStatement) {
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createConvertSwitchToIfIntention((PsiSwitchStatement)switchBlock));
|
||||
}
|
||||
if (PsiType.LONG.equals(type) || PsiType.FLOAT.equals(type) || PsiType.DOUBLE.equals(type)) {
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddTypeCastFix(PsiType.INT, expression));
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createWrapWithAdapterFix(PsiType.INT, expression));
|
||||
@@ -1845,8 +1847,8 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
|
||||
@Nullable
|
||||
static HighlightInfo checkCaseStatement(@NotNull PsiSwitchLabelStatementBase statement) {
|
||||
PsiSwitchStatement switchStatement = statement.getEnclosingSwitchStatement();
|
||||
if (switchStatement == null) {
|
||||
PsiSwitchBlock switchBlock = statement.getEnclosingSwitchBlock();
|
||||
if (switchBlock == null) {
|
||||
String description = JavaErrorMessages.message("case.statement.outside.switch");
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(description).create();
|
||||
}
|
||||
@@ -1855,15 +1857,16 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Collection<HighlightInfo> checkSwitchLabelValues(@NotNull PsiSwitchStatement switchStatement) {
|
||||
PsiCodeBlock body = switchStatement.getBody();
|
||||
static Collection<HighlightInfo> checkSwitchLabelValues(@NotNull PsiSwitchBlock switchBlock) {
|
||||
PsiCodeBlock body = switchBlock.getBody();
|
||||
if (body == null) return null;
|
||||
|
||||
PsiExpression switchExpression = switchStatement.getExpression();
|
||||
PsiType switchType = switchExpression == null ? PsiType.INT : switchExpression.getType();
|
||||
PsiExpression selectorExpression = switchBlock.getExpression();
|
||||
PsiType selectorType = selectorExpression == null ? PsiType.INT : selectorExpression.getType();
|
||||
MultiMap<Object, PsiElement> values = new MultiMap<>();
|
||||
Object defaultValue = new Object();
|
||||
Collection<HighlightInfo> results = new ArrayList<>();
|
||||
boolean hasDefaultCase = false;
|
||||
|
||||
for (PsiStatement st : body.getStatements()) {
|
||||
if (!(st instanceof PsiSwitchLabelStatementBase)) continue;
|
||||
@@ -1872,13 +1875,14 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
|
||||
if (defaultCase) {
|
||||
values.putValue(defaultValue, ObjectUtils.notNull(labelStatement.getFirstChild(), labelStatement));
|
||||
hasDefaultCase = true;
|
||||
}
|
||||
else {
|
||||
PsiExpressionList expressionList = labelStatement.getCaseValues();
|
||||
if (expressionList != null) {
|
||||
for (PsiExpression expr : expressionList.getExpressions()) {
|
||||
if (switchExpression != null) {
|
||||
HighlightInfo result = checkAssignability(switchType, expr.getType(), expr, expr);
|
||||
if (selectorExpression != null) {
|
||||
HighlightInfo result = checkAssignability(selectorType, expr.getType(), expr, expr);
|
||||
if (result != null) {
|
||||
results.add(result);
|
||||
continue;
|
||||
@@ -1898,7 +1902,7 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
}
|
||||
}
|
||||
if (value == null) {
|
||||
value = ConstantExpressionUtil.computeCastTo(expr, switchType);
|
||||
value = ConstantExpressionUtil.computeCastTo(expr, selectorType);
|
||||
}
|
||||
if (value == null) {
|
||||
String description = JavaErrorMessages.message("constant.expression.required");
|
||||
@@ -1922,6 +1926,33 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (results.isEmpty() && switchBlock instanceof PsiSwitchExpression) {
|
||||
if (values.isEmpty()) {
|
||||
String message = JavaErrorMessages.message("switch.expr.empty");
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(switchBlock).descriptionAndTooltip(message).create());
|
||||
}
|
||||
else if (!hasDefaultCase) {
|
||||
if (selectorType instanceof PsiClassType) {
|
||||
PsiClass type = ((PsiClassType)selectorType).resolve();
|
||||
if (type != null && type.isEnum()) {
|
||||
Set<Object> constants = new HashSet<>();
|
||||
for (PsiField field : type.getFields()) {
|
||||
if (field instanceof PsiEnumConstant) {
|
||||
constants.add(field.getName());
|
||||
}
|
||||
}
|
||||
constants.removeAll(values.keySet());
|
||||
hasDefaultCase = constants.isEmpty();
|
||||
}
|
||||
}
|
||||
if (!hasDefaultCase) {
|
||||
PsiElement range = ObjectUtils.notNull(selectorExpression, switchBlock);
|
||||
String message = JavaErrorMessages.message("switch.expr.incomplete");
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(message).create());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -2416,10 +2447,10 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
|
||||
|
||||
@Nullable
|
||||
static HighlightInfo checkSwitchBlockStatements(@NotNull PsiSwitchStatement switchStatement,
|
||||
static HighlightInfo checkSwitchBlockStatements(@NotNull PsiSwitchBlock switchBlock,
|
||||
@NotNull LanguageLevel languageLevel,
|
||||
@NotNull PsiFile file) {
|
||||
PsiCodeBlock body = switchStatement.getBody();
|
||||
PsiCodeBlock body = switchBlock.getBody();
|
||||
if (body != null) {
|
||||
PsiElement first = PsiTreeUtil.skipWhitespacesAndCommentsForward(body.getLBrace());
|
||||
if (first != null && !(first instanceof PsiSwitchLabelStatementBase) && !PsiUtil.isJavaToken(first, JavaTokenType.RBRACE)) {
|
||||
@@ -2968,7 +2999,8 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
REFS_AS_RESOURCE(LanguageLevel.JDK_1_9, "feature.try.with.resources.refs"),
|
||||
MODULES(LanguageLevel.JDK_1_9, "feature.modules"),
|
||||
RAW_LITERALS(LanguageLevel.JDK_12_PREVIEW, "feature.raw.literals"),
|
||||
ENHANCED_SWITCH(LanguageLevel.JDK_12_PREVIEW, "feature.enhanced.switch");
|
||||
ENHANCED_SWITCH(LanguageLevel.JDK_12_PREVIEW, "feature.enhanced.switch"),
|
||||
SWITCH_EXPRESSION(LanguageLevel.JDK_12_PREVIEW, "feature.switch.expressions");
|
||||
|
||||
private final LanguageLevel level;
|
||||
private final String key;
|
||||
|
||||
+14
-3
@@ -1591,9 +1591,20 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
@Override
|
||||
public void visitSwitchStatement(PsiSwitchStatement statement) {
|
||||
super.visitSwitchStatement(statement);
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkSwitchBlockStatements(statement, myLanguageLevel, myFile));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkSwitchSelectorType(statement, myLanguageLevel));
|
||||
if (!myHolder.hasErrorResults()) myHolder.addAll(HighlightUtil.checkSwitchLabelValues(statement));
|
||||
checkSwitchBlock(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSwitchExpression(PsiSwitchExpression expression) {
|
||||
super.visitSwitchExpression(expression);
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(checkFeature(expression, Feature.SWITCH_EXPRESSION));
|
||||
checkSwitchBlock(expression);
|
||||
}
|
||||
|
||||
private void checkSwitchBlock(PsiSwitchBlock switchBlock) {
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkSwitchBlockStatements(switchBlock, myLanguageLevel, myFile));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkSwitchSelectorType(switchBlock, myLanguageLevel));
|
||||
if (!myHolder.hasErrorResults()) myHolder.addAll(HighlightUtil.checkSwitchLabelValues(switchBlock));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ public class EnclosingLoopOrSwitchMatcherExpression extends EnclosingLoopMatcher
|
||||
|
||||
@Override
|
||||
public Boolean match(PsiElement element) {
|
||||
if (element instanceof PsiSwitchStatement) return Boolean.TRUE;
|
||||
if (element instanceof PsiSwitchBlock) return Boolean.TRUE;
|
||||
return super.match(element);
|
||||
}
|
||||
}
|
||||
@@ -396,7 +396,8 @@ public final class PsiUtil extends PsiUtilCore {
|
||||
}
|
||||
|
||||
if (element instanceof PsiExpressionStatement) {
|
||||
return isStatement(((PsiExpressionStatement)element).getExpression());
|
||||
return parent instanceof PsiSwitchLabeledRuleStatement && ((PsiSwitchLabeledRuleStatement)parent).getEnclosingSwitchBlock() instanceof PsiSwitchExpression ||
|
||||
isStatement(((PsiExpressionStatement)element).getExpression());
|
||||
}
|
||||
|
||||
if (element instanceof PsiDeclarationStatement) {
|
||||
|
||||
+4
-1
@@ -3,6 +3,7 @@ package com.intellij.psi.impl.source.tree.java;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.PsiImmediateClassType;
|
||||
import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.impl.source.tree.JavaSourceUtil;
|
||||
@@ -21,7 +22,9 @@ public class PsiSwitchExpressionImpl extends PsiSwitchBlockImpl implements PsiSw
|
||||
|
||||
@Override
|
||||
public PsiType getType() {
|
||||
return null; //tbd
|
||||
//todo[ann] http://cr.openjdk.java.net/~gbierman/switch-expressions.html#jep325-15.29.1
|
||||
PsiClass objClass = JavaPsiFacade.getInstance(getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, getResolveScope());
|
||||
return objClass != null ? new PsiImmediateClassType(objClass, PsiSubstitutor.EMPTY) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -262,6 +262,8 @@ constant.expression.required=Constant expression required
|
||||
duplicate.default.switch.label=Duplicate default label
|
||||
duplicate.switch.label=Duplicate label ''{0}''
|
||||
switch.colon.expected.after.case.label=':' expected
|
||||
switch.expr.empty='switch' expression does not have any case clauses
|
||||
switch.expr.incomplete='switch' expression does not cover all possible input values
|
||||
|
||||
illegal.forward.reference=Illegal forward reference
|
||||
illegal.self.reference=Illegal self reference
|
||||
@@ -478,4 +480,5 @@ feature.try.with.resources.refs=Resource references
|
||||
feature.modules=Modules
|
||||
feature.raw.literals=Raw string literals
|
||||
feature.enhanced.switch=Enhanced 'switch' blocks
|
||||
feature.switch.expressions='switch' expressions
|
||||
insufficient.language.level={0} are not supported at language level ''{1}''
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import java.util.Random;
|
||||
|
||||
class SwitchExpressions {
|
||||
enum E { E1, E2 }
|
||||
|
||||
void m() {
|
||||
System.out.println(switch (new Random().nextInt()) {
|
||||
default -> "whatever";
|
||||
});
|
||||
|
||||
System.out.println(<error descr="'switch' expression does not have any case clauses">switch (new Random().nextInt()) { }</error>);
|
||||
|
||||
System.out.println(switch (new Random().nextInt()) {
|
||||
case 0 -> throw new IllegalStateException("no args");
|
||||
<error descr="Different case kinds used in the switch">case 1:</error> break;
|
||||
});
|
||||
|
||||
System.out.println(
|
||||
switch (<error descr="Incompatible types. Found: 'java.lang.Object', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum'">new Object()</error>) {
|
||||
default -> "whatever";
|
||||
}
|
||||
);
|
||||
|
||||
System.out.println(switch (E.valueOf("E1")) {
|
||||
case <error descr="Constant expression required">null</error> -> 0;
|
||||
case <error descr="An enum switch case label must be the unqualified name of an enumeration constant">E.E1</error> -> 1;
|
||||
case E2 -> 2;
|
||||
case <error descr="Incompatible types. Found: 'int', required: 'SwitchExpressions.E'">1</error> -> 1;
|
||||
});
|
||||
|
||||
System.out.println(switch (new Random().nextInt()) {
|
||||
<error descr="Duplicate default label">default</error> -> -1;
|
||||
case 1 -> 1;
|
||||
<error descr="Duplicate default label">default</error> -> 0;
|
||||
});
|
||||
|
||||
System.out.println(switch (<error descr="'switch' expression does not cover all possible input values">new Random().nextInt()</error>) {
|
||||
case 1 -> 1;
|
||||
});
|
||||
System.out.println(switch (<error descr="'switch' expression does not cover all possible input values">E.valueOf("E1")</error>) {
|
||||
case E1 -> 1;
|
||||
});
|
||||
System.out.println(switch (E.valueOf("E1")) {
|
||||
case E1 -> 1;
|
||||
case E2 -> 2;
|
||||
});
|
||||
}
|
||||
}
|
||||
+3
@@ -32,6 +32,9 @@ class UnsupportedFeatures {
|
||||
switch (list.size()) {
|
||||
case <error descr="Enhanced 'switch' blocks are not supported at language level '6'">1, 2</error>:
|
||||
}
|
||||
System.out.println(<error descr="'switch' expressions are not supported at language level '6'">switch (list.size()) {
|
||||
default -> "whoa!";
|
||||
}</error>);
|
||||
}
|
||||
|
||||
void f(<error descr="Receiver parameters are not supported at language level '6'">Object this</error>) { }
|
||||
|
||||
+1
@@ -9,6 +9,7 @@ class LightJava12HighlightingTest : LightCodeInsightFixtureTestCase() {
|
||||
override fun getBasePath() = JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/advHighlighting12"
|
||||
|
||||
fun testEnhancedSwitchStatements() = doTest()
|
||||
fun testSwitchExpressions() = doTest()
|
||||
|
||||
private fun doTest() {
|
||||
myFixture.configureByFile(getTestName(false) + ".java")
|
||||
|
||||
Reference in New Issue
Block a user