From 6bc07438c6142d1fd8868374a053c40f46fe8d3f Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 16 Nov 2018 12:04:03 +0700 Subject: [PATCH] Support of enhanced Java 12 switch statements in DFA (IDEA-202132, no expressions yet) --- .../dataFlow/ControlFlowAnalyzer.java | 59 ++++++++++++------- .../dataFlow/DataFlowInspectionBase.java | 31 ++++++---- .../dataFlow/fix/DeleteSwitchLabelFix.java | 28 ++++++--- .../impl/quickfix/UnwrapSwitchLabelFix.java | 15 ++--- ...fterDeleteCaseMultiLabelJava12Preview.java | 11 ++++ .../afterDeleteCaseRuleJava12Preview.java | 12 ++++ .../afterUnwrapJava12Preview.java | 19 ++++++ ...foreDeleteCaseMultiLabelJava12Preview.java | 11 ++++ .../beforeDeleteCaseRuleJava12Preview.java | 11 ++++ .../deleteSwitchLabel/beforeFixAll.java | 2 +- .../beforeUnwrapJava12Preview.java | 22 +++++++ .../dataFlow/ancient/CaseAndNpe.java | 4 +- .../fixture/AlwaysTrueSwitchLabel.java | 6 +- .../dataFlow/fixture/LongRangeBasics.java | 2 +- .../dataFlow/fixture/StringEquality.java | 2 +- .../dataFlow/fixture/SwitchEnumConstant.java | 2 +- .../fixture/SwitchStatementsJava12.java | 42 +++++++++++++ .../DataFlowInspection12Test.java | 42 +++++++++++++ .../LightCodeInsightTestCase.java | 5 +- .../com/siyeh/ig/psiutils/SwitchUtils.java | 17 ++++++ 20 files changed, 285 insertions(+), 58 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterDeleteCaseMultiLabelJava12Preview.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterDeleteCaseRuleJava12Preview.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterUnwrapJava12Preview.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeDeleteCaseMultiLabelJava12Preview.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeDeleteCaseRuleJava12Preview.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeUnwrapJava12Preview.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/SwitchStatementsJava12.java create mode 100644 java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection12Test.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index b1689198d984..cd0d5adfbd8a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -401,9 +401,11 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { @Override public void visitBreakStatement(PsiBreakStatement statement) { startElement(statement); + jumpOut(statement.findExitedStatement()); + finishElement(statement); + } - PsiStatement exitedStatement = statement.findExitedStatement(); - + private void jumpOut(PsiElement exitedStatement) { if (exitedStatement != null && PsiTreeUtil.isAncestor(myCodeFragment, exitedStatement, false)) { controlTransfer(new InstructionTransfer(getEndOffset(exitedStatement), getVariablesInside(exitedStatement)), getTrapsInsideElement(exitedStatement)); @@ -411,8 +413,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { // Jumping out of analyzed code fragment controlTransfer(ReturnTransfer.INSTANCE, getTrapsInsideElement(myCodeFragment)); } - - finishElement(statement); } private void controlTransfer(@NotNull TransferTarget target, FList traps) { @@ -844,6 +844,19 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { finishElement(statement); } + @Override + public void visitSwitchLabeledRuleStatement(PsiSwitchLabeledRuleStatement statement) { + startElement(statement); + PsiStatement body = statement.getBody(); + if (body != null) { + body.accept(this); + if (!(body instanceof PsiThrowStatement)) { + jumpOut(statement.getEnclosingSwitchBlock()); + } + } + finishElement(statement); + } + @Override public void visitSwitchStatement(PsiSwitchStatement switchStmt) { startElement(switchStmt); PsiExpression caseExpression = switchStmt.getExpression(); @@ -889,34 +902,38 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { if (body != null) { PsiStatement[] statements = body.getStatements(); - PsiSwitchLabelStatement defaultLabel = null; ControlFlowOffset offset = null; + PsiSwitchLabelStatementBase defaultLabel = null; for (PsiStatement statement : statements) { - if (statement instanceof PsiSwitchLabelStatement) { - PsiSwitchLabelStatement psiLabelStatement = (PsiSwitchLabelStatement)statement; + if (statement instanceof PsiSwitchLabelStatementBase) { + PsiSwitchLabelStatementBase psiLabelStatement = (PsiSwitchLabelStatementBase)statement; if (psiLabelStatement.isDefaultCase()) { defaultLabel = psiLabelStatement; } else { try { offset = getStartOffset(statement); - PsiExpression caseValue = psiLabelStatement.getCaseValue(); + PsiExpressionList values = psiLabelStatement.getCaseValues(); + if (values != null) { + for (PsiExpression caseValue : values.getExpressions()) { - if (enumValues != null && caseValue instanceof PsiReferenceExpression) { - //noinspection SuspiciousMethodCalls - enumValues.remove(((PsiReferenceExpression)caseValue).resolve()); - } + if (enumValues != null && caseValue instanceof PsiReferenceExpression) { + //noinspection SuspiciousMethodCalls + enumValues.remove(((PsiReferenceExpression)caseValue).resolve()); + } - if (caseValue != null && expressionValue != null) { - addInstruction(new PushInstruction(expressionValue, null)); - caseValue.accept(this); - addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, PsiType.BOOLEAN)); - } - else { - pushUnknown(); - } + if (caseValue != null && expressionValue != null) { + addInstruction(new PushInstruction(expressionValue, null)); + caseValue.accept(this); + addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, PsiType.BOOLEAN)); + } + else { + pushUnknown(); + } - addInstruction(new ConditionalGotoInstruction(offset, false, statement)); + addInstruction(new ConditionalGotoInstruction(offset, false, caseValue)); + } + } } catch (IncorrectOperationException e) { LOG.error(e); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java index e8eeb0698282..21fecd0273e2 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java @@ -305,25 +305,30 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool } private void reportUnreachableSwitchBranches(Set trueSet, Set falseSet, ProblemsHolder holder) { - Set coveredSwitches = new HashSet<>(); - Set trueLabels = StreamEx.of(trueSet).select(BranchingInstruction.class) - .map(BranchingInstruction::getPsiAnchor).select(PsiSwitchLabelStatement.class).toSet(); - Set falseLabels = StreamEx.of(falseSet).select(BranchingInstruction.class) - .map(BranchingInstruction::getPsiAnchor).select(PsiSwitchLabelStatement.class).toSet(); + Set coveredSwitches = new HashSet<>(); + Set trueLabels = StreamEx.of(trueSet).select(ConditionalGotoInstruction.class) + .map(ConditionalGotoInstruction::getPsiAnchor).select(PsiExpression.class) + .filter(e -> SwitchUtils.getLabelStatementForLabel(e) != null).toSet(); + Set falseLabels = StreamEx.of(falseSet).select(ConditionalGotoInstruction.class) + .map(ConditionalGotoInstruction::getPsiAnchor).select(PsiExpression.class) + .filter(e -> SwitchUtils.getLabelStatementForLabel(e) != null).toSet(); - for (PsiSwitchLabelStatement label : trueLabels) { - PsiSwitchStatement statement = label.getEnclosingSwitchStatement(); + for (PsiExpression label : trueLabels) { + PsiSwitchLabelStatementBase labelStatement = Objects.requireNonNull(SwitchUtils.getLabelStatementForLabel(label)); + PsiSwitchBlock statement = labelStatement.getEnclosingSwitchBlock(); if (statement == null) continue; - if (!StreamEx.iterate(label, Objects::nonNull, l -> PsiTreeUtil.getPrevSiblingOfType(l, PsiSwitchLabelStatement.class)) - .skip(1).allMatch(falseLabels::contains)) { + if (!StreamEx.iterate(labelStatement, Objects::nonNull, l -> PsiTreeUtil.getPrevSiblingOfType(l, PsiSwitchLabelStatement.class)) + .skip(1).map(PsiSwitchLabelStatementBase::getCaseValues) + .nonNull().flatArray(PsiExpressionList::getExpressions).allMatch(falseLabels::contains)) { continue; } coveredSwitches.add(statement); holder.registerProblem(label, InspectionsBundle.message("dataflow.message.only.switch.label"), createUnwrapSwitchLabelFix()); } - for (PsiSwitchLabelStatement label : falseLabels) { - if (!coveredSwitches.contains(label.getEnclosingSwitchStatement())) { + for (PsiExpression label : falseLabels) { + PsiSwitchLabelStatementBase labelStatement = Objects.requireNonNull(SwitchUtils.getLabelStatementForLabel(label)); + if (!coveredSwitches.contains(labelStatement.getEnclosingSwitchBlock())) { holder.registerProblem(label, InspectionsBundle.message("dataflow.message.unreachable.switch.label"), new DeleteSwitchLabelFix(label)); } @@ -724,7 +729,9 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool reportConstantBoolean(holder, psiAnchor, reportedAnchors, true); } } - else if (psiAnchor != null && !(psiAnchor instanceof PsiSwitchLabelStatement) && !isFlagCheck(psiAnchor)) { + else if (psiAnchor != null && + (!(psiAnchor instanceof PsiExpression) || SwitchUtils.getLabelStatementForLabel((PsiExpression)psiAnchor) == null) && + !isFlagCheck(psiAnchor)) { boolean evaluatesToTrue = trueSet.contains(instruction); final PsiElement parent = psiAnchor.getParent(); if (parent instanceof PsiAssignmentExpression && diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/fix/DeleteSwitchLabelFix.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/fix/DeleteSwitchLabelFix.java index 7010cf72570a..aa9ddd62781d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/fix/DeleteSwitchLabelFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/fix/DeleteSwitchLabelFix.java @@ -11,6 +11,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ObjectUtils; import com.siyeh.ig.psiutils.CommentTracker; import com.siyeh.ig.psiutils.ControlFlowUtils; +import com.siyeh.ig.psiutils.SwitchUtils; import one.util.streamex.StreamEx; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -24,12 +25,16 @@ public class DeleteSwitchLabelFix implements LocalQuickFix { private final String myName; private final boolean myBranch; - public DeleteSwitchLabelFix(PsiSwitchLabelStatement label) { - myName = Objects.requireNonNull(label.getCaseValue()).getText(); - myBranch = shouldRemoveBranch(label); + public DeleteSwitchLabelFix(@NotNull PsiExpression label) { + myName = label.getText(); + PsiSwitchLabelStatementBase labelStatement = Objects.requireNonNull(SwitchUtils.getLabelStatementForLabel(label)); + PsiExpressionList values = labelStatement.getCaseValues(); + boolean multiple = values != null && values.getExpressionCount() > 1; + myBranch = !multiple && shouldRemoveBranch(labelStatement); } - private static boolean shouldRemoveBranch(PsiSwitchLabelStatement label) { + private static boolean shouldRemoveBranch(PsiSwitchLabelStatementBase label) { + if (label instanceof PsiSwitchLabeledRuleStatement) return true; PsiStatement nextStatement = PsiTreeUtil.getNextSiblingOfType(label, PsiStatement.class); if (nextStatement instanceof PsiSwitchLabelStatement) { return false; @@ -56,16 +61,23 @@ public class DeleteSwitchLabelFix implements LocalQuickFix { @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiSwitchLabelStatement label = PsiTreeUtil.getNonStrictParentOfType(descriptor.getStartElement(), PsiSwitchLabelStatement.class); + PsiExpression expression = ObjectUtils.tryCast(descriptor.getStartElement(), PsiExpression.class); + if (expression == null) return; + PsiSwitchLabelStatementBase label = SwitchUtils.getLabelStatementForLabel(expression); if (label == null) return; - deleteLabel(label); + PsiExpressionList values = label.getCaseValues(); + if (values != null && values.getExpressionCount() == 1) { + deleteLabel(label); + } else { + new CommentTracker().deleteAndRestoreComments(expression); + } } - public static void deleteLabel(PsiSwitchLabelStatement label) { + public static void deleteLabel(PsiSwitchLabelStatementBase label) { if (shouldRemoveBranch(label)) { PsiCodeBlock scope = ObjectUtils.tryCast(label.getParent(), PsiCodeBlock.class); if (scope == null) return; - PsiSwitchLabelStatement nextLabel = PsiTreeUtil.getNextSiblingOfType(label, PsiSwitchLabelStatement.class); + PsiSwitchLabelStatementBase nextLabel = PsiTreeUtil.getNextSiblingOfType(label, PsiSwitchLabelStatementBase.class); PsiElement stopAt = nextLabel == null ? scope.getRBrace() : nextLabel; while(true) { PsiStatement next = PsiTreeUtil.getNextSiblingOfType(nextLabel, PsiStatement.class); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/UnwrapSwitchLabelFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/UnwrapSwitchLabelFix.java index ff6901d6b6f1..46e1461b499f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/UnwrapSwitchLabelFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/UnwrapSwitchLabelFix.java @@ -6,12 +6,11 @@ import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.dataFlow.fix.DeleteSwitchLabelFix; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiKeyword; -import com.intellij.psi.PsiSwitchLabelStatement; -import com.intellij.psi.PsiSwitchStatement; +import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ObjectUtils; import com.siyeh.ig.psiutils.CommentTracker; +import com.siyeh.ig.psiutils.SwitchUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -27,17 +26,19 @@ public class UnwrapSwitchLabelFix implements LocalQuickFix { @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiSwitchLabelStatement label = ObjectUtils.tryCast(descriptor.getStartElement(), PsiSwitchLabelStatement.class); + PsiExpression label = ObjectUtils.tryCast(descriptor.getStartElement(), PsiExpression.class); if (label == null) return; - PsiSwitchStatement statement = label.getEnclosingSwitchStatement(); + PsiSwitchLabelStatementBase labelStatement = SwitchUtils.getLabelStatementForLabel(label); + if (labelStatement == null) return; + PsiSwitchStatement statement = labelStatement.getEnclosingSwitchStatement(); if (statement == null) return; List labels = PsiTreeUtil.getChildrenOfTypeAsList(statement.getBody(), PsiSwitchLabelStatement.class); for (PsiSwitchLabelStatement otherLabel : labels) { - if (otherLabel != label) { + if (otherLabel != labelStatement) { DeleteSwitchLabelFix.deleteLabel(otherLabel); } } - new CommentTracker().replaceAndRestoreComments(label, "default:"); + new CommentTracker().replaceAndRestoreComments(labelStatement, "default:"); ConvertSwitchToIfIntention.doProcessIntention(statement); // will not create 'if', just unwrap, because only default label is left } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterDeleteCaseMultiLabelJava12Preview.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterDeleteCaseMultiLabelJava12Preview.java new file mode 100644 index 000000000000..b4d03ad738d9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterDeleteCaseMultiLabelJava12Preview.java @@ -0,0 +1,11 @@ +// "Remove switch label '0-/*x*/1'" "true" +class Main { + static void fff(int x) { + if (x > 0) { + switch (x) { + case 1, 3 /*x*/: System.out.println("one"); //1 + case 2: System.out.println("two"); //2 + } + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterDeleteCaseRuleJava12Preview.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterDeleteCaseRuleJava12Preview.java new file mode 100644 index 000000000000..42c5dd4523fd --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterDeleteCaseRuleJava12Preview.java @@ -0,0 +1,12 @@ +// "Remove switch branch '-/*x*/1'" "true" +class Main { + static void fff(int x) { + if (x > 0) { + switch (x) { + case 2 -> System.out.println("two"); //2 + //1 + /*x*/ + } + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterUnwrapJava12Preview.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterUnwrapJava12Preview.java new file mode 100644 index 000000000000..e8d66cdc93fe --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/afterUnwrapJava12Preview.java @@ -0,0 +1,19 @@ +// "Unwrap 'switch' statement" "true" +class Main { + static void fff(int x) { + if (x == 5) { + //1 + //2 + //3 + //4 + System.out.println("five-ten-fifteen"); //5 + System.out.println("six"); //6 + System.out.println("seven"); //7 + //other + } + } + + public static void main(String[] args) { + fff(); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeDeleteCaseMultiLabelJava12Preview.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeDeleteCaseMultiLabelJava12Preview.java new file mode 100644 index 000000000000..39c111d3e03e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeDeleteCaseMultiLabelJava12Preview.java @@ -0,0 +1,11 @@ +// "Remove switch label '0-/*x*/1'" "true" +class Main { + static void fff(int x) { + if (x > 0) { + switch (x) { + case 1, 3, 0-/*x*/1: System.out.println("one"); //1 + case 2: System.out.println("two"); //2 + } + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeDeleteCaseRuleJava12Preview.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeDeleteCaseRuleJava12Preview.java new file mode 100644 index 000000000000..7891749ca95f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeDeleteCaseRuleJava12Preview.java @@ -0,0 +1,11 @@ +// "Remove switch branch '-/*x*/1'" "true" +class Main { + static void fff(int x) { + if (x > 0) { + switch (x) { + case 2 -> System.out.println("two"); //2 + case -/*x*/1 -> System.out.println("one"); //1 + } + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeFixAll.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeFixAll.java index 67815b3907ef..b73f923dee2f 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeFixAll.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeFixAll.java @@ -3,7 +3,7 @@ class Main { void t() { int i = 5; switch(i) { - case 1: case 3: // Apply 'Fix all problems in the file' + case 1: case 3: // Apply 'Fix all problems in the file' System.out.println("odd"); break; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeUnwrapJava12Preview.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeUnwrapJava12Preview.java new file mode 100644 index 000000000000..f0bc4b8033e7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/deleteSwitchLabel/beforeUnwrapJava12Preview.java @@ -0,0 +1,22 @@ +// "Unwrap 'switch' statement" "true" +class Main { + static void fff(int x) { + if (x == 5) { + switch (x) { + case 1: System.out.println("one"); //1 + case 2: System.out.println("two"); //2 + case 3: System.out.println("three"); //3 + case 4: System.out.println("four"); //4 + case 0, 5, 10: System.out.println("five-ten-fifteen"); //5 + case 6: System.out.println("six"); //6 + case 7: System.out.println("seven"); //7 + break; + default: System.out.println("and more"); //other + } + } + } + + public static void main(String[] args) { + fff(); + } +} diff --git a/java/java-tests/testData/inspection/dataFlow/ancient/CaseAndNpe.java b/java/java-tests/testData/inspection/dataFlow/ancient/CaseAndNpe.java index 377d105e3814..b4b1cb36c441 100644 --- a/java/java-tests/testData/inspection/dataFlow/ancient/CaseAndNpe.java +++ b/java/java-tests/testData/inspection/dataFlow/ancient/CaseAndNpe.java @@ -37,8 +37,8 @@ public class aaa { //System.exit(0); switch(i) { - case 1: System.out.println("1 not reachable"); break; - case 2: System.out.println("2 not reachable"); break; + case 1: System.out.println("1 not reachable"); break; + case 2: System.out.println("2 not reachable"); break; case 6: System.out.println("6 reachable"); break; case 5: System.out.println("5 reachable"); break; default: System.out.println("Default not reachable"); break; diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AlwaysTrueSwitchLabel.java b/java/java-tests/testData/inspection/dataFlow/fixture/AlwaysTrueSwitchLabel.java index 1230e32bd343..16a8614883e1 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/AlwaysTrueSwitchLabel.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/AlwaysTrueSwitchLabel.java @@ -1,7 +1,7 @@ class Scratch { public static void main(String[] args) { switch("ping") { - case "ping": + case "ping": System.out.println("ping"); break; case "pong": @@ -17,7 +17,7 @@ class Scratch { case "pong": System.out.println("pong"); break; - case "ping": + case "ping": System.out.println("ping"); break; case "simple": @@ -33,7 +33,7 @@ class Scratch { case "simple": System.out.println("simple"); break; - case "ping": + case "ping": System.out.println("ping"); break; default: diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeBasics.java b/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeBasics.java index 272dace0cc13..57994ccc5c98 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeBasics.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeBasics.java @@ -160,7 +160,7 @@ public class LongRangeBasics { public void testBitwiseAnd() { int state = getState() & 0xF; switch (state) { - case 24: + case 24: System.out.println("Impossible"); } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java b/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java index 5b558380de75..4ae12c12e813 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java @@ -17,7 +17,7 @@ class StringEquality { switch(s) { case "bar": case "baz": - case "foo": + case "foo": } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/SwitchEnumConstant.java b/java/java-tests/testData/inspection/dataFlow/fixture/SwitchEnumConstant.java index e3a1be64e5ea..ed08969b6da4 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/SwitchEnumConstant.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/SwitchEnumConstant.java @@ -51,7 +51,7 @@ class InspectionTest { System.out.println("It's b");break; case C: System.out.println("It's c");break; - case A: + case A: System.out.println("It's a again");break; } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/SwitchStatementsJava12.java b/java/java-tests/testData/inspection/dataFlow/fixture/SwitchStatementsJava12.java new file mode 100644 index 000000000000..46e3efa6ab39 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/SwitchStatementsJava12.java @@ -0,0 +1,42 @@ +public class SwitchStatementsJava12 { + void testMultiLabel(int x) { + if(x > 0) { + switch (x) { + case 1, 2, -1: + System.out.println("oops"); + break; + } + switch (x) { + case 1, 2, -1 -> { + System.out.println("oops"); + break; + } + } + } + } + + void testThrowRule(int x) { + switch (x) { + case 0 -> throw new IllegalArgumentException(); + default -> System.out.println(x == 0); + } + if (x == 0) System.out.println("impossible"); + } + + void testFallthrough(int x) { + switch (x) { + case 0 -> System.out.println(x); + case 1 -> { + System.out.println(x == 0); + System.out.println(x == 1); + } + } + switch (x) { + case 0: System.out.println(x); + case 1: { + System.out.println(x == 0); + System.out.println(x == 1); + } + } + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection12Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection12Test.java new file mode 100644 index 000000000000..cd0d217313bb --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection12Test.java @@ -0,0 +1,42 @@ +// 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. +package com.intellij.java.codeInspection; + +import com.intellij.JavaTestUtil; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ContentEntry; +import com.intellij.openapi.roots.LanguageLevelModuleExtension; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.PsiTestUtil; +import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor; +import org.jetbrains.annotations.NotNull; + +public class DataFlowInspection12Test extends DataFlowInspectionTestCase { + private static final DefaultLightProjectDescriptor PROJECT_DESCRIPTOR = new DefaultLightProjectDescriptor() { + @Override + public Sdk getSdk() { + return PsiTestUtil.addJdkAnnotations(IdeaTestUtil.getMockJdk(LanguageLevel.JDK_12_PREVIEW.toJavaVersion())); + } + + @Override + public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { + model.getModuleExtension(LanguageLevelModuleExtension.class).setLanguageLevel(LanguageLevel.JDK_12_PREVIEW); + } + }; + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return PROJECT_DESCRIPTOR; + } + + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath() + "/inspection/dataFlow/fixture/"; + } + + public void testSwitchStatementsJava12() { doTest(); } +} \ No newline at end of file diff --git a/java/testFramework/src/com/intellij/testFramework/LightCodeInsightTestCase.java b/java/testFramework/src/com/intellij/testFramework/LightCodeInsightTestCase.java index a8cb02bf1c11..22ea2806bfdd 100644 --- a/java/testFramework/src/com/intellij/testFramework/LightCodeInsightTestCase.java +++ b/java/testFramework/src/com/intellij/testFramework/LightCodeInsightTestCase.java @@ -16,7 +16,7 @@ import java.util.regex.Pattern; * A TestCase for single PsiFile being opened in Editor conversion. See configureXXX and checkResultXXX method docs. */ public abstract class LightCodeInsightTestCase extends LightPlatformCodeInsightTestCase { - private static final Pattern JDK_SELECT_PATTERN = Pattern.compile("Java([\\d.]+)(\\.java)?$"); + private static final Pattern JDK_SELECT_PATTERN = Pattern.compile("Java([\\d.]+)(Preview)?(\\.java)?$"); public static JavaPsiFacadeEx getJavaFacade() { return JavaPsiFacadeEx.getInstanceEx(ourProject); @@ -42,6 +42,9 @@ public abstract class LightCodeInsightTestCase extends LightPlatformCodeInsightT if (matcher.find()) { LanguageLevel level = LanguageLevel.parse(matcher.group(1)); if (level != null) { + if (!matcher.group(2).isEmpty()) { + level = LanguageLevel.valueOf(level + "_PREVIEW"); + } return level; } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/SwitchUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/SwitchUtils.java index 677ca9f93c9e..02c77db76992 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/SwitchUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/SwitchUtils.java @@ -23,6 +23,8 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.ObjectUtils; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -239,6 +241,21 @@ public class SwitchUtils { } } + /** + * Returns enclosing label statement for given label expression + * + * @param expression switch label expression + * @return enclosing label statement or null if given expression is not a label statement + */ + @Contract("null -> null") + @Nullable + public static PsiSwitchLabelStatementBase getLabelStatementForLabel(PsiExpression expression) { + if (expression == null) return null; + PsiElement parent = expression.getParent(); + if (!(parent instanceof PsiExpressionList)) return null; + return ObjectUtils.tryCast(parent.getParent(), PsiSwitchLabelStatementBase.class); + } + private static boolean checkForLabel(String name, PsiElement ancestor) { final LabelSearchVisitor visitor = new LabelSearchVisitor(name); ancestor.accept(visitor);