8281100: Spurious "variable might not have been initialized" with sealed class switch
Reviewed-by: vromero
This commit is contained in:
parent
d254cf28c5
commit
4ff5824f5b
src/jdk.compiler/share/classes/com/sun/tools/javac
test/langtools/tools/javac/patterns
@ -664,10 +664,7 @@ public class Flow {
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
pendingExits = new ListBuffer<>();
|
||||
scan(tree.selector);
|
||||
boolean exhaustiveSwitch = tree.patternSwitch ||
|
||||
tree.cases.stream()
|
||||
.flatMap(c -> c.labels.stream())
|
||||
.anyMatch(l -> TreeInfo.isNull(l));
|
||||
boolean exhaustiveSwitch = TreeInfo.expectedExhaustive(tree);
|
||||
Set<Symbol> constants = exhaustiveSwitch ? new HashSet<>() : null;
|
||||
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
|
||||
alive = Liveness.ALIVE;
|
||||
@ -689,10 +686,13 @@ public class Flow {
|
||||
l.tail.head.pos(),
|
||||
Warnings.PossibleFallThroughIntoCase);
|
||||
}
|
||||
if (!tree.hasTotalPattern && exhaustiveSwitch &&
|
||||
!TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) &&
|
||||
(constants == null || !isExhaustive(tree.selector.pos(), tree.selector.type, constants))) {
|
||||
log.error(tree, Errors.NotExhaustiveStatement);
|
||||
tree.isExhaustive = tree.hasTotalPattern ||
|
||||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases);
|
||||
if (exhaustiveSwitch) {
|
||||
tree.isExhaustive |= isExhaustive(tree.selector.pos(), tree.selector.type, constants);
|
||||
if (!tree.isExhaustive) {
|
||||
log.error(tree, Errors.NotExhaustiveStatement);
|
||||
}
|
||||
}
|
||||
if (!tree.hasTotalPattern) {
|
||||
alive = Liveness.ALIVE;
|
||||
@ -725,8 +725,10 @@ public class Flow {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!tree.hasTotalPattern && !TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) &&
|
||||
!isExhaustive(tree.selector.pos(), tree.selector.type, constants)) {
|
||||
tree.isExhaustive = tree.hasTotalPattern ||
|
||||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) ||
|
||||
isExhaustive(tree.selector.pos(), tree.selector.type, constants);
|
||||
if (!tree.isExhaustive) {
|
||||
log.error(tree, Errors.NotExhaustive);
|
||||
}
|
||||
alive = prevAlive;
|
||||
@ -2432,15 +2434,15 @@ public class Flow {
|
||||
}
|
||||
|
||||
public void visitSwitch(JCSwitch tree) {
|
||||
handleSwitch(tree, tree.selector, tree.cases, tree.hasTotalPattern);
|
||||
handleSwitch(tree, tree.selector, tree.cases, tree.isExhaustive);
|
||||
}
|
||||
|
||||
public void visitSwitchExpression(JCSwitchExpression tree) {
|
||||
handleSwitch(tree, tree.selector, tree.cases, tree.hasTotalPattern);
|
||||
handleSwitch(tree, tree.selector, tree.cases, tree.isExhaustive);
|
||||
}
|
||||
|
||||
private void handleSwitch(JCTree tree, JCExpression selector,
|
||||
List<JCCase> cases, boolean hasTotalPattern) {
|
||||
List<JCCase> cases, boolean isExhaustive) {
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
pendingExits = new ListBuffer<>();
|
||||
int nextadrPrev = nextadr;
|
||||
@ -2478,10 +2480,10 @@ public class Flow {
|
||||
addVars(c.stats, initsSwitch, uninitsSwitch);
|
||||
// Warn about fall-through if lint switch fallthrough enabled.
|
||||
}
|
||||
if (!hasTotalPattern) {
|
||||
if (!isExhaustive) {
|
||||
if (tree.hasTag(SWITCH_EXPRESSION)) {
|
||||
markDead();
|
||||
} else {
|
||||
} else if (tree.hasTag(SWITCH) && !TreeInfo.expectedExhaustive((JCSwitch) tree)) {
|
||||
inits.assign(initsSwitch);
|
||||
uninits.assign(uninits.andSet(uninitsSwitch));
|
||||
}
|
||||
|
@ -1285,6 +1285,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
/** Position of closing brace, optional. */
|
||||
public int endpos = Position.NOPOS;
|
||||
public boolean hasTotalPattern;
|
||||
public boolean isExhaustive;
|
||||
public boolean patternSwitch;
|
||||
protected JCSwitch(JCExpression selector, List<JCCase> cases) {
|
||||
this.selector = selector;
|
||||
@ -1371,6 +1372,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
/** Position of closing brace, optional. */
|
||||
public int endpos = Position.NOPOS;
|
||||
public boolean hasTotalPattern;
|
||||
public boolean isExhaustive;
|
||||
public boolean patternSwitch;
|
||||
protected JCSwitchExpression(JCExpression selector, List<JCCase> cases) {
|
||||
this.selector = selector;
|
||||
|
@ -1384,4 +1384,10 @@ public class TreeInfo {
|
||||
|
||||
public record PatternPrimaryType(Type type, boolean unconditional) {}
|
||||
|
||||
public static boolean expectedExhaustive(JCSwitch tree) {
|
||||
return tree.patternSwitch ||
|
||||
tree.cases.stream()
|
||||
.flatMap(c -> c.labels.stream())
|
||||
.anyMatch(l -> TreeInfo.isNull(l));
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8262891 8268871 8274363
|
||||
* @bug 8262891 8268871 8274363 8281100
|
||||
* @summary Check exhaustiveness of switches over sealed types.
|
||||
* @library /tools/lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
@ -791,6 +791,82 @@ public class Exhaustiveness extends TestRunner {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefiniteAssignment(Path base) throws Exception {
|
||||
doTest(base,
|
||||
new String[]{"""
|
||||
package lib;
|
||||
public sealed interface S permits A, B {}
|
||||
""",
|
||||
"""
|
||||
package lib;
|
||||
public final class A implements S {}
|
||||
""",
|
||||
"""
|
||||
package lib;
|
||||
public final class B implements S {}
|
||||
"""},
|
||||
"""
|
||||
package test;
|
||||
import lib.*;
|
||||
public class Test {
|
||||
private void testStatement(S obj) {
|
||||
int data;
|
||||
switch (obj) {
|
||||
case A a -> data = 0;
|
||||
case B b -> data = 0;
|
||||
};
|
||||
System.err.println(data);
|
||||
}
|
||||
private void testExpression(S obj) {
|
||||
int data;
|
||||
int v = switch (obj) {
|
||||
case A a -> data = 0;
|
||||
case B b -> data = 0;
|
||||
};
|
||||
System.err.println(data);
|
||||
}
|
||||
private void testStatementNotExhaustive(S obj) {
|
||||
int data;
|
||||
switch (obj) {
|
||||
case A a -> data = 0;
|
||||
};
|
||||
System.err.println(data);
|
||||
}
|
||||
private void testExpressionNotExhaustive(S obj) {
|
||||
int data;
|
||||
int v = switch (obj) {
|
||||
case A a -> data = 0;
|
||||
};
|
||||
System.err.println(data);
|
||||
}
|
||||
private void testStatementErrorEnum(E e) { //"E" is intentionally unresolvable
|
||||
int data;
|
||||
switch (e) {
|
||||
case A -> data = 0;
|
||||
case B -> data = 0;
|
||||
};
|
||||
System.err.println(data);
|
||||
}
|
||||
private void testExpressionErrorEnum(E e) { //"E" is intentionally unresolvable
|
||||
int data;
|
||||
int v = switch (e) {
|
||||
case A -> data = 0;
|
||||
case B -> data = 0;
|
||||
};
|
||||
System.err.println(data);
|
||||
}
|
||||
}
|
||||
""",
|
||||
"Test.java:34:41: compiler.err.cant.resolve.location: kindname.class, E, , , (compiler.misc.location: kindname.class, test.Test, null)",
|
||||
"Test.java:42:42: compiler.err.cant.resolve.location: kindname.class, E, , , (compiler.misc.location: kindname.class, test.Test, null)",
|
||||
"Test.java:22:9: compiler.err.not.exhaustive.statement",
|
||||
"Test.java:29:17: compiler.err.not.exhaustive",
|
||||
"- compiler.note.preview.filename: Test.java, DEFAULT",
|
||||
"- compiler.note.preview.recompile",
|
||||
"4 errors");
|
||||
}
|
||||
|
||||
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
|
||||
Path current = base.resolve(".");
|
||||
Path libClasses = current.resolve("libClasses");
|
||||
@ -825,7 +901,8 @@ public class Exhaustiveness extends TestRunner {
|
||||
"-source", JAVA_VERSION,
|
||||
"-XDrawDiagnostics",
|
||||
"-Xlint:-preview",
|
||||
"--class-path", libClasses.toString())
|
||||
"--class-path", libClasses.toString(),
|
||||
"-XDshould-stop.at=FLOW")
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(expectedErrors.length > 0 ? Task.Expect.FAIL : Task.Expect.SUCCESS)
|
||||
|
Loading…
x
Reference in New Issue
Block a user