mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-highlighting] evaluateSwitchCompleteness moved to SwitchUtils
Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: 6a71dfcf7c7166dea51e424d57c75998b68cb7c9
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a1d51b1179
commit
0f412df4d0
-84
@@ -230,91 +230,7 @@ public final class SwitchBlockHighlightingModel {
|
||||
return StreamEx.of(result).distinct().toList();
|
||||
}
|
||||
|
||||
private static @Nullable PsiCaseLabelElement findUnconditionalPatternForType(@NotNull List<? extends PsiCaseLabelElement> 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<PsiCaseLabelElement> 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PsiCaseLabelElement> 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;
|
||||
|
||||
+4
-5
@@ -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) {
|
||||
|
||||
+2
-2
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user