mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
[java-highlighting] IDEA-324880 Exhaustive switches can produce definitely assigned variables
GitOrigin-RevId: 7e644a2565803fdd900830d986dba481aeb127db
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ff09dfa2e1
commit
5237df5eb3
@@ -358,8 +358,8 @@ public class SwitchBlockHighlightingModel {
|
||||
}
|
||||
|
||||
private boolean isEnhancedSwitch(@NotNull List<? extends PsiCaseLabelElement> labelElements) {
|
||||
if (getSwitchSelectorKind() == SelectorKind.CLASS_OR_ARRAY) return true;
|
||||
return ContainerUtil.exists(labelElements, st -> st instanceof PsiPattern || st instanceof PsiPatternGuard || isNullType(st));
|
||||
boolean selectorIsTypeOrClass = getSwitchSelectorKind() == SelectorKind.CLASS_OR_ARRAY;
|
||||
return JavaPsiSwitchUtil.isEnhancedSwitch(labelElements, selectorIsTypeOrClass);
|
||||
}
|
||||
|
||||
private static boolean isNullType(@NotNull PsiElement element) {
|
||||
|
||||
@@ -9,10 +9,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.JavaPsiPatternUtil;
|
||||
import com.intellij.psi.util.JavaPsiRecordUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
@@ -963,7 +960,7 @@ final class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
PsiCodeBlock body = statement.getBody();
|
||||
if (body != null) {
|
||||
PsiStatement[] statements = body.getStatements();
|
||||
boolean needToCreateDefault = false;
|
||||
boolean needToCreateDefault = JavaPsiSwitchUtil.isEnhancedSwitchStatement(statement);
|
||||
PsiType exprType = expr == null ? null : expr.getType();
|
||||
for (PsiStatement aStatement : statements) {
|
||||
ProgressManager.checkCanceled();
|
||||
@@ -973,7 +970,7 @@ final class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
needToCreateDefault = true;
|
||||
}
|
||||
PsiCaseLabelElementList labelElementList = labelStatement.getCaseLabelElementList();
|
||||
if (labelElementList != null) {
|
||||
if (!needToCreateDefault && labelElementList != null) {
|
||||
for (PsiCaseLabelElement element : labelElementList.getElements()) {
|
||||
if (element instanceof PsiDefaultCaseLabelElement ||
|
||||
exprType != null && JavaPsiPatternUtil.isUnconditionalForType(element, exprType)) {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.util;
|
||||
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility methods to support switch
|
||||
*/
|
||||
public final class JavaPsiSwitchUtil {
|
||||
|
||||
/**
|
||||
* Checks if the given switch statement is enhanced.
|
||||
*
|
||||
* @param statement the switch statement to check
|
||||
* @return true if the switch statement is an enhanced switch statement, false otherwise
|
||||
*/
|
||||
public static boolean isEnhancedSwitchStatement(@NotNull PsiSwitchBlock statement) {
|
||||
if(!(statement instanceof PsiSwitchStatement)) return false;
|
||||
|
||||
PsiExpression selector = statement.getExpression();
|
||||
if (selector == null) {
|
||||
return false;
|
||||
}
|
||||
PsiType selectorType = selector.getType();
|
||||
if (selectorType == null) {
|
||||
return false;
|
||||
}
|
||||
PsiCodeBlock body = statement.getBody();
|
||||
if (body == null) {
|
||||
return false;
|
||||
}
|
||||
List<PsiCaseLabelElement> cases = new ArrayList<>();
|
||||
for (PsiStatement psiStatement : body.getStatements()) {
|
||||
if (psiStatement instanceof PsiSwitchLabelStatementBase) {
|
||||
PsiSwitchLabelStatementBase labelStatementBase = (PsiSwitchLabelStatementBase)psiStatement;
|
||||
PsiCaseLabelElementList elementList = labelStatementBase.getCaseLabelElementList();
|
||||
if (elementList == null) {
|
||||
continue;
|
||||
}
|
||||
PsiCaseLabelElement[] elements = elementList.getElements();
|
||||
for (PsiCaseLabelElement caseLabelElement : elements) {
|
||||
if (caseLabelElement != null) {
|
||||
cases.add(caseLabelElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return isEnhancedSwitch(cases, isClassSelectorType(selectorType));
|
||||
}
|
||||
|
||||
public static boolean isEnhancedSwitch(@NotNull List<? extends PsiCaseLabelElement> labelElements, boolean selectorIsTypeOrClass) {
|
||||
if (selectorIsTypeOrClass) return true;
|
||||
return ContainerUtil.exists(labelElements, st -> st instanceof PsiPattern || st instanceof PsiPatternGuard || isNullType(st));
|
||||
}
|
||||
|
||||
|
||||
private static boolean isNullType(@NotNull PsiElement element) {
|
||||
return element instanceof PsiExpression && TypeConversionUtil.isNullType(((PsiExpression)element).getType());
|
||||
}
|
||||
|
||||
private static boolean isClassSelectorType(@NotNull PsiType type) {
|
||||
if (TypeConversionUtil.isPrimitiveAndNotNull(type)) return false;
|
||||
PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(type);
|
||||
if (psiClass != null) {
|
||||
if (psiClass.isEnum()) return false;
|
||||
String fqn = psiClass.getQualifiedName();
|
||||
if (Comparing.strEqual(fqn, CommonClassNames.JAVA_LANG_STRING)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ class Test {
|
||||
case Second s:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
System.out.println();
|
||||
<error descr="Unreachable statement">System.out.println();</error>
|
||||
}
|
||||
|
||||
void testSealedClassUnreachable1(I i) {
|
||||
|
||||
@@ -216,4 +216,26 @@ class C {
|
||||
}
|
||||
System.out.println(<error descr="Variable 's' might not have been initialized">s</error>);
|
||||
}
|
||||
|
||||
sealed interface T permits T1, T2 {}
|
||||
final class T1 implements T {}
|
||||
final class T2 implements T {}
|
||||
|
||||
private void testStatement1(T obj) {
|
||||
int i;
|
||||
switch (obj) {
|
||||
case T1 t1 -> i = 1;
|
||||
case T2 t2 -> i = 2;
|
||||
};
|
||||
System.out.println(i);
|
||||
}
|
||||
|
||||
private void testStatement2(int obj) {
|
||||
int i;
|
||||
switch (obj) {
|
||||
case 1 -> i = 1;
|
||||
case 2 -> i = 2;
|
||||
};
|
||||
System.out.println(<error descr="Variable 'i' might not have been initialized">i</error>);
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@ class JavaSwitchExpressionsHighlightingTest : LightJavaCodeInsightFixtureTestCas
|
||||
fun testSwitchExpressionsEnumResolve() = doTest()
|
||||
fun testSwitchNumericPromotion() = doTest()
|
||||
fun testSimpleInferenceCases() = doTest()
|
||||
fun testEnhancedSwitchDefinitelyAssigned() = doTest()
|
||||
fun testEnhancedSwitchUnreachable() = IdeaTestUtil.withLevel(module, LanguageLevel.JDK_20_PREVIEW) { doTest() }
|
||||
fun testEnhancedSwitchDefinitelyAssigned() = IdeaTestUtil.withLevel(module, LanguageLevel.JDK_21) { doTest() }
|
||||
fun testEnhancedSwitchUnreachable() = IdeaTestUtil.withLevel(module, LanguageLevel.JDK_21) { doTest() }
|
||||
fun testSwitchExpressionHasResult() = doTest()
|
||||
fun testYieldStatements() = doTest()
|
||||
fun testAssignToFinalInSwitchExpression() = doTest()
|
||||
|
||||
Reference in New Issue
Block a user