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 e7b36cd8f104..4f9e2546df15 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 @@ -230,91 +230,7 @@ public final class SwitchBlockHighlightingModel { return StreamEx.of(result).distinct().toList(); } - private static @Nullable PsiCaseLabelElement findUnconditionalPatternForType(@NotNull List labelElements, - @NotNull PsiType type) { - return ContainerUtil.find(labelElements, element -> JavaPsiPatternUtil.isUnconditionalForType(element, type)); - } - - /** - * Evaluates the exhaustiveness state of a switch block. - * - * @param switchBlock the PsiSwitchBlock to evaluate - * @param considerNestedDeconstructionPatterns flag indicating whether to consider nested deconstruction patterns. It is necessary to take into account, - * because nested deconstruction patterns don't cover null values - * @return exhaustiveness state. - */ - public static @NotNull SwitchExhaustivenessState evaluateSwitchCompleteness(@NotNull PsiSwitchBlock switchBlock, - boolean considerNestedDeconstructionPatterns) { - PsiExpression selector = switchBlock.getExpression(); - if (selector == null) return SwitchExhaustivenessState.MALFORMED; - PsiType selectorType = selector.getType(); - if (selectorType == null) return SwitchExhaustivenessState.MALFORMED; - PsiCodeBlock switchBody = switchBlock.getBody(); - if (switchBody == null) return SwitchExhaustivenessState.MALFORMED; - List labelElements = StreamEx.of(JavaPsiSwitchUtil.getSwitchBranches(switchBlock)).select(PsiCaseLabelElement.class) - .filter(element -> !(element instanceof PsiDefaultCaseLabelElement)).toList(); - if (labelElements.isEmpty()) return SwitchExhaustivenessState.EMPTY; - boolean needToCheckCompleteness = ExpressionUtil.isEnhancedSwitch(switchBlock); - boolean isEnumSelector = JavaPsiSwitchUtil.getSwitchSelectorKind(selectorType) == JavaPsiSwitchUtil.SelectorKind.ENUM; - if (findUnconditionalPatternForType(labelElements, selectorType) != null) { - return SwitchExhaustivenessState.EXHAUSTIVE_NO_DEFAULT; - } - if (JavaPsiSwitchUtil.isBooleanSwitchWithTrueAndFalse(switchBlock)) { - return SwitchExhaustivenessState.EXHAUSTIVE_NO_DEFAULT; - } - if (!needToCheckCompleteness && !isEnumSelector) return SwitchExhaustivenessState.INCOMPLETE; - // It is necessary because deconstruction patterns don't cover cases - // when some of their components are null and deconstructionPattern too - if (!considerNestedDeconstructionPatterns) { - labelElements = ContainerUtil.filter( - labelElements, label -> !(label instanceof PsiDeconstructionPattern deconstructionPattern && - ContainerUtil.or( - deconstructionPattern.getDeconstructionList().getDeconstructionComponents(), - component -> component instanceof PsiDeconstructionPattern))); - } - boolean hasError = hasExhaustivenessError(switchBlock, labelElements); - // if a switch block is needed to check completeness and switch is incomplete we let highlighting to inform about it as it's a compilation error - if (needToCheckCompleteness) { - return hasError ? SwitchExhaustivenessState.UNNECESSARY : SwitchExhaustivenessState.EXHAUSTIVE_CAN_ADD_DEFAULT; - } - return hasError ? SwitchExhaustivenessState.INCOMPLETE : SwitchExhaustivenessState.EXHAUSTIVE_CAN_ADD_DEFAULT; - } - private static QuickFixFactory getFixFactory() { return QuickFixFactory.getInstance(); } - - - /** - * State of switch exhaustiveness. - */ - public enum SwitchExhaustivenessState { - /** - * Switch is malformed and produces a compilation error (no body, no selector, etc.), - * no exhaustiveness analysis is performed - */ - MALFORMED, - /** - * Switch contains no labels (except probably default label) - */ - EMPTY, - /** - * Switch should not be exhaustive (classic switch statement) - */ - UNNECESSARY, - /** - * Switch is not exhaustive - */ - INCOMPLETE, - /** - * Switch is exhaustive (complete), and adding a default branch would be a compilation error. - * This includes a switch over boolean having both true and false branches, - * or a switch that has an unconditional pattern branch. - */ - EXHAUSTIVE_NO_DEFAULT, - /** - * Switch is exhaustive (complete), but it's possible to add a default branch. - */ - EXHAUSTIVE_CAN_ADD_DEFAULT - } } diff --git a/java/java-analysis-impl/src/com/siyeh/ig/psiutils/SwitchUtils.java b/java/java-analysis-impl/src/com/siyeh/ig/psiutils/SwitchUtils.java index 3b8d5c2e620c..5ac4419b7db8 100644 --- a/java/java-analysis-impl/src/com/siyeh/ig/psiutils/SwitchUtils.java +++ b/java/java-analysis-impl/src/com/siyeh/ig/psiutils/SwitchUtils.java @@ -38,6 +38,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import static com.intellij.java.codeserver.core.JavaPatternExhaustivenessUtil.hasExhaustivenessError; import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING; import static com.siyeh.ig.callMatcher.CallMatcher.instanceCall; @@ -926,6 +927,88 @@ public final class SwitchUtils { } return unreachableElements; } + + /** + * Evaluates the exhaustiveness state of a switch block. + * + * @param switchBlock the PsiSwitchBlock to evaluate + * @param considerNestedDeconstructionPatterns flag indicating whether to consider nested deconstruction patterns. It is necessary to take into account, + * because nested deconstruction patterns don't cover null values + * @return exhaustiveness state. + */ + public static @NotNull SwitchExhaustivenessState evaluateSwitchCompleteness(@NotNull PsiSwitchBlock switchBlock, + boolean considerNestedDeconstructionPatterns) { + PsiExpression selector = switchBlock.getExpression(); + if (selector == null) return SwitchExhaustivenessState.MALFORMED; + PsiType selectorType = selector.getType(); + if (selectorType == null) return SwitchExhaustivenessState.MALFORMED; + PsiCodeBlock switchBody = switchBlock.getBody(); + if (switchBody == null) return SwitchExhaustivenessState.MALFORMED; + List labelElements = StreamEx.of(JavaPsiSwitchUtil.getSwitchBranches(switchBlock)).select(PsiCaseLabelElement.class) + .filter(element -> !(element instanceof PsiDefaultCaseLabelElement)).toList(); + if (labelElements.isEmpty()) return SwitchExhaustivenessState.EMPTY; + boolean needToCheckCompleteness = ExpressionUtil.isEnhancedSwitch(switchBlock); + boolean isEnumSelector = JavaPsiSwitchUtil.getSwitchSelectorKind(selectorType) == JavaPsiSwitchUtil.SelectorKind.ENUM; + if (ContainerUtil.find(labelElements, element -> JavaPsiPatternUtil.isUnconditionalForType(element, selectorType)) != null) { + return SwitchExhaustivenessState.EXHAUSTIVE_NO_DEFAULT; + } + if (JavaPsiSwitchUtil.isBooleanSwitchWithTrueAndFalse(switchBlock)) { + return SwitchExhaustivenessState.EXHAUSTIVE_NO_DEFAULT; + } + if (!needToCheckCompleteness && !isEnumSelector) return SwitchExhaustivenessState.INCOMPLETE; + // It is necessary because deconstruction patterns don't cover cases + // when some of their components are null and deconstructionPattern too + if (!considerNestedDeconstructionPatterns) { + labelElements = ContainerUtil.filter( + labelElements, label -> !(label instanceof PsiDeconstructionPattern deconstructionPattern && + ContainerUtil.or( + deconstructionPattern.getDeconstructionList().getDeconstructionComponents(), + component -> component instanceof PsiDeconstructionPattern))); + } + boolean hasError = hasExhaustivenessError(switchBlock, labelElements); + // if a switch block is needed to check completeness and switch is incomplete we let highlighting to inform about it as it's a compilation error + if (!hasError) { + return SwitchExhaustivenessState.EXHAUSTIVE_CAN_ADD_DEFAULT; + } + if (needToCheckCompleteness) { + return SwitchExhaustivenessState.UNNECESSARY; + } + return SwitchExhaustivenessState.INCOMPLETE; + } + + /** + * State of switch exhaustiveness. + */ + public enum SwitchExhaustivenessState { + /** + * Switch is malformed and produces a compilation error (no body, no selector, etc.), + * no exhaustiveness analysis is performed + */ + MALFORMED, + /** + * Switch contains no labels (except probably default label) + */ + EMPTY, + /** + * Switch should not be exhaustive (classic switch statement) + */ + UNNECESSARY, + /** + * Switch is not exhaustive + */ + INCOMPLETE, + /** + * Switch is exhaustive (complete), and adding a default branch would be a compilation error. + * This includes a switch over boolean having both true and false branches, + * or a switch that has an unconditional pattern branch. + */ + EXHAUSTIVE_NO_DEFAULT, + /** + * Switch is exhaustive (complete), but it's possible to add a default branch. + */ + EXHAUSTIVE_CAN_ADD_DEFAULT + } + private static class LabelSearchVisitor extends JavaRecursiveElementWalkingVisitor { private final String m_labelName; diff --git a/java/java-impl/src/com/intellij/codeInspection/EnhancedSwitchMigrationInspection.java b/java/java-impl/src/com/intellij/codeInspection/EnhancedSwitchMigrationInspection.java index 25dadf5ff170..c8fab16f368e 100644 --- a/java/java-impl/src/com/intellij/codeInspection/EnhancedSwitchMigrationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/EnhancedSwitchMigrationInspection.java @@ -2,7 +2,6 @@ package com.intellij.codeInspection; import com.intellij.codeInsight.BlockUtils; -import com.intellij.codeInsight.daemon.impl.analysis.SwitchBlockHighlightingModel; import com.intellij.codeInspection.options.OptPane; import com.intellij.java.JavaBundle; import com.intellij.modcommand.ModPsiUpdater; @@ -26,9 +25,9 @@ import org.jetbrains.annotations.PropertyKey; import java.util.*; -import static com.intellij.codeInsight.daemon.impl.analysis.SwitchBlockHighlightingModel.evaluateSwitchCompleteness; import static com.intellij.codeInspection.options.OptPane.*; import static com.intellij.util.ObjectUtils.tryCast; +import static com.siyeh.ig.psiutils.SwitchUtils.evaluateSwitchCompleteness; public final class EnhancedSwitchMigrationInspection extends AbstractBaseJavaLocalInspectionTool { @SuppressWarnings("WeakerAccess") public boolean myWarnOnlyOnExpressionConversion = true; @@ -285,9 +284,9 @@ public final class EnhancedSwitchMigrationInspection extends AbstractBaseJavaLoc if (branch.isDefault()) return true; if (existsDefaultLabelElement(branch.myLabelStatement)) return true; } - SwitchBlockHighlightingModel.SwitchExhaustivenessState completenessResult = evaluateSwitchCompleteness(switchStatement, true); - return completenessResult == SwitchBlockHighlightingModel.SwitchExhaustivenessState.EXHAUSTIVE_CAN_ADD_DEFAULT || - completenessResult == SwitchBlockHighlightingModel.SwitchExhaustivenessState.EXHAUSTIVE_NO_DEFAULT; + SwitchUtils.SwitchExhaustivenessState completenessResult = evaluateSwitchCompleteness(switchStatement, true); + return completenessResult == SwitchUtils.SwitchExhaustivenessState.EXHAUSTIVE_CAN_ADD_DEFAULT || + completenessResult == SwitchUtils.SwitchExhaustivenessState.EXHAUSTIVE_NO_DEFAULT; } private static boolean isConvertibleBranch(@NotNull OldSwitchStatementBranch branch, boolean hasNext) { diff --git a/java/java-impl/src/com/siyeh/ig/controlflow/SwitchStatementsWithoutDefaultInspection.java b/java/java-impl/src/com/siyeh/ig/controlflow/SwitchStatementsWithoutDefaultInspection.java index cf8d8f05a159..d1f04b6de569 100644 --- a/java/java-impl/src/com/siyeh/ig/controlflow/SwitchStatementsWithoutDefaultInspection.java +++ b/java/java-impl/src/com/siyeh/ig/controlflow/SwitchStatementsWithoutDefaultInspection.java @@ -30,10 +30,10 @@ import com.siyeh.ig.psiutils.SwitchUtils; import org.intellij.lang.annotations.Pattern; import org.jetbrains.annotations.NotNull; -import static com.intellij.codeInsight.daemon.impl.analysis.SwitchBlockHighlightingModel.SwitchExhaustivenessState; -import static com.intellij.codeInsight.daemon.impl.analysis.SwitchBlockHighlightingModel.evaluateSwitchCompleteness; import static com.intellij.codeInspection.options.OptPane.checkbox; import static com.intellij.codeInspection.options.OptPane.pane; +import static com.siyeh.ig.psiutils.SwitchUtils.SwitchExhaustivenessState; +import static com.siyeh.ig.psiutils.SwitchUtils.evaluateSwitchCompleteness; public final class SwitchStatementsWithoutDefaultInspection extends AbstractBaseJavaLocalInspectionTool { diff --git a/java/java-impl/src/com/siyeh/ig/controlflow/UnnecessaryDefaultInspection.java b/java/java-impl/src/com/siyeh/ig/controlflow/UnnecessaryDefaultInspection.java index 80ce6d6ea81e..45394ab9114a 100644 --- a/java/java-impl/src/com/siyeh/ig/controlflow/UnnecessaryDefaultInspection.java +++ b/java/java-impl/src/com/siyeh/ig/controlflow/UnnecessaryDefaultInspection.java @@ -40,12 +40,12 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; -import static com.intellij.codeInsight.daemon.impl.analysis.SwitchBlockHighlightingModel.SwitchExhaustivenessState; -import static com.intellij.codeInsight.daemon.impl.analysis.SwitchBlockHighlightingModel.evaluateSwitchCompleteness; import static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING; import static com.intellij.codeInspection.ProblemHighlightType.INFORMATION; import static com.intellij.codeInspection.options.OptPane.checkbox; import static com.intellij.codeInspection.options.OptPane.pane; +import static com.siyeh.ig.psiutils.SwitchUtils.SwitchExhaustivenessState; +import static com.siyeh.ig.psiutils.SwitchUtils.evaluateSwitchCompleteness; public final class UnnecessaryDefaultInspection extends BaseInspection {