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..0187420199f 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/MissingDefault.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/MissingDefault.java @@ -33,9 +33,12 @@ 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; +import org.jspecify.annotations.Nullable; /** A {@link BugChecker}; see the associated {@link BugPattern} annotation for details. */ @BugPattern( @@ -55,6 +58,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 +101,22 @@ public Description matchSwitch(SwitchTree tree, VisitorState state) { .addFix(SuggestedFix.postfixWith(defaultCase, " // fall out")) .build(); } + + private static final Field IS_EXHAUSTIVE = getIsExhaustive(); + + private static @Nullable 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..0723688d33e 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.isAtLeast14()); + 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(); + } }