[java-inspections] IDEA-304462 Nested total pattern is reported as always true, despite component might be null

GitOrigin-RevId: 0dcd2be4710c6d7f6ed87b7b05ae7ddf2fca14f7
This commit is contained in:
Tagir Valeev
2022-10-24 18:19:40 +02:00
committed by intellij-monorepo-bot
parent d30eb09f54
commit 55584525ea
19 changed files with 70 additions and 33 deletions

View File

@@ -417,7 +417,7 @@ public class SwitchBlockHighlightingModel {
PsiType permittedType = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass, substitutor);
if (pattern == null && (PsiUtil.getLanguageLevel(permittedClass).isLessThan(LanguageLevel.JDK_18_PREVIEW) ||
TypeConversionUtil.areTypesConvertible(selectorType, permittedType)) ||
pattern != null && !JavaPsiPatternUtil.isTotalForType(pattern, TypeUtils.getType(permittedClass), false)) {
pattern != null && !JavaPsiPatternUtil.isTotalForType(pattern, TypeUtils.getType(permittedClass), true)) {
nonVisited.add(permittedClass);
}
}
@@ -1002,7 +1002,7 @@ public class SwitchBlockHighlightingModel {
if (exhaustiveGroups.isEmpty()) return false;
List<PsiPattern> patterns = ContainerUtil.map(exhaustiveGroups, it -> it.getValue().iterator().next().get(0));
if (ContainerUtil.exists(patterns, pattern -> JavaPsiPatternUtil.isTotalForType(pattern, typeToCheck, false))) {
if (ContainerUtil.exists(patterns, pattern -> JavaPsiPatternUtil.isTotalForType(pattern, typeToCheck, true))) {
return true;
}
LinkedHashMap<PsiClass, PsiPattern> patternClasses = SwitchBlockHighlightingModel.findPatternClasses(patterns);

View File

@@ -1779,8 +1779,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
PsiTypeElement checkType = InstanceOfUtils.findCheckTypeElement(expression);
CFGBuilder builder = new CFGBuilder(this);
DfaVariableValue expressionValue;
PsiPatternVariable patternVariable = pattern == null ? null : JavaPsiPatternUtil.getPatternVariable(pattern);
if (patternVariable == null) {
if (pattern == null) {
if (checkType == null) {
pushUnknown();
}

View File

@@ -223,7 +223,7 @@ public class UnwrapSwitchLabelFix implements LocalQuickFix {
String newVarName = generator.byType(JavaPsiPatternUtil.getPatternType(label)).generate(true);
declarationStatementText += newVarName + "=";
}
if (!JavaPsiPatternUtil.isTotalForType(pattern, selectorType)) {
if (!type.isAssignableFrom(selectorType)) {
declarationStatementText += "(" + type.getPresentableText() + ")";
}
declarationStatementText += selector.getText() + ";";
@@ -273,7 +273,7 @@ public class UnwrapSwitchLabelFix implements LocalQuickFix {
String newVarName = generator.byType(type).generate(true);
declarationStatementText += newVarName + "=";
}
if (!JavaPsiPatternUtil.isTotalForType(deconstructionComponent, components[i].getType())) {
if (!type.isAssignableFrom(components[i].getType())) {
declarationStatementText += "(" + type.getPresentableText() + ")";
}
declarationStatementText += variable.getName() + "." + components[i].getName() + "();";

View File

@@ -193,26 +193,26 @@ public final class JavaPsiPatternUtil {
@Contract(value = "null, _ -> false", pure = true)
public static boolean isTotalForType(@Nullable PsiCaseLabelElement pattern, @NotNull PsiType type) {
return isTotalForType(pattern, type, true);
return isTotalForType(pattern, type, false);
}
public static boolean isTotalForType(@Nullable PsiCaseLabelElement pattern, @NotNull PsiType type, boolean checkComponents) {
public static boolean isTotalForType(@Nullable PsiCaseLabelElement pattern, @NotNull PsiType type, boolean forDomination) {
if (pattern == null) return false;
if (pattern instanceof PsiPatternGuard) {
PsiPatternGuard guarded = (PsiPatternGuard)pattern;
Object constVal = evaluateConstant(guarded.getGuardingExpression());
return isTotalForType(guarded.getPattern(), type, checkComponents) && Boolean.TRUE.equals(constVal);
return isTotalForType(guarded.getPattern(), type, forDomination) && Boolean.TRUE.equals(constVal);
}
if (pattern instanceof PsiGuardedPattern) {
PsiGuardedPattern guarded = (PsiGuardedPattern)pattern;
Object constVal = evaluateConstant(guarded.getGuardingExpression());
return isTotalForType(guarded.getPrimaryPattern(), type, checkComponents) && Boolean.TRUE.equals(constVal);
return isTotalForType(guarded.getPrimaryPattern(), type, forDomination) && Boolean.TRUE.equals(constVal);
}
else if (pattern instanceof PsiParenthesizedPattern) {
return isTotalForType(((PsiParenthesizedPattern)pattern).getPattern(), type, checkComponents);
return isTotalForType(((PsiParenthesizedPattern)pattern).getPattern(), type, forDomination);
}
else if (pattern instanceof PsiDeconstructionPattern) {
return dominates(getPatternType(pattern), type) && (!checkComponents || hasTotalComponents((PsiDeconstructionPattern)pattern));
return forDomination && dominates(getPatternType(pattern), type);
}
else if (pattern instanceof PsiTypeTestPattern) {
return dominates(getPatternType(pattern), type);
@@ -234,7 +234,7 @@ public final class JavaPsiPatternUtil {
for (int i = 0; i < patternComponents.length; i++) {
PsiPattern patternComponent = patternComponents[i];
PsiType componentType = recordComponents[i].getType();
if (!isTotalForType(patternComponent, componentType, true)) {
if (!isTotalForType(patternComponent, componentType)) {
return false;
}
}
@@ -261,7 +261,7 @@ public final class JavaPsiPatternUtil {
public static boolean dominates(@Nullable PsiCaseLabelElement who, @NotNull PsiCaseLabelElement overWhom) {
if (who == null) return false;
PsiType overWhomType = getPatternType(overWhom);
if (overWhomType == null || !isTotalForType(who, overWhomType, false)) {
if (overWhomType == null || !isTotalForType(who, overWhomType, true)) {
return false;
}
PsiDeconstructionPattern whoDeconstruction = findDeconstructionPattern(who);

View File

@@ -30,15 +30,15 @@ public class Basic {
Generic<C> genericC;
<T extends Super> void testGenerics(T p, Pair<T> pair1, Pair<? extends Super> pair2) {
switch(p) {
case SuperChild sc -> {}
case SuperRecord sr -> {}
}
switch (p) {
case SuperChild superChild -> {}
case SuperRecord(C i) -> {}
case SuperRecord(D i) -> {}
}
switch(p) {
case SuperChild sc -> {}
case SuperRecord sr -> {}
}
switch (pair1.x()) {
case SuperChild sc -> {}
case SuperRecord sr -> {}

View File

@@ -10,12 +10,12 @@ public class Totality {
void test(){
switch (recordInterface){
case <error descr="'switch' has both a total pattern and a default label">RecordInterface(I x, I y)</error> -> {}
case <error descr="'switch' has both a total pattern and a default label">default</error> -> {}
case RecordInterface(I x, I y) -> {}
case default -> {}
}
switch (recordInterface){
case <error descr="'switch' has both a total pattern and a default label">RecordInterface(I x, I y) r when true</error>-> {}
case <error descr="'switch' has both a total pattern and a default label">default</error> -> {}
case RecordInterface(I x, I y) r when true-> {}
case default -> {}
}
switch (recordInterface){
case RecordInterface(I x, I y) -> {}

View File

@@ -1,4 +1,6 @@
// "Remove unreachable branches" "true"
import org.jetbrains.annotations.*;
class Test {
void test(Object obj) {
if (!(obj instanceof Rect)) return;
@@ -6,5 +8,5 @@ class Test {
}
record Point(double x, double y) {}
record Rect(Point point1, Point point2) {}
record Rect(@NotNull Point point1, @NotNull Point point2) {}
}

View File

@@ -1,4 +1,6 @@
// "Remove unreachable branches" "true"
import org.jetbrains.annotations.*;
class Test {
void test(Object obj) {
if (!(obj instanceof Rect)) return;
@@ -6,5 +8,5 @@ class Test {
}
record Point(double x, double y) {}
record Rect(Point point1, Point point2) {}
record Rect(@NotNull Point point1, @NotNull Point point2) {}
}

View File

@@ -1,4 +1,6 @@
// "Remove unreachable branches" "true"
import org.jetbrains.annotations.*;
class Test {
void foo(Object obj) {
switch (obj) {
@@ -11,4 +13,4 @@ class Test {
}
}
record X(X x) { }
record X(@NotNull X x) { }

View File

@@ -1,4 +1,6 @@
// "Remove unreachable branches" "true"
import org.jetbrains.annotations.*;
class Test {
void foo(Object obj) {
switch (obj) {
@@ -13,4 +15,4 @@ class Test {
}
}
record X(X x) { }
record X(@NotNull X x) { }

View File

@@ -6,7 +6,7 @@ class Test {
default -> { return; }
}
((A) ((Rec) rec).i()).doA();
((A) rec.i()).doA();
}
}

View File

@@ -1,4 +1,6 @@
// "Remove unreachable branches" "true"
import org.jetbrains.annotations.*;
class Test {
void test(Object obj) {
if (!(obj instanceof Rect)) return;
@@ -12,5 +14,5 @@ class Test {
}
record Point(double x, double y) {}
record Rect(Point point1, Point point2) {}
record Rect(@NotNull Point point1, @NotNull Point point2) {}
}

View File

@@ -1,4 +1,6 @@
// "Remove unreachable branches" "true"
import org.jetbrains.annotations.*;
class Test {
void test(Object obj) {
if (!(obj instanceof Rect)) return;
@@ -12,5 +14,5 @@ class Test {
}
record Point(double x, double y) {}
record Rect(Point point1, Point point2) {}
record Rect(@NotNull Point point1, @NotNull Point point2) {}
}

View File

@@ -1,4 +1,6 @@
// "Remove unreachable branches" "true"
import org.jetbrains.annotations.*;
class Test {
void foo(Object obj) {
switch (obj) {
@@ -13,4 +15,4 @@ class Test {
}
}
record X(X x) { }
record X(@NotNull X x) { }

View File

@@ -1,4 +1,6 @@
// "Remove unreachable branches" "true"
import org.jetbrains.annotations.*;
class Test {
void foo(Object obj) {
switch (obj) {
@@ -17,4 +19,4 @@ class Test {
}
}
record X(X x) { }
record X(@NotNull X x) { }

View File

@@ -1,4 +1,6 @@
// "Unwrap 'if' statement extracting side effects" "true-preview"
import org.jetbrains.annotations.NotNull;
class Test {
void foo(Object obj) {
if (!(obj instanceof Rect)) {
@@ -25,5 +27,5 @@ record Point(double x, double y) {
record Size(double w, double h) {
}
record Rect(Point pos, Size size) {
record Rect(@NotNull Point pos, @NotNull Size size) {
}

View File

@@ -1,4 +1,6 @@
// "Unwrap 'if' statement extracting side effects" "true-preview"
import org.jetbrains.annotations.NotNull;
class Test {
void foo(Object obj) {
if (!(obj instanceof Rect)) {
@@ -20,5 +22,5 @@ record Point(double x, double y) {
record Size(double w, double h) {
}
record Rect(Point pos, Size size) {
record Rect(@NotNull Point pos, @NotNull Size size) {
}

View File

@@ -0,0 +1,15 @@
class Hello {
record X(X x) {}
static void foo(Object obj) {
if (!(obj instanceof X)) {
return;
}
System.out.println(obj instanceof X(X(X(X(X x)))));
}
public static void main(String[] args) {
foo(new X(new X(<warning descr="Passing 'null' argument to non-annotated parameter">null</warning>)));
}
}

View File

@@ -32,4 +32,7 @@ public class DataFlowInspection19Test extends DataFlowInspectionTestCase {
public void testRecordPatternAndWhen() {
doTest();
}
public void testNestedRecordPatterns() {
doTest();
}
}