mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-09 21:19:38 +00:00
8383414: Exhaustiveness rejects a type pattern that covers all permitted subtypes
Reviewed-by: abimpoudis
This commit is contained in:
parent
f74a4476ee
commit
5ccf99b8e9
@ -223,15 +223,31 @@ public class ExhaustivenessComputer {
|
||||
|
||||
private boolean checkCovered(Type seltype, Iterable<PatternDescription> patterns) {
|
||||
for (Type seltypeComponent : components(seltype)) {
|
||||
for (PatternDescription pd : patterns) {
|
||||
if(isBpCovered(seltypeComponent, pd)) {
|
||||
return true;
|
||||
}
|
||||
if (isCoveredBy(seltypeComponent, patterns)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isCoveredBy(Type seltype, Iterable<PatternDescription> patterns) {
|
||||
for (PatternDescription pd : patterns) {
|
||||
if(isBpCovered(seltype, pd)) {
|
||||
return true;
|
||||
}
|
||||
if (seltype.tsym.isSealed() && seltype.tsym.isAbstract()) {
|
||||
boolean allDirectPermittedSubtypesPermitted =
|
||||
directPermittedSubTypes(seltype)
|
||||
.map(csym -> instantiatePatternType(seltype, csym))
|
||||
.allMatch(currentPermitted -> isCoveredBy(currentPermitted, patterns));
|
||||
|
||||
return allDirectPermittedSubtypesPermitted;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<Type> components(Type seltype) {
|
||||
return switch (seltype.getTag()) {
|
||||
case CLASS -> {
|
||||
@ -244,10 +260,10 @@ public class ExhaustivenessComputer {
|
||||
}
|
||||
yield List.nil();
|
||||
}
|
||||
yield List.of(types.erasure(seltype));
|
||||
yield List.of(seltype);
|
||||
}
|
||||
case TYPEVAR -> components(((TypeVar) seltype).getUpperBound());
|
||||
default -> List.of(types.erasure(seltype));
|
||||
default -> List.of(seltype);
|
||||
};
|
||||
}
|
||||
|
||||
@ -371,6 +387,14 @@ public class ExhaustivenessComputer {
|
||||
}
|
||||
}
|
||||
|
||||
private Stream<TypeSymbol> directPermittedSubTypes(Type type) {
|
||||
List<Type> permitted = ((ClassSymbol) type.tsym).getPermittedSubclasses();
|
||||
|
||||
return permitted.stream()
|
||||
.map(permittedType -> permittedType.tsym)
|
||||
.filter(isApplicableSubtypePredicate(type));
|
||||
}
|
||||
|
||||
private Set<ClassSymbol> leafPermittedSubTypes(TypeSymbol root, Predicate<ClassSymbol> accept) {
|
||||
Set<ClassSymbol> permitted = new HashSet<>();
|
||||
List<ClassSymbol> permittedSubtypesClosure = baseClasses(root);
|
||||
@ -877,11 +901,8 @@ public class ExhaustivenessComputer {
|
||||
if (toExpand instanceof BindingPattern bp) {
|
||||
if (bp.type.tsym.isSealed()) {
|
||||
//try to replace binding patterns for sealed types with all their immediate permitted applicable types:
|
||||
List<Type> permitted = ((ClassSymbol) bp.type.tsym).getPermittedSubclasses();
|
||||
Set<PatternDescription> applicableDirectPermittedPatterns =
|
||||
permitted.stream()
|
||||
.map(type -> type.tsym)
|
||||
.filter(isApplicableSubtypePredicate(targetType))
|
||||
directPermittedSubTypes(bp.type)
|
||||
.map(csym -> new BindingPattern(types.erasure(csym.type)))
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368 8366968 8364991
|
||||
* @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368 8366968 8364991 8383414
|
||||
* @summary Check exhaustiveness of switches over sealed types.
|
||||
* @library /tools/lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
@ -1486,9 +1486,7 @@ public class Exhaustiveness extends TestRunner {
|
||||
}
|
||||
}
|
||||
}
|
||||
""",
|
||||
"Test.java:11:9: compiler.err.not.exhaustive.statement",
|
||||
"1 error");
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -2487,6 +2485,96 @@ public class Exhaustiveness extends TestRunner {
|
||||
"1 error");
|
||||
}
|
||||
|
||||
@Test //JDK-8383414
|
||||
public void testVerifyCoverageCheckWithApplicablePermittedSubTypes(Path base) throws Exception {
|
||||
doTest(base,
|
||||
new String[0],
|
||||
"""
|
||||
class Test {
|
||||
interface J {}
|
||||
static sealed interface II<T> permits A2, B2 {}
|
||||
static final class A2 implements II<Long>, J {}
|
||||
static final class B2 implements II<Long>, J {}
|
||||
static void f(II<Long> i) {
|
||||
switch (i) {
|
||||
case J j -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
//deeper hierarchy:
|
||||
doTest(base,
|
||||
new String[0],
|
||||
"""
|
||||
class Test {
|
||||
interface J {}
|
||||
static sealed interface II<T> permits Med, B2 {}
|
||||
static sealed interface Med extends II<Long> {}
|
||||
static final class A2 implements Med, J {}
|
||||
static final class B2 implements II<Long>, J {}
|
||||
static void f(II<Long> i) {
|
||||
switch (i) {
|
||||
case J j -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
//even deeper hierarchy:
|
||||
doTest(base,
|
||||
new String[0],
|
||||
"""
|
||||
class Test {
|
||||
interface J {}
|
||||
static sealed interface II<T> permits Med1, B2 {}
|
||||
static sealed interface Med1 extends II<Long> {}
|
||||
static sealed interface Med2 extends Med1 {}
|
||||
static sealed interface Med3 extends Med2 {}
|
||||
static sealed interface Med4 extends Med3 {}
|
||||
static sealed interface Med5 extends Med4 {}
|
||||
static final class A2 implements Med5, J {}
|
||||
static final class B2 implements II<Long>, J {}
|
||||
static void f(II<Long> i) {
|
||||
switch (i) {
|
||||
case J j -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
//a class that does not implement J, but is not applicable:
|
||||
doTest(base,
|
||||
new String[0],
|
||||
"""
|
||||
class Test {
|
||||
interface J {}
|
||||
static sealed interface II<T> permits A2, B2 {}
|
||||
static final class A2 implements II<String> {}
|
||||
static final class B2 implements II<Long>, J {}
|
||||
static void f(II<Long> i) {
|
||||
switch (i) {
|
||||
case J j -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
doTest(base,
|
||||
new String[0],
|
||||
"""
|
||||
class Test {
|
||||
interface J1 {}
|
||||
interface J2 {}
|
||||
static sealed interface II<T> permits A2, B2 {}
|
||||
static final class A2 implements II<Long>, J1 {}
|
||||
static final class B2 implements II<Long>, J2 {}
|
||||
static void f(II<Long> i) {
|
||||
switch (i) {
|
||||
case J1 j -> {}
|
||||
case J2 j -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
|
||||
doTest(base, libraryCode, testCode, false, expectedErrors);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user