From 0fd4c1187d8e5321417e37be9e56089ce8a4dfeb Mon Sep 17 00:00:00 2001 From: Mikhail Pyltsin Date: Fri, 3 Jul 2026 15:55:31 +0200 Subject: [PATCH] [java] IDEA-391221 Support JEP 532. Detect "MatchException" with primitive type patterns (cherry picked from commit 79f111cf01c2a56d8cdab062a2be376cbedcaf62) GitOrigin-RevId: 300914c986a54438394eaa52d48c0a92f610c12d --- .../codeserver/core/JavaPsiSwitchUtil.java | 18 ++- .../MatchExceptionWithPrimitivePatterns.java | 116 ++++++++++++++++++ .../dataFlow/fixture/SwitchWithPrimitive.java | 4 +- .../DataFlowInspection26Test.java | 4 +- 4 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/MatchExceptionWithPrimitivePatterns.java diff --git a/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiSwitchUtil.java b/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiSwitchUtil.java index 7b0eeaf0eec4..dfe635019dfd 100644 --- a/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiSwitchUtil.java +++ b/java/codeserver/core/src/com/intellij/java/codeserver/core/JavaPsiSwitchUtil.java @@ -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; } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/MatchExceptionWithPrimitivePatterns.java b/java/java-tests/testData/inspection/dataFlow/fixture/MatchExceptionWithPrimitivePatterns.java new file mode 100644 index 000000000000..47fb5f503695 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/MatchExceptionWithPrimitivePatterns.java @@ -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 IntBox(int p) -> "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 LongBox(long p) -> "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 DoubleBox(double p) -> "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 BoolBox(boolean p) -> "bool:" + p; + }; + } + + static String primWiden(WidenBox b) { + return switch (b) { + case WidenBox(float p) -> "widen float:" + p; + case WidenBox(double p) -> "widen:" + p; + }; + } + + static String recordComponent_control(PointBox b) { + return switch (b) { + case PointBox(Point(var x, var y)) -> "pt:" + x + "," + y; + }; + } + + static String typePattern_control(PointBox b) { + return switch (b) { + case PointBox(Point p) -> "p=" + p; + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/SwitchWithPrimitive.java b/java/java-tests/testData/inspection/dataFlow/fixture/SwitchWithPrimitive.java index 49410c69d711..b12ba679b3ff 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/SwitchWithPrimitive.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/SwitchWithPrimitive.java @@ -9,8 +9,8 @@ public class SwitchWithPrimitive { public static int exhaustiveByPrimitives(RecordCharObj source) { switch (source) { - case RecordCharObj(char source1) -> System.out.println("1"); - case RecordCharObj(int source1) -> System.out.println("1"); + case RecordCharObj(char source1) -> System.out.println("1"); + case RecordCharObj(int source1) -> System.out.println("1"); } return 1; } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection26Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection26Test.java index d87567ec5f40..8a3c6898ea58 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection26Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection26Test.java @@ -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(); } } \ No newline at end of file