diff --git a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties index 5d6f56505f02..4d22bccd1613 100644 --- a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties +++ b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties @@ -314,6 +314,8 @@ switch.expression.no.result=Switch expression does not have any result expressio switch.expression.should.produce.result=Switch expression should produce a result in all execution paths switch.expression.incompatible.type=Bad type in switch expression: {0} cannot be converted to {1} switch.expression.cannot.be.void=Target type for switch expression cannot be void +switch.label.expected=Statement must be prepended with a case label +switch.different.case.kinds=Different 'case' kinds used in 'switch' guard.misplaced=Guard is allowed after patterns only guard.evaluated.to.false=Case label has a guard that is a constant expression with value 'false' diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java index 6d5f6b78abf4..d62e9dea87ac 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java @@ -1729,4 +1729,63 @@ final class ExpressionChecker { } } } + + void checkSwitchBlockStatements(@NotNull PsiSwitchBlock block) { + PsiCodeBlock body = block.getBody(); + if (body == null) return; + PsiElement first = PsiTreeUtil.skipWhitespacesAndCommentsForward(body.getLBrace()); + if (first != null && !(first instanceof PsiSwitchLabelStatementBase) && !PsiUtil.isJavaToken(first, JavaTokenType.RBRACE)) { + myVisitor.report(JavaErrorKinds.SWITCH_LABEL_EXPECTED.create(first)); + } + PsiElement element = first; + PsiStatement alien = null; + boolean classicLabels = false; + boolean enhancedLabels = false; + boolean levelChecked = false; + while (element != null && !PsiUtil.isJavaToken(element, JavaTokenType.RBRACE)) { + if (element instanceof PsiSwitchLabeledRuleStatement) { + if (!levelChecked) { + myVisitor.checkFeature(element, JavaFeature.ENHANCED_SWITCH); + if (myVisitor.hasErrorResults()) return; + levelChecked = true; + } + if (classicLabels) { + alien = (PsiStatement)element; + break; + } + enhancedLabels = true; + } + else if (element instanceof PsiStatement statement) { + if (enhancedLabels) { + //let's not highlight twice + if (statement instanceof PsiSwitchLabelStatement labelStatement && + labelStatement.getChildren().length != 0 && + labelStatement.getChildren()[labelStatement.getChildren().length - 1] instanceof PsiErrorElement errorElement && + errorElement.getErrorDescription().startsWith(JavaPsiBundle.message("expected.colon.or.arrow"))) { + break; + } + alien = statement; + break; + } + classicLabels = true; + } + + if (!levelChecked && element instanceof PsiSwitchLabelStatementBase label) { + @Nullable PsiCaseLabelElementList values = label.getCaseLabelElementList(); + if (values != null && values.getElementCount() > 1) { + myVisitor.checkFeature(values, JavaFeature.ENHANCED_SWITCH); + if (myVisitor.hasErrorResults()) return; + levelChecked = true; + } + } + + element = PsiTreeUtil.skipWhitespacesAndCommentsForward(element); + } + if (alien == null) return; + if (enhancedLabels && !(alien instanceof PsiSwitchLabelStatementBase)) { + myVisitor.report(JavaErrorKinds.SWITCH_LABEL_EXPECTED.create(alien)); + return; + } + myVisitor.report(JavaErrorKinds.SWITCH_DIFFERENT_CASE_KINDS.create(alien)); + } } diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java index bc21fb7c91b4..ad1b9a223ecd 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java @@ -628,6 +628,17 @@ final class JavaErrorVisitor extends JavaElementVisitor { if (!hasErrorResults()) checkFeature(expression, JavaFeature.SWITCH_EXPRESSION); if (!hasErrorResults()) myExpressionChecker.checkSwitchExpressionReturnTypeCompatible(expression); if (!hasErrorResults()) myExpressionChecker.checkSwitchExpressionHasResult(expression); + checkSwitchBlock(expression); + } + + @Override + public void visitSwitchStatement(@NotNull PsiSwitchStatement statement) { + super.visitSwitchStatement(statement); + checkSwitchBlock(statement); + } + + private void checkSwitchBlock(@NotNull PsiSwitchBlock block) { + if (!hasErrorResults()) myExpressionChecker.checkSwitchBlockStatements(block); } @Override diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java index 5ec8c8bed227..c1ed2fb12099 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java @@ -923,6 +923,8 @@ public final class JavaErrorKinds { public static final Parameterized SWITCH_EXPRESSION_INCOMPATIBLE_TYPE = parameterized(PsiExpression.class, JavaIncompatibleTypeErrorContext.class, "switch.expression.incompatible.type") .withRawDescription((expr, context) -> message("switch.expression.incompatible.type", formatType(context.rType()), formatType(context.lType()))); + public static final Simple SWITCH_LABEL_EXPECTED = error(PsiElement.class, "switch.label.expected"); + public static final Simple SWITCH_DIFFERENT_CASE_KINDS = error("switch.different.case.kinds"); public static final Simple EXPRESSION_EXPECTED = error("expression.expected"); public static final Parameterized EXPRESSION_SUPER_UNQUALIFIED_DEFAULT_METHOD = 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 7823d6905bad..41b1a3681bf1 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 @@ -327,7 +327,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh private void checkSwitchBlock(@NotNull PsiSwitchBlock switchBlock) { SwitchBlockHighlightingModel model = SwitchBlockHighlightingModel.createInstance(myLanguageLevel, switchBlock, myFile); if (model == null) return; - if (!hasErrorResults()) model.checkSwitchBlockStatements(myErrorSink); if (!hasErrorResults()) model.checkSwitchSelectorType(myErrorSink); if (!hasErrorResults()) model.checkSwitchLabelValues(myErrorSink); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java index e4c827945e25..34010cbf978c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java @@ -223,6 +223,10 @@ final class JavaErrorFixProvider { error -> myFactory.createDeleteFix(error.psi(), QuickFixBundle.message("delete.unreachable.statement.fix.text"))); fix(STATEMENT_UNREACHABLE_LOOP_BODY, error -> myFactory.createSimplifyBooleanFix(error.psi(), false)); fix(FOREACH_NOT_APPLICABLE, error -> myFactory.createNotIterableForEachLoopFix(error.psi())); + fix(SWITCH_LABEL_EXPECTED, error -> { + PsiSwitchLabeledRuleStatement previousRule = PsiTreeUtil.getPrevSiblingOfType(error.psi(), PsiSwitchLabeledRuleStatement.class); + return previousRule == null ? null : myFactory.createWrapSwitchRuleStatementsIntoBlockFix(previousRule); + }); } private void createMethodFixes() { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/SwitchBlockHighlightingModel.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/SwitchBlockHighlightingModel.java index 7e14f0d7504b..7e459c3893c2 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/SwitchBlockHighlightingModel.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/SwitchBlockHighlightingModel.java @@ -7,7 +7,6 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper; -import com.intellij.core.JavaPsiBundle; import com.intellij.modcommand.ModCommandAction; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.NlsContexts; @@ -80,77 +79,6 @@ public class SwitchBlockHighlightingModel { return found.get(); } - void checkSwitchBlockStatements(@NotNull Consumer errorSink) { - PsiCodeBlock body = myBlock.getBody(); - if (body == null) return; - PsiElement first = PsiTreeUtil.skipWhitespacesAndCommentsForward(body.getLBrace()); - if (first != null && !(first instanceof PsiSwitchLabelStatementBase) && !PsiUtil.isJavaToken(first, JavaTokenType.RBRACE)) { - errorSink.accept(createError(first, JavaErrorBundle.message("statement.must.be.prepended.with.case.label"))); - } - PsiElement element = first; - PsiStatement alien = null; - boolean classicLabels = false; - boolean enhancedLabels = false; - boolean levelChecked = false; - while (element != null && !PsiUtil.isJavaToken(element, JavaTokenType.RBRACE)) { - if (element instanceof PsiSwitchLabeledRuleStatement) { - if (!levelChecked) { - HighlightInfo.Builder info = HighlightUtil.checkFeature(element, JavaFeature.ENHANCED_SWITCH, myLevel, myFile); - if (info != null) { - errorSink.accept(info); - return; - } - levelChecked = true; - } - if (classicLabels) { - alien = (PsiStatement)element; - break; - } - enhancedLabels = true; - } - else if (element instanceof PsiStatement statement) { - if (enhancedLabels) { - //let's not highlight twice - if (statement instanceof PsiSwitchLabelStatement labelStatement && - labelStatement.getChildren().length != 0 && - labelStatement.getChildren()[labelStatement.getChildren().length - 1] instanceof PsiErrorElement errorElement && - errorElement.getErrorDescription().startsWith(JavaPsiBundle.message("expected.colon.or.arrow"))) { - break; - } - alien = statement; - break; - } - classicLabels = true; - } - - if (!levelChecked && element instanceof PsiSwitchLabelStatementBase label) { - @Nullable PsiCaseLabelElementList values = label.getCaseLabelElementList(); - if (values != null && values.getElementCount() > 1) { - HighlightInfo.Builder info = HighlightUtil.checkFeature(values, JavaFeature.ENHANCED_SWITCH, myLevel, myFile); - if (info != null) { - errorSink.accept(info); - return; - } - levelChecked = true; - } - } - - element = PsiTreeUtil.skipWhitespacesAndCommentsForward(element); - } - if (alien == null) return; - if (enhancedLabels && !(alien instanceof PsiSwitchLabelStatementBase)) { - PsiSwitchLabeledRuleStatement previousRule = PsiTreeUtil.getPrevSiblingOfType(alien, PsiSwitchLabeledRuleStatement.class); - HighlightInfo.Builder info = createError(alien, JavaErrorBundle.message("statement.must.be.prepended.with.case.label")); - if (previousRule != null) { - IntentionAction action = getFixFactory().createWrapSwitchRuleStatementsIntoBlockFix(previousRule); - info.registerFix(action, null, null, null, null); - } - errorSink.accept(info); - return; - } - errorSink.accept(createError(alien, JavaErrorBundle.message("different.case.kinds.in.switch"))); - } - void checkSwitchSelectorType(@NotNull Consumer errorSink) { SelectorKind kind = getSwitchSelectorKind(); if (kind == SelectorKind.INT) return; diff --git a/java/java-psi-impl/resources/messages/JavaErrorBundle.properties b/java/java-psi-impl/resources/messages/JavaErrorBundle.properties index 8718c9397108..81fe2158b01d 100644 --- a/java/java-psi-impl/resources/messages/JavaErrorBundle.properties +++ b/java/java-psi-impl/resources/messages/JavaErrorBundle.properties @@ -97,8 +97,6 @@ identifier.is.not.allowed.here=Identifier is not allowed here illegal.forward.reference=Cannot read value of field ''{0}'' before the field''s definition unknown.class=Unknown class: ''{0}'' illegal.type.void=Illegal type: 'void' -statement.must.be.prepended.with.case.label=Statement must be prepended with case label -different.case.kinds.in.switch=Different 'case' kinds used in 'switch' numeric.overflow.in.expression=Numeric overflow in expression static.member.accessed.via.instance.reference=Static member ''{0}.{1}'' accessed via instance reference deprecated.symbol=''{0}'' is deprecated diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/SwitchStatement.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/SwitchStatement.java index b7a2bba5713c..32d7c7eda6ac 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/SwitchStatement.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/SwitchStatement.java @@ -11,15 +11,15 @@ class SwitchStatement { switch (0) { //////////////// /** */ - System.out.println(); + System.out.println(); } switch (0) { - break; + break; } switch (0) { - return; + return; } switch (0) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/EnhancedSwitchStatements.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/EnhancedSwitchStatements.java index 6b36aebdb16d..da08c6728925 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/EnhancedSwitchStatements.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/EnhancedSwitchStatements.java @@ -15,7 +15,7 @@ class EnhancedSwitchStatements { switch (new Random().nextInt()) { case 0 -> throw new IllegalStateException("no args"); - break; + break; } switch (new Random().nextInt()) { case 0 -> throw new IllegalStateException("no args"); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/SwitchExpressions.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/SwitchExpressions.java index 79fc7c46a1bd..eac8cb40a0b6 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/SwitchExpressions.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/SwitchExpressions.java @@ -10,9 +10,9 @@ class SwitchExpressions { System.out.println(switch (new Random().nextInt()) { }); - System.out.println(switch (new Random().nextInt()) { + System.out.println(switch (new Random().nextInt()) { case 0 -> throw new IllegalStateException("no args"); - case 1: yield "lone"; + case 1: yield "lone"; }); System.out.println( diff --git a/java/java-tests/testData/inspection/duplicateBranchesInSwitch/NoExceptionWhenFirstLabelIsMissing.java b/java/java-tests/testData/inspection/duplicateBranchesInSwitch/NoExceptionWhenFirstLabelIsMissing.java index 731cd1e5ac56..6c5609243660 100644 --- a/java/java-tests/testData/inspection/duplicateBranchesInSwitch/NoExceptionWhenFirstLabelIsMissing.java +++ b/java/java-tests/testData/inspection/duplicateBranchesInSwitch/NoExceptionWhenFirstLabelIsMissing.java @@ -2,7 +2,7 @@ class C { public static void main(String[] args) { switch (args.length) { - return; + return; case 1: System.out.println(""); System.out.println("");