mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8366968: Exhaustive switch expression rejected by for not covering all possible values
Reviewed-by: abimpoudis
This commit is contained in:
parent
f3dfdfa3fd
commit
87a4772198
@ -210,7 +210,6 @@ public class ExhaustivenessComputer {
|
|||||||
if (clazz.isSealed() && clazz.isAbstract() &&
|
if (clazz.isSealed() && clazz.isAbstract() &&
|
||||||
//if a binding pattern for clazz already exists, no need to analyze it again:
|
//if a binding pattern for clazz already exists, no need to analyze it again:
|
||||||
!existingBindings.contains(clazz)) {
|
!existingBindings.contains(clazz)) {
|
||||||
ListBuffer<PatternDescription> bindings = new ListBuffer<>();
|
|
||||||
//do not reduce to types unrelated to the selector type:
|
//do not reduce to types unrelated to the selector type:
|
||||||
Type clazzErasure = types.erasure(clazz.type);
|
Type clazzErasure = types.erasure(clazz.type);
|
||||||
if (components(selectorType).stream()
|
if (components(selectorType).stream()
|
||||||
@ -230,30 +229,36 @@ public class ExhaustivenessComputer {
|
|||||||
return instantiated != null && types.isCastable(selectorType, instantiated);
|
return instantiated != null && types.isCastable(selectorType, instantiated);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//the set of pending permitted subtypes needed to cover clazz:
|
||||||
|
Set<Symbol> pendingPermitted = new HashSet<>(permitted);
|
||||||
|
|
||||||
for (PatternDescription pdOther : patterns) {
|
for (PatternDescription pdOther : patterns) {
|
||||||
if (pdOther instanceof BindingPattern bpOther) {
|
if (pdOther instanceof BindingPattern bpOther) {
|
||||||
Set<Symbol> currentPermittedSubTypes =
|
//remove all types from pendingPermitted that we can
|
||||||
allPermittedSubTypes(bpOther.type.tsym, s -> true);
|
//cover using bpOther:
|
||||||
|
|
||||||
PERMITTED: for (Iterator<Symbol> it = permitted.iterator(); it.hasNext();) {
|
//all types that are permitted subtypes of bpOther's type:
|
||||||
Symbol perm = it.next();
|
pendingPermitted.removeIf(pending -> types.isSubtype(types.erasure(pending.type),
|
||||||
|
types.erasure(bpOther.type)));
|
||||||
|
|
||||||
for (Symbol currentPermitted : currentPermittedSubTypes) {
|
if (bpOther.type.tsym.isAbstract()) {
|
||||||
if (types.isSubtype(types.erasure(currentPermitted.type),
|
//all types that are in a diamond hierarchy with bpOther's type
|
||||||
types.erasure(perm.type))) {
|
//i.e. there's a common subtype of the given type and bpOther's type:
|
||||||
it.remove();
|
Predicate<Symbol> check =
|
||||||
continue PERMITTED;
|
pending -> permitted.stream()
|
||||||
}
|
.filter(perm -> types.isSubtype(types.erasure(perm.type),
|
||||||
}
|
types.erasure(bpOther.type)))
|
||||||
if (types.isSubtype(types.erasure(perm.type),
|
.filter(perm -> types.isSubtype(types.erasure(perm.type),
|
||||||
types.erasure(bpOther.type))) {
|
types.erasure(pending.type)))
|
||||||
it.remove();
|
.findAny()
|
||||||
}
|
.isPresent();
|
||||||
|
|
||||||
|
pendingPermitted.removeIf(check);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permitted.isEmpty()) {
|
if (pendingPermitted.isEmpty()) {
|
||||||
toAdd.add(new BindingPattern(clazz.type));
|
toAdd.add(new BindingPattern(clazz.type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368
|
* @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368 8366968
|
||||||
* @summary Check exhaustiveness of switches over sealed types.
|
* @summary Check exhaustiveness of switches over sealed types.
|
||||||
* @library /tools/lib
|
* @library /tools/lib
|
||||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||||
@ -2182,6 +2182,135 @@ public class Exhaustiveness extends TestRunner {
|
|||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test //JDK-8366968
|
||||||
|
public void testNonSealedDiamond(Path base) throws Exception {
|
||||||
|
doTest(base,
|
||||||
|
new String[0],
|
||||||
|
"""
|
||||||
|
class Demo {
|
||||||
|
|
||||||
|
sealed interface Base permits Special, Value {}
|
||||||
|
|
||||||
|
non-sealed interface Value extends Base {}
|
||||||
|
|
||||||
|
sealed interface Special extends Base permits SpecialValue {}
|
||||||
|
|
||||||
|
non-sealed interface SpecialValue extends Value, Special {}
|
||||||
|
|
||||||
|
static int demo(final Base base) {
|
||||||
|
return switch (base) {
|
||||||
|
case Value value -> 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test //JDK-8366968
|
||||||
|
public void testNonSealedDiamond2(Path base) throws Exception {
|
||||||
|
doTest(base,
|
||||||
|
new String[0],
|
||||||
|
"""
|
||||||
|
class Demo {
|
||||||
|
|
||||||
|
sealed interface Base permits Special, Value {}
|
||||||
|
|
||||||
|
non-sealed interface Value extends Base {}
|
||||||
|
|
||||||
|
non-sealed interface Special extends Base {}
|
||||||
|
|
||||||
|
interface SpecialValue extends Value, Special {}
|
||||||
|
|
||||||
|
static int demo(final Base base) {
|
||||||
|
return switch (base) {
|
||||||
|
case Value value -> 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
"Demo.java:12:16: compiler.err.not.exhaustive",
|
||||||
|
"1 error");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test //JDK-8366968
|
||||||
|
public void testNonAbstract(Path base) throws Exception {
|
||||||
|
doTest(base,
|
||||||
|
new String[0],
|
||||||
|
"""
|
||||||
|
class Demo {
|
||||||
|
sealed interface I permits Base, C3 { }
|
||||||
|
sealed class Base implements I permits C1, C2 { }
|
||||||
|
final class C1 extends Base { }
|
||||||
|
final class C2 extends Base { }
|
||||||
|
final class C3 implements I { }
|
||||||
|
|
||||||
|
void method1(I i) {
|
||||||
|
switch (i) {
|
||||||
|
case C1 _ -> {}
|
||||||
|
case C2 _ -> {}
|
||||||
|
case C3 _ -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
"Demo.java:9:9: compiler.err.not.exhaustive.statement",
|
||||||
|
"1 error");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test //JDK-8366968
|
||||||
|
public void testNonSealedDiamondGeneric(Path base) throws Exception {
|
||||||
|
doTest(base,
|
||||||
|
new String[0],
|
||||||
|
"""
|
||||||
|
class Demo {
|
||||||
|
class SomeType {}
|
||||||
|
sealed interface Base<T extends SomeType> permits Special, Value {}
|
||||||
|
non-sealed interface Value<T extends SomeType> extends Base<T> {}
|
||||||
|
sealed interface Special<T extends SomeType> extends Base<T> permits SpecialValue {}
|
||||||
|
non-sealed interface SpecialValue<T extends SomeType> extends Value<T>, Special<T> {}
|
||||||
|
|
||||||
|
static <T extends SomeType> int demo(final Base<T> base) {
|
||||||
|
return switch (base) {
|
||||||
|
case Value<T> value -> 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test //JDK-8366968
|
||||||
|
public void testNonSealedDiamondMultiple(Path base) throws Exception {
|
||||||
|
doTest(base,
|
||||||
|
new String[0],
|
||||||
|
"""
|
||||||
|
class Demo {
|
||||||
|
|
||||||
|
sealed interface Base permits Special, Value {}
|
||||||
|
|
||||||
|
non-sealed interface Value extends Base {}
|
||||||
|
|
||||||
|
sealed interface Special extends Base permits SpecialValue, Special2 {}
|
||||||
|
|
||||||
|
non-sealed interface SpecialValue extends Value, Special {}
|
||||||
|
non-sealed interface Special2 extends Special {}
|
||||||
|
|
||||||
|
static int demo(final Base base) {
|
||||||
|
return switch (base) {
|
||||||
|
case Value value -> 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
"Demo.java:13:16: compiler.err.not.exhaustive",
|
||||||
|
"1 error");
|
||||||
|
}
|
||||||
|
|
||||||
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
|
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
|
||||||
doTest(base, libraryCode, testCode, false, expectedErrors);
|
doTest(base, libraryCode, testCode, false, expectedErrors);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user