From cd6a50432f5874966aa8eddf8786cfcb71d903e6 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 25 Mar 2024 10:40:08 -0700 Subject: [PATCH] Handle exhaustive switches on sealed types in MissingDefault PiperOrigin-RevId: 618889679 --- .../bugpatterns/MissingDefault.java | 23 +++++++++++++++++++ .../bugpatterns/MissingDefaultTest.java | 21 +++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/MissingDefault.java b/core/src/main/java/com/google/errorprone/bugpatterns/MissingDefault.java index 07fe35f2f59..5ea53624666 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/MissingDefault.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/MissingDefault.java @@ -33,6 +33,8 @@ import com.sun.source.tree.StatementTree; import com.sun.source.tree.SwitchTree; import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.tree.JCTree; +import java.lang.reflect.Field; import java.util.List; import java.util.Optional; import javax.lang.model.element.ElementKind; @@ -55,6 +57,9 @@ public Description matchSwitch(SwitchTree tree, VisitorState state) { } Optional maybeDefault = getSwitchDefault(tree); if (!maybeDefault.isPresent()) { + if (isExhaustive(tree)) { + return NO_MATCH; + } Description.Builder description = buildDescription(tree); if (!tree.getCases().isEmpty()) { // Inserting the default after the last case is easier than finding the closing brace @@ -95,4 +100,22 @@ public Description matchSwitch(SwitchTree tree, VisitorState state) { .addFix(SuggestedFix.postfixWith(defaultCase, " // fall out")) .build(); } + + private static final Field IS_EXHAUSTIVE = getIsExhaustive(); + + private static Field getIsExhaustive() { + try { + return JCTree.JCSwitch.class.getField("isExhaustive"); + } catch (NoSuchFieldException e) { + return null; + } + } + + private static boolean isExhaustive(SwitchTree tree) { + try { + return IS_EXHAUSTIVE != null && IS_EXHAUSTIVE.getBoolean(tree); + } catch (IllegalAccessException e) { + throw new LinkageError(e.getMessage(), e); + } + } } diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/MissingDefaultTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/MissingDefaultTest.java index 9799b78fe4f..977467e0d17 100644 --- a/core/src/test/java/com/google/errorprone/bugpatterns/MissingDefaultTest.java +++ b/core/src/test/java/com/google/errorprone/bugpatterns/MissingDefaultTest.java @@ -282,4 +282,25 @@ public void arrowComment() { "}") .doTest(); } + + @Test + public void exhaustiveExpressionSwitch() { + assumeTrue(RuntimeVersion.isAtLeast21()); + compilationHelper + .addSourceLines( + "Test.java", + "class Test {", + " sealed interface I permits A, B {}", + " abstract static class S {}", + " static final class A extends S implements I {}", + " static final class B extends S implements I {}", + " void f(I i) {", + " switch (i) {", + " case A a -> System.err.println(a);", + " case B b -> System.err.println(b);", + " };", + " }", + "}") + .doTest(); + } }