mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8212982: Rule cases in switch expression accepted even if complete normally
Ensure an error is reported if switch expression does not correctly produce a value. Reviewed-by: mcimadamore
This commit is contained in:
parent
9314c7f110
commit
60b32f222f
10 changed files with 280 additions and 55 deletions
|
@ -357,9 +357,9 @@ public class Flow {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resolve all jumps of this statement. */
|
/** Resolve all jumps of this statement. */
|
||||||
private boolean resolveJump(JCTree tree,
|
private Liveness resolveJump(JCTree tree,
|
||||||
ListBuffer<P> oldPendingExits,
|
ListBuffer<P> oldPendingExits,
|
||||||
JumpKind jk) {
|
JumpKind jk) {
|
||||||
boolean resolved = false;
|
boolean resolved = false;
|
||||||
List<P> exits = pendingExits.toList();
|
List<P> exits = pendingExits.toList();
|
||||||
pendingExits = oldPendingExits;
|
pendingExits = oldPendingExits;
|
||||||
|
@ -373,16 +373,16 @@ public class Flow {
|
||||||
pendingExits.append(exit);
|
pendingExits.append(exit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resolved;
|
return Liveness.from(resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resolve all continues of this statement. */
|
/** Resolve all continues of this statement. */
|
||||||
boolean resolveContinues(JCTree tree) {
|
Liveness resolveContinues(JCTree tree) {
|
||||||
return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE);
|
return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resolve all breaks of this statement. */
|
/** Resolve all breaks of this statement. */
|
||||||
boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) {
|
Liveness resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) {
|
||||||
return resolveJump(tree, oldPendingExits, JumpKind.BREAK);
|
return resolveJump(tree, oldPendingExits, JumpKind.BREAK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,11 +417,11 @@ public class Flow {
|
||||||
/** A flag that indicates whether the last statement could
|
/** A flag that indicates whether the last statement could
|
||||||
* complete normally.
|
* complete normally.
|
||||||
*/
|
*/
|
||||||
private boolean alive;
|
private Liveness alive;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void markDead() {
|
void markDead() {
|
||||||
alive = false;
|
alive = Liveness.DEAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
|
@ -432,7 +432,7 @@ public class Flow {
|
||||||
*/
|
*/
|
||||||
void scanDef(JCTree tree) {
|
void scanDef(JCTree tree) {
|
||||||
scanStat(tree);
|
scanStat(tree);
|
||||||
if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) {
|
if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && alive == Liveness.DEAD) {
|
||||||
log.error(tree.pos(),
|
log.error(tree.pos(),
|
||||||
Errors.InitializerMustBeAbleToCompleteNormally);
|
Errors.InitializerMustBeAbleToCompleteNormally);
|
||||||
}
|
}
|
||||||
|
@ -441,9 +441,9 @@ public class Flow {
|
||||||
/** Analyze a statement. Check that statement is reachable.
|
/** Analyze a statement. Check that statement is reachable.
|
||||||
*/
|
*/
|
||||||
void scanStat(JCTree tree) {
|
void scanStat(JCTree tree) {
|
||||||
if (!alive && tree != null) {
|
if (alive == Liveness.DEAD && tree != null) {
|
||||||
log.error(tree.pos(), Errors.UnreachableStmt);
|
log.error(tree.pos(), Errors.UnreachableStmt);
|
||||||
if (!tree.hasTag(SKIP)) alive = true;
|
if (!tree.hasTag(SKIP)) alive = Liveness.RECOVERY;
|
||||||
}
|
}
|
||||||
scan(tree);
|
scan(tree);
|
||||||
}
|
}
|
||||||
|
@ -460,7 +460,7 @@ public class Flow {
|
||||||
|
|
||||||
public void visitClassDef(JCClassDecl tree) {
|
public void visitClassDef(JCClassDecl tree) {
|
||||||
if (tree.sym == null) return;
|
if (tree.sym == null) return;
|
||||||
boolean alivePrev = alive;
|
Liveness alivePrev = alive;
|
||||||
ListBuffer<PendingExit> pendingExitsPrev = pendingExits;
|
ListBuffer<PendingExit> pendingExitsPrev = pendingExits;
|
||||||
Lint lintPrev = lint;
|
Lint lintPrev = lint;
|
||||||
|
|
||||||
|
@ -506,10 +506,10 @@ public class Flow {
|
||||||
Assert.check(pendingExits.isEmpty());
|
Assert.check(pendingExits.isEmpty());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
scanStat(tree.body);
|
scanStat(tree.body);
|
||||||
|
|
||||||
if (alive && !tree.sym.type.getReturnType().hasTag(VOID))
|
if (alive == Liveness.ALIVE && !tree.sym.type.getReturnType().hasTag(VOID))
|
||||||
log.error(TreeInfo.diagEndPos(tree.body), Errors.MissingRetStmt);
|
log.error(TreeInfo.diagEndPos(tree.body), Errors.MissingRetStmt);
|
||||||
|
|
||||||
List<PendingExit> exits = pendingExits.toList();
|
List<PendingExit> exits = pendingExits.toList();
|
||||||
|
@ -544,21 +544,21 @@ public class Flow {
|
||||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
scanStat(tree.body);
|
scanStat(tree.body);
|
||||||
alive |= resolveContinues(tree);
|
alive = alive.or(resolveContinues(tree));
|
||||||
scan(tree.cond);
|
scan(tree.cond);
|
||||||
alive = alive && !tree.cond.type.isTrue();
|
alive = alive.and(!tree.cond.type.isTrue());
|
||||||
alive |= resolveBreaks(tree, prevPendingExits);
|
alive = alive.or(resolveBreaks(tree, prevPendingExits));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitWhileLoop(JCWhileLoop tree) {
|
public void visitWhileLoop(JCWhileLoop tree) {
|
||||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
scan(tree.cond);
|
scan(tree.cond);
|
||||||
alive = !tree.cond.type.isFalse();
|
alive = Liveness.from(!tree.cond.type.isFalse());
|
||||||
scanStat(tree.body);
|
scanStat(tree.body);
|
||||||
alive |= resolveContinues(tree);
|
alive = alive.or(resolveContinues(tree));
|
||||||
alive = resolveBreaks(tree, prevPendingExits) ||
|
alive = resolveBreaks(tree, prevPendingExits).or(
|
||||||
!tree.cond.type.isTrue();
|
!tree.cond.type.isTrue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitForLoop(JCForLoop tree) {
|
public void visitForLoop(JCForLoop tree) {
|
||||||
|
@ -567,15 +567,15 @@ public class Flow {
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
if (tree.cond != null) {
|
if (tree.cond != null) {
|
||||||
scan(tree.cond);
|
scan(tree.cond);
|
||||||
alive = !tree.cond.type.isFalse();
|
alive = Liveness.from(!tree.cond.type.isFalse());
|
||||||
} else {
|
} else {
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
}
|
}
|
||||||
scanStat(tree.body);
|
scanStat(tree.body);
|
||||||
alive |= resolveContinues(tree);
|
alive = alive.or(resolveContinues(tree));
|
||||||
scan(tree.step);
|
scan(tree.step);
|
||||||
alive = resolveBreaks(tree, prevPendingExits) ||
|
alive = resolveBreaks(tree, prevPendingExits).or(
|
||||||
tree.cond != null && !tree.cond.type.isTrue();
|
tree.cond != null && !tree.cond.type.isTrue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
|
@ -584,16 +584,16 @@ public class Flow {
|
||||||
scan(tree.expr);
|
scan(tree.expr);
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
scanStat(tree.body);
|
scanStat(tree.body);
|
||||||
alive |= resolveContinues(tree);
|
alive = alive.or(resolveContinues(tree));
|
||||||
resolveBreaks(tree, prevPendingExits);
|
resolveBreaks(tree, prevPendingExits);
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitLabelled(JCLabeledStatement tree) {
|
public void visitLabelled(JCLabeledStatement tree) {
|
||||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
scanStat(tree.body);
|
scanStat(tree.body);
|
||||||
alive |= resolveBreaks(tree, prevPendingExits);
|
alive = alive.or(resolveBreaks(tree, prevPendingExits));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitSwitch(JCSwitch tree) {
|
public void visitSwitch(JCSwitch tree) {
|
||||||
|
@ -602,7 +602,7 @@ public class Flow {
|
||||||
scan(tree.selector);
|
scan(tree.selector);
|
||||||
boolean hasDefault = false;
|
boolean hasDefault = false;
|
||||||
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
|
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
JCCase c = l.head;
|
JCCase c = l.head;
|
||||||
if (c.pats.isEmpty())
|
if (c.pats.isEmpty())
|
||||||
hasDefault = true;
|
hasDefault = true;
|
||||||
|
@ -612,13 +612,13 @@ public class Flow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scanStats(c.stats);
|
scanStats(c.stats);
|
||||||
c.completesNormally = alive;
|
c.completesNormally = alive != Liveness.DEAD;
|
||||||
if (alive && c.caseKind == JCCase.RULE) {
|
if (alive != Liveness.DEAD && c.caseKind == JCCase.RULE) {
|
||||||
scanSyntheticBreak(make, tree);
|
scanSyntheticBreak(make, tree);
|
||||||
alive = false;
|
alive = Liveness.DEAD;
|
||||||
}
|
}
|
||||||
// Warn about fall-through if lint switch fallthrough enabled.
|
// Warn about fall-through if lint switch fallthrough enabled.
|
||||||
if (alive &&
|
if (alive == Liveness.ALIVE &&
|
||||||
lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
|
lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
|
||||||
c.stats.nonEmpty() && l.tail.nonEmpty())
|
c.stats.nonEmpty() && l.tail.nonEmpty())
|
||||||
log.warning(Lint.LintCategory.FALLTHROUGH,
|
log.warning(Lint.LintCategory.FALLTHROUGH,
|
||||||
|
@ -626,9 +626,9 @@ public class Flow {
|
||||||
Warnings.PossibleFallThroughIntoCase);
|
Warnings.PossibleFallThroughIntoCase);
|
||||||
}
|
}
|
||||||
if (!hasDefault) {
|
if (!hasDefault) {
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
}
|
}
|
||||||
alive |= resolveBreaks(tree, prevPendingExits);
|
alive = alive.or(resolveBreaks(tree, prevPendingExits));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -644,9 +644,9 @@ public class Flow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean hasDefault = false;
|
boolean hasDefault = false;
|
||||||
boolean prevAlive = alive;
|
Liveness prevAlive = alive;
|
||||||
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
|
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
JCCase c = l.head;
|
JCCase c = l.head;
|
||||||
if (c.pats.isEmpty())
|
if (c.pats.isEmpty())
|
||||||
hasDefault = true;
|
hasDefault = true;
|
||||||
|
@ -662,13 +662,22 @@ public class Flow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scanStats(c.stats);
|
scanStats(c.stats);
|
||||||
c.completesNormally = alive;
|
if (alive == Liveness.ALIVE) {
|
||||||
|
if (c.caseKind == JCCase.RULE) {
|
||||||
|
log.error(TreeInfo.diagEndPos(c.body),
|
||||||
|
Errors.RuleCompletesNormally);
|
||||||
|
} else if (l.tail.isEmpty()) {
|
||||||
|
log.error(TreeInfo.diagEndPos(tree),
|
||||||
|
Errors.SwitchExpressionCompletesNormally);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.completesNormally = alive != Liveness.DEAD;
|
||||||
}
|
}
|
||||||
if ((constants == null || !constants.isEmpty()) && !hasDefault) {
|
if ((constants == null || !constants.isEmpty()) && !hasDefault) {
|
||||||
log.error(tree, Errors.NotExhaustive);
|
log.error(tree, Errors.NotExhaustive);
|
||||||
}
|
}
|
||||||
alive = prevAlive;
|
alive = prevAlive;
|
||||||
alive |= resolveBreaks(tree, prevPendingExits);
|
alive = alive.or(resolveBreaks(tree, prevPendingExits));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitTry(JCTry tree) {
|
public void visitTry(JCTry tree) {
|
||||||
|
@ -686,22 +695,22 @@ public class Flow {
|
||||||
}
|
}
|
||||||
|
|
||||||
scanStat(tree.body);
|
scanStat(tree.body);
|
||||||
boolean aliveEnd = alive;
|
Liveness aliveEnd = alive;
|
||||||
|
|
||||||
for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
|
for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
JCVariableDecl param = l.head.param;
|
JCVariableDecl param = l.head.param;
|
||||||
scan(param);
|
scan(param);
|
||||||
scanStat(l.head.body);
|
scanStat(l.head.body);
|
||||||
aliveEnd |= alive;
|
aliveEnd = aliveEnd.or(alive);
|
||||||
}
|
}
|
||||||
if (tree.finalizer != null) {
|
if (tree.finalizer != null) {
|
||||||
ListBuffer<PendingExit> exits = pendingExits;
|
ListBuffer<PendingExit> exits = pendingExits;
|
||||||
pendingExits = prevPendingExits;
|
pendingExits = prevPendingExits;
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
scanStat(tree.finalizer);
|
scanStat(tree.finalizer);
|
||||||
tree.finallyCanCompleteNormally = alive;
|
tree.finallyCanCompleteNormally = alive != Liveness.DEAD;
|
||||||
if (!alive) {
|
if (alive == Liveness.DEAD) {
|
||||||
if (lint.isEnabled(Lint.LintCategory.FINALLY)) {
|
if (lint.isEnabled(Lint.LintCategory.FINALLY)) {
|
||||||
log.warning(Lint.LintCategory.FINALLY,
|
log.warning(Lint.LintCategory.FINALLY,
|
||||||
TreeInfo.diagEndPos(tree.finalizer),
|
TreeInfo.diagEndPos(tree.finalizer),
|
||||||
|
@ -726,12 +735,12 @@ public class Flow {
|
||||||
scan(tree.cond);
|
scan(tree.cond);
|
||||||
scanStat(tree.thenpart);
|
scanStat(tree.thenpart);
|
||||||
if (tree.elsepart != null) {
|
if (tree.elsepart != null) {
|
||||||
boolean aliveAfterThen = alive;
|
Liveness aliveAfterThen = alive;
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
scanStat(tree.elsepart);
|
scanStat(tree.elsepart);
|
||||||
alive = alive | aliveAfterThen;
|
alive = alive.or(aliveAfterThen);
|
||||||
} else {
|
} else {
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,12 +785,12 @@ public class Flow {
|
||||||
}
|
}
|
||||||
|
|
||||||
ListBuffer<PendingExit> prevPending = pendingExits;
|
ListBuffer<PendingExit> prevPending = pendingExits;
|
||||||
boolean prevAlive = alive;
|
Liveness prevAlive = alive;
|
||||||
try {
|
try {
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
scanStat(tree.body);
|
scanStat(tree.body);
|
||||||
tree.canCompleteNormally = alive;
|
tree.canCompleteNormally = alive != Liveness.DEAD;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
pendingExits = prevPending;
|
pendingExits = prevPending;
|
||||||
|
@ -807,7 +816,7 @@ public class Flow {
|
||||||
attrEnv = env;
|
attrEnv = env;
|
||||||
Flow.this.make = make;
|
Flow.this.make = make;
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
alive = true;
|
alive = Liveness.ALIVE;
|
||||||
scan(tree);
|
scan(tree);
|
||||||
} finally {
|
} finally {
|
||||||
pendingExits = null;
|
pendingExits = null;
|
||||||
|
@ -2776,4 +2785,58 @@ public class Flow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Liveness {
|
||||||
|
ALIVE {
|
||||||
|
@Override
|
||||||
|
public Liveness or(Liveness other) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Liveness and(Liveness other) {
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DEAD {
|
||||||
|
@Override
|
||||||
|
public Liveness or(Liveness other) {
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Liveness and(Liveness other) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RECOVERY {
|
||||||
|
@Override
|
||||||
|
public Liveness or(Liveness other) {
|
||||||
|
if (other == ALIVE) {
|
||||||
|
return ALIVE;
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Liveness and(Liveness other) {
|
||||||
|
if (other == DEAD) {
|
||||||
|
return DEAD;
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public abstract Liveness or(Liveness other);
|
||||||
|
public abstract Liveness and(Liveness other);
|
||||||
|
public Liveness or(boolean value) {
|
||||||
|
return or(from(value));
|
||||||
|
}
|
||||||
|
public Liveness and(boolean value) {
|
||||||
|
return and(from(value));
|
||||||
|
}
|
||||||
|
public static Liveness from(boolean value) {
|
||||||
|
return value ? ALIVE : DEAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1392,6 +1392,7 @@ public class JavacParser implements Parser {
|
||||||
case RBRACE: case EOF:
|
case RBRACE: case EOF:
|
||||||
JCSwitchExpression e = to(F.at(switchPos).SwitchExpression(selector,
|
JCSwitchExpression e = to(F.at(switchPos).SwitchExpression(selector,
|
||||||
cases.toList()));
|
cases.toList()));
|
||||||
|
e.endpos = token.pos;
|
||||||
accept(RBRACE);
|
accept(RBRACE);
|
||||||
return e;
|
return e;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -200,6 +200,14 @@ compiler.err.continue.outside.switch.expression=\
|
||||||
compiler.err.return.outside.switch.expression=\
|
compiler.err.return.outside.switch.expression=\
|
||||||
return outside of enclosing switch expression
|
return outside of enclosing switch expression
|
||||||
|
|
||||||
|
compiler.err.rule.completes.normally=\
|
||||||
|
switch rule completes without providing a value\n\
|
||||||
|
(switch rules in switch expressions must either provide a value or throw)
|
||||||
|
|
||||||
|
compiler.err.switch.expression.completes.normally=\
|
||||||
|
switch expression completes without providing a value\n\
|
||||||
|
(switch expressions must either provide a value or throw for all possible input values)
|
||||||
|
|
||||||
# 0: name
|
# 0: name
|
||||||
compiler.err.break.ambiguous.target=\
|
compiler.err.break.ambiguous.target=\
|
||||||
ambiguous reference to ''{0}''\n\
|
ambiguous reference to ''{0}''\n\
|
||||||
|
|
|
@ -1305,6 +1305,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||||
public static class JCSwitchExpression extends JCPolyExpression implements SwitchExpressionTree {
|
public static class JCSwitchExpression extends JCPolyExpression implements SwitchExpressionTree {
|
||||||
public JCExpression selector;
|
public JCExpression selector;
|
||||||
public List<JCCase> cases;
|
public List<JCCase> cases;
|
||||||
|
/** Position of closing brace, optional. */
|
||||||
|
public int endpos = Position.NOPOS;
|
||||||
protected JCSwitchExpression(JCExpression selector, List<JCCase> cases) {
|
protected JCSwitchExpression(JCExpression selector, List<JCCase> cases) {
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.cases = cases;
|
this.cases = cases;
|
||||||
|
|
|
@ -383,6 +383,9 @@ public class TreeInfo {
|
||||||
JCTry t = (JCTry) tree;
|
JCTry t = (JCTry) tree;
|
||||||
return endPos((t.finalizer != null) ? t.finalizer
|
return endPos((t.finalizer != null) ? t.finalizer
|
||||||
: (t.catchers.nonEmpty() ? t.catchers.last().body : t.body));
|
: (t.catchers.nonEmpty() ? t.catchers.last().body : t.body));
|
||||||
|
} else if (tree.hasTag(SWITCH_EXPRESSION) &&
|
||||||
|
((JCSwitchExpression) tree).endpos != Position.NOPOS) {
|
||||||
|
return ((JCSwitchExpression) tree).endpos;
|
||||||
} else
|
} else
|
||||||
return tree.pos;
|
return tree.pos;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// key: compiler.err.rule.completes.normally
|
||||||
|
// key: compiler.note.preview.filename
|
||||||
|
// key: compiler.note.preview.recompile
|
||||||
|
// options: --enable-preview -source 12
|
||||||
|
|
||||||
|
class RuleCompletesNormally {
|
||||||
|
public String convert(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
default -> {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// key: compiler.err.switch.expression.completes.normally
|
||||||
|
// key: compiler.note.preview.filename
|
||||||
|
// key: compiler.note.preview.recompile
|
||||||
|
// options: --enable-preview -source 12
|
||||||
|
|
||||||
|
class SwitchExpressionCompletesNormally {
|
||||||
|
public String convert(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
default:
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,7 +46,7 @@ public class ExpSwitchNestingTest extends JavacTemplateTestBase {
|
||||||
private static final String ESWITCH_S = "String res_string = switch (x) { case 0 -> { # } default -> \"default\"; };";
|
private static final String ESWITCH_S = "String res_string = switch (x) { case 0 -> { # } default -> \"default\"; };";
|
||||||
private static final String INT_FN_ESWITCH = "java.util.function.IntSupplier r = switch (x) { case 0 -> { # } default -> null; };";
|
private static final String INT_FN_ESWITCH = "java.util.function.IntSupplier r = switch (x) { case 0 -> { # } default -> null; };";
|
||||||
private static final String INT_ESWITCH_DEFAULT = "int res = switch (x) { default -> { # } };";
|
private static final String INT_ESWITCH_DEFAULT = "int res = switch (x) { default -> { # } };";
|
||||||
private static final String IF = "if (cond) { # }";
|
private static final String IF = "if (cond) { # } else throw new RuntimeException();";
|
||||||
private static final String BLOCK = "{ # }";
|
private static final String BLOCK = "{ # }";
|
||||||
private static final String BREAK_Z = "break 0;";
|
private static final String BREAK_Z = "break 0;";
|
||||||
private static final String BREAK_S = "break \"hello world\";";
|
private static final String BREAK_S = "break \"hello world\";";
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* @test /nodynamiccopyright/
|
||||||
|
* @bug 8212982
|
||||||
|
* @summary Verify a compile-time error is produced if switch expression does not provide a value
|
||||||
|
* @compile/fail/ref=ExpressionSwitchFlow.out --enable-preview -source 12 -XDrawDiagnostics ExpressionSwitchFlow.java
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ExpressionSwitchFlow {
|
||||||
|
private String test1(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0 -> {}
|
||||||
|
default -> { break "other"; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private String test2(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0 -> {
|
||||||
|
}
|
||||||
|
default -> "other";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private String test3(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0 -> {}
|
||||||
|
default -> throw new IllegalStateException();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private String test4(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0 -> { break "other"; }
|
||||||
|
default -> {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private String test5(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0 -> "other";
|
||||||
|
default -> {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private String test6(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0 -> throw new IllegalStateException();
|
||||||
|
default -> {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private String test7(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0: throw new IllegalStateException();
|
||||||
|
default:
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private String test8(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0: i++;
|
||||||
|
default: {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private String test9(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
System.err.println();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
ExpressionSwitchFlow.java:11:24: compiler.err.rule.completes.normally
|
||||||
|
ExpressionSwitchFlow.java:18:13: compiler.err.rule.completes.normally
|
||||||
|
ExpressionSwitchFlow.java:24:24: compiler.err.rule.completes.normally
|
||||||
|
ExpressionSwitchFlow.java:31:25: compiler.err.rule.completes.normally
|
||||||
|
ExpressionSwitchFlow.java:37:25: compiler.err.rule.completes.normally
|
||||||
|
ExpressionSwitchFlow.java:43:25: compiler.err.rule.completes.normally
|
||||||
|
ExpressionSwitchFlow.java:50:9: compiler.err.switch.expression.completes.normally
|
||||||
|
ExpressionSwitchFlow.java:57:9: compiler.err.switch.expression.completes.normally
|
||||||
|
ExpressionSwitchFlow.java:64:9: compiler.err.switch.expression.completes.normally
|
||||||
|
- compiler.note.preview.filename: ExpressionSwitchFlow.java
|
||||||
|
- compiler.note.preview.recompile
|
||||||
|
9 errors
|
Loading…
Add table
Add a link
Reference in a new issue