[java] IDEA-391221 Support JEP 532. Detect "MatchException" with primitive type patterns

(cherry picked from commit 79f111cf01c2a56d8cdab062a2be376cbedcaf62)

GitOrigin-RevId: 300914c986a54438394eaa52d48c0a92f610c12d
This commit is contained in:
Mikhail Pyltsin
2026-07-23 10:59:41 +00:00
committed by intellij-monorepo-bot
parent b8bcd55bae
commit 0fd4c1187d
4 changed files with 138 additions and 4 deletions
@@ -1,4 +1,4 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeserver.core;
import com.intellij.codeInsight.ExpressionUtil;
@@ -26,6 +26,7 @@ import com.intellij.psi.PsiKeyword;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiPattern;
import com.intellij.psi.PsiPatternVariable;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiRecordComponent;
import com.intellij.psi.PsiReferenceExpression;
@@ -33,6 +34,7 @@ import com.intellij.psi.PsiStatement;
import com.intellij.psi.PsiSwitchBlock;
import com.intellij.psi.PsiSwitchLabelStatementBase;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiTypeTestPattern;
import com.intellij.psi.PsiTypes;
import com.intellij.psi.util.ConstantExpressionUtil;
import com.intellij.psi.util.JavaPsiPatternUtil;
@@ -501,6 +503,20 @@ public final class JavaPsiSwitchUtil {
return true;
}
}
if (PsiUtil.isAvailable(JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS, deconstructionComponent) &&
deconstructionComponent instanceof PsiTypeTestPattern testPattern) {
PsiPatternVariable patternVariable = testPattern.getPatternVariable();
if (patternVariable != null && patternVariable.getType() instanceof PsiPrimitiveType) {
if (!hasDominated(switchBlock,
topLevelDeconstruction,
deconstructionComponent,
recordComponentClass,
skipDominatingElements)) {
return true;
}
}
}
return false;
}
@@ -0,0 +1,116 @@
import org.jetbrains.annotations.Nullable;
class MatchExceptionWithPrimitivePatterns {
interface Case {
String run();
}
static void check(String name, Case c) {
try {
System.out.println(name + " => OK[" + c.run() + "]");
}
catch (Throwable t) {
System.out.println(name + " => " + t.getClass().getSimpleName());
}
}
record Point(int x, int y) {
}
record IntBox(@Nullable Integer v) {
}
record LongBox(@Nullable Long v) {
}
record DoubleBox(@Nullable Double v) {
}
record BoolBox(@Nullable Boolean v) {
}
record WidenBox(@Nullable Integer v) {
} // Integer component matched by a wider primitive
record PointBox(@Nullable Point p) {
}
static void main() {
check("primInt", () -> primInt(new IntBox(null)));
check("primIntWithUncond", () -> primIntWithUncond(new IntBox(null)));
check("primLong", () -> primLong(new LongBox(null)));
check("primLongWithUncond", () -> primLongWithUncond(new LongBox(null)));
check("primDouble", () -> primDouble(new DoubleBox(null)));
check("primDoubleWithUncond", () -> primDoubleWithUncond(new DoubleBox(null)));
check("primBoolean", () -> primBoolean(new BoolBox(null)));
check("primWiden", () -> primWiden(new WidenBox(null)));
check("recordComponent_control", () -> recordComponent_control(new PointBox(null)));
check("typePattern_control", () -> typePattern_control(new PointBox(null)));
}
static String primInt(IntBox b) {
return switch (b) {
case <warning descr="Switch label 'IntBox(int p)' is the only reachable in the whole switch">IntBox(<warning descr="Pattern matching may throw 'MatchException'">int p</warning>)</warning> -> "int:" + p;
};
}
static String primIntWithUncond(IntBox b) {
return switch (b) {
case IntBox(int p) -> "int:" + p;
default -> "primIntWithUncond(" + b + ")";
};
}
static String primLong(LongBox b) {
return switch (b) {
case <warning descr="Switch label 'LongBox(long p)' is the only reachable in the whole switch">LongBox(<warning descr="Pattern matching may throw 'MatchException'">long p</warning>)</warning> -> "long:" + p;
};
}
static String primLongWithUncond(LongBox b) {
return switch (b) {
case LongBox(long p) -> "long:" + p;
case LongBox(Long v) -> "primLongWithUncond(" + v + ")";
};
}
static String primDouble(DoubleBox b) {
return switch (b) {
case <warning descr="Switch label 'DoubleBox(double p)' is the only reachable in the whole switch">DoubleBox(<warning descr="Pattern matching may throw 'MatchException'">double p</warning>)</warning> -> "double:" + p;
};
}
static String primDoubleWithUncond(DoubleBox b) {
return switch (b) {
case DoubleBox(double p) -> "double:" + p;
case DoubleBox(Object p) -> "primDoubleWithUncond(" + p + ")";
};
}
static String primBoolean(BoolBox b) {
return switch (b) {
case <warning descr="Switch label 'BoolBox(boolean p)' is the only reachable in the whole switch">BoolBox(<warning descr="Pattern matching may throw 'MatchException'">boolean p</warning>)</warning> -> "bool:" + p;
};
}
static String primWiden(WidenBox b) {
return switch (b) {
case WidenBox(<warning descr="Pattern matching may throw 'MatchException'">float p</warning>) -> "widen float:" + p;
case WidenBox(double p) -> "widen:" + p;
};
}
static String recordComponent_control(PointBox b) {
return switch (b) {
case <warning descr="Switch label 'PointBox(Point(var x, var y))' is the only reachable in the whole switch">PointBox(<warning descr="Pattern matching may throw 'MatchException'">Point(var x, var y)</warning>)</warning> -> "pt:" + x + "," + y;
};
}
static String typePattern_control(PointBox b) {
return switch (b) {
case <warning descr="Switch label 'PointBox(Point p)' is the only reachable in the whole switch">PointBox(Point p)</warning> -> "p=" + p;
};
}
}
@@ -9,8 +9,8 @@ public class SwitchWithPrimitive {
public static int exhaustiveByPrimitives(RecordCharObj source) {
switch (source) {
case RecordCharObj(char source1) -> System.out.println("1");
case <warning descr="Switch label 'RecordCharObj(int source1)' is unreachable">RecordCharObj(int source1)</warning> -> System.out.println("1");
case <warning descr="Switch label 'RecordCharObj(char source1)' is the only reachable in the whole switch">RecordCharObj(char source1)</warning> -> System.out.println("1");
case RecordCharObj(int source1) -> System.out.println("1");
}
return 1;
}
@@ -30,6 +30,8 @@ public class DataFlowInspection26Test extends DataFlowInspectionTestCase {
public V get();
}
""");
doTestWith((dfi, cvi) -> dfi.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = false);
doTestWith((dfi, _) -> dfi.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = false);
}
public void testMatchExceptionWithPrimitivePatterns() { doTest(); }
}