diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 797cfc16da60..b57f147bf61a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -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()); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 24842d35eb92..338929a91765 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -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); diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index a8ff9e9217c9..4cc3bec8b662 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -378,8 +378,13 @@ public final class PsiUtil extends PsiUtilCore { private static void collectSwitchResultExpressions(@NotNull List result, @NotNull PsiElement container) { List 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 yields = new ArrayList<>(); + addStatements(yields, container, PsiYieldStatement.class, element -> false); + for (PsiYieldStatement statement : yields) { + ContainerUtil.addIfNotNull(result, statement.getExpression()); } } diff --git a/java/java-psi-api/src/messages/JavaCoreBundle.properties b/java/java-psi-api/src/messages/JavaCoreBundle.properties index e5ccffb0580a..b09efaf70143 100644 --- a/java/java-psi-api/src/messages/JavaCoreBundle.properties +++ b/java/java-psi-api/src/messages/JavaCoreBundle.properties @@ -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 diff --git a/java/java-psi-impl/src/com/intellij/lang/java/lexer/JavaLexer.java b/java/java-psi-impl/src/com/intellij/lang/java/lexer/JavaLexer.java index 8bb8879d840e..be8325a1b6f9 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/lexer/JavaLexer.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/lexer/JavaLexer.java @@ -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; diff --git a/java/java-psi-impl/src/messages/JavaErrorMessages.properties b/java/java-psi-impl/src/messages/JavaErrorMessages.properties index 22c0fe0c203e..6e5808814ea2 100644 --- a/java/java-psi-impl/src/messages/JavaErrorMessages.properties +++ b/java/java-psi-impl/src/messages/JavaErrorMessages.properties @@ -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 diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/YieldStatements.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/YieldStatements.java new file mode 100644 index 000000000000..62801632720f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/YieldStatements.java @@ -0,0 +1,40 @@ +class YieldStatements { + void m(int i) { + System.out.println(switch (i) { + case 0: yield "zero"; + default: break "many"; + }); + + switch (i) { + default: yield 0; + } + System.out.println(switch (i) { + default: + Runnable r = () -> { yield 0; }; + 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: break; + case 1: break i; + default: break out; + }); + } + + System.out.println(switch (i) { + default: yield m(0); + }); + + int yield = i; + i = switch (i) { + default: yield yield; + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavaSwitchExpressionsHighlightingTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavaSwitchExpressionsHighlightingTest.kt index 4764a7f5632b..f43a83695f40 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavaSwitchExpressionsHighlightingTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/JavaSwitchExpressionsHighlightingTest.kt @@ -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() diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ControlFlowUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ControlFlowUtils.java index 264762b871b3..41683d931902 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ControlFlowUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ControlFlowUtils.java @@ -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; }