[java] 'yield' statement: basic highlighting (IDEA-216410)

GitOrigin-RevId: 4edae910f16a27c2a46068c2d8b6fce4674e0571
This commit is contained in:
Roman Shevchenko
2019-07-02 06:52:16 +03:00
committed by intellij-monorepo-bot
parent 6941d27505
commit 06d3d41999
9 changed files with 111 additions and 9 deletions
@@ -809,11 +809,23 @@ public class HighlightUtil extends HighlightUtilBase {
}
@Nullable
static HighlightInfo checkValueBreakExpression(@NotNull PsiBreakStatement statement, @Nullable PsiExpression expression) {
static HighlightInfo checkValueBreakExpression(@NotNull PsiBreakStatement statement,
@Nullable PsiExpression expression,
@NotNull LanguageLevel languageLevel) {
PsiElement enclosing = PsiImplUtil.findEnclosingSwitchOrLoop(statement);
boolean plainRef = PsiImplUtil.isUnqualifiedReference(expression);
if (enclosing instanceof PsiSwitchExpression) {
if (languageLevel == LanguageLevel.JDK_13_PREVIEW) {
if (expression == null || plainRef && ((PsiReferenceExpression)expression).resolve() instanceof PsiLabeledStatement) {
String message = JavaErrorMessages.message("break.outside.switch.expr");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
}
else {
String message = "Value breaks are superseded by 'yield' statements";
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
}
}
if (expression == null) {
String message = JavaErrorMessages.message("value.break.missing");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
@@ -831,6 +843,27 @@ public class HighlightUtil extends HighlightUtilBase {
return null;
}
@Nullable
static HighlightInfo checkYieldOutsideSwitchExpression(@NotNull PsiYieldStatement statement) {
if (statement.findEnclosingExpression() == null) {
String message = JavaErrorMessages.message("yield.unexpected");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
}
return null;
}
@Nullable
static HighlightInfo checkYieldExpressionType(@NotNull PsiYieldStatement statement) {
PsiExpression expression = statement.getExpression();
if (expression != null && PsiType.VOID.equals(expression.getType())) {
String message = JavaErrorMessages.message("yield.void");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message).create();
}
return null;
}
@Nullable
static HighlightInfo checkContinueOutsideLoop(@NotNull PsiContinueStatement statement, LanguageLevel languageLevel) {
if (PsiImplUtil.findEnclosingLoop(statement) == null) {
@@ -3171,7 +3204,7 @@ public class HighlightUtil extends HighlightUtilBase {
}
private boolean isSufficient(LanguageLevel useSiteLevel) {
return level.isPreview() ? useSiteLevel == level : useSiteLevel.isAtLeast(level);
return useSiteLevel.isAtLeast(level) && (!level.isPreview() || useSiteLevel.isPreview());
}
}
@@ -414,10 +414,17 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myHolder.add(HighlightUtil.checkBreakOutsideSwitchOrLoop(statement));
}
if (!myHolder.hasErrorResults() && Feature.ENHANCED_SWITCH.isAvailable(myFile)) {
myHolder.add(HighlightUtil.checkValueBreakExpression(statement, expression));
myHolder.add(HighlightUtil.checkValueBreakExpression(statement, expression, myLanguageLevel));
}
}
@Override
public void visitYieldStatement(PsiYieldStatement statement) {
super.visitYieldStatement(statement);
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkYieldOutsideSwitchExpression(statement));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkYieldExpressionType(statement));
}
@Override
public void visitClass(PsiClass aClass) {
super.visitClass(aClass);
@@ -378,8 +378,13 @@ public final class PsiUtil extends PsiUtilCore {
private static void collectSwitchResultExpressions(@NotNull List<? super PsiExpression> result, @NotNull PsiElement container) {
List<PsiBreakStatement> breaks = new ArrayList<>();
addStatements(breaks, container, PsiBreakStatement.class, element -> element instanceof PsiSwitchBlock);
for (PsiBreakStatement aBreak : breaks) {
ContainerUtil.addIfNotNull(result, aBreak.getExpression());
for (PsiBreakStatement statement : breaks) {
ContainerUtil.addIfNotNull(result, statement.getExpression());
}
List<PsiYieldStatement> yields = new ArrayList<>();
addStatements(yields, container, PsiYieldStatement.class, element -> false);
for (PsiYieldStatement statement : yields) {
ContainerUtil.addIfNotNull(result, statement.getExpression());
}
}
@@ -13,5 +13,5 @@ jdk.11.language.level.description=11 - Local variable syntax for lambda paramete
jdk.12.language.level.description=12 - No new language features
jdk.12.preview.language.level.description=12 (Preview) - Switch expressions
jdk.13.language.level.description=13 - No new language features
jdk.13.preview.language.level.description=13 (Preview) - Text blocks
jdk.13.preview.language.level.description=13 (Preview) - Switch expressions, text blocks
jdk.X.language.level.description=X - Experimental features
@@ -38,7 +38,8 @@ public class JavaLexer extends LexerBase {
public static boolean isSoftKeyword(CharSequence id, @NotNull LanguageLevel level) {
return id != null &&
(level.isAtLeast(LanguageLevel.JDK_1_9) && JAVA9_KEYWORDS.contains(id) ||
level.isAtLeast(LanguageLevel.JDK_10) && VAR.contentEquals(id));
level.isAtLeast(LanguageLevel.JDK_10) && VAR.contentEquals(id) ||
level.isAtLeast(LanguageLevel.JDK_13_PREVIEW) && YIELD.contentEquals(id));
}
private final _JavaLexer myFlexLexer;
@@ -233,6 +233,8 @@ variable.already.defined=Variable ''{0}'' is already defined in the scope
break.outside.switch.or.loop=Break outside switch or loop
value.break.unexpected=Value break outside switch expression
value.break.missing=Missing break value
yield.unexpected=Yield outside of switch expression
yield.void=Expression type should not be 'void'
break.outside.switch.expr=Break outside of enclosing switch expression
continue.outside.loop=Continue outside of loop
continue.outside.switch.expr=Continue outside of enclosing switch expression
@@ -0,0 +1,40 @@
class YieldStatements {
void m(int i) {
System.out.println(switch (i) {
case 0: yield "zero";
default: <error descr="Value breaks are superseded by 'yield' statements">break "many";</error>
});
switch (i) {
default: <error descr="Yield outside of switch expression">yield 0;</error>
}
System.out.println(switch (i) {
default:
Runnable r = () -> { <error descr="Yield outside of switch expression">yield 0;</error> };
r.run();
yield 0;
});
System.out.println(switch (i) {
default: switch (i) {
default: yield "0";
}
});
out: while (true) {
System.out.println(switch (i) {
case 0: <error descr="Break outside of enclosing switch expression">break;</error>
case 1: <error descr="Value breaks are superseded by 'yield' statements">break i;</error>
default: <error descr="Break outside of enclosing switch expression">break out;</error>
});
}
System.out.println(switch (i) {
default: yield <error descr="Expression type should not be 'void'">m(0)</error>;
});
int yield = i;
i = switch (i) {
default: yield yield;
};
}
}
@@ -1,7 +1,10 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInsight.daemon
import com.intellij.JavaTestUtil
import com.intellij.openapi.roots.LanguageLevelModuleExtension
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
class JavaSwitchExpressionsHighlightingTest : LightJavaCodeInsightFixtureTestCase() {
@@ -18,6 +21,17 @@ class JavaSwitchExpressionsHighlightingTest : LightJavaCodeInsightFixtureTestCas
fun testEnhancedSwitchUnreachable() = doTest()
fun testSwitchExpressionHasResult() = doTest()
fun testYieldStatements() = try {
level(LanguageLevel.JDK_13_PREVIEW)
doTest()
}
finally {
level(LanguageLevel.JDK_12_PREVIEW)
}
private fun level(level: LanguageLevel) =
ModuleRootModificationUtil.updateModel(module) { it.getModuleExtension(LanguageLevelModuleExtension::class.java).languageLevel = level }
private fun doTest() {
myFixture.configureByFile("${getTestName(false)}.java")
myFixture.checkHighlighting()
@@ -60,7 +60,7 @@ public class ControlFlowUtils {
if (statement == null) {
return true;
}
if (statement instanceof PsiBreakStatement || statement instanceof PsiContinueStatement ||
if (statement instanceof PsiBreakStatement || statement instanceof PsiContinueStatement || statement instanceof PsiYieldStatement ||
statement instanceof PsiReturnStatement || statement instanceof PsiThrowStatement) {
return false;
}