8268320: Better error recovery for broken patterns in switch

Reviewed-by: vromero
This commit is contained in:
Jan Lahoda 2021-06-30 09:47:22 +00:00
parent ca283c3ac0
commit 4ee400ae43
7 changed files with 161 additions and 52 deletions
src/jdk.compiler/share/classes/com/sun/tools/javac
test/langtools/tools/javac

@ -169,6 +169,8 @@ public class Attr extends JCTree.Visitor {
allowStaticInterfaceMethods = Feature.STATIC_INTERFACE_METHODS.allowedInSource(source);
allowReifiableTypesInInstanceof = Feature.REIFIABLE_TYPES_INSTANCEOF.allowedInSource(source);
allowRecords = Feature.RECORDS.allowedInSource(source);
allowPatternSwitch = (preview.isEnabled() || !preview.isPreview(Feature.PATTERN_SWITCH)) &&
Feature.PATTERN_SWITCH.allowedInSource(source);
sourceName = source.name;
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
@ -209,6 +211,10 @@ public class Attr extends JCTree.Visitor {
*/
private final boolean allowRecords;
/** Are patterns in switch allowed
*/
private final boolean allowPatternSwitch;
/**
* Switch: warn about use of variable before declaration?
* RFE: 6425594
@ -1724,14 +1730,22 @@ public class Attr extends JCTree.Visitor {
rs.basicLogResolveHelper = prevResolveHelper;
}
} else {
Type pattype = attribExpr(expr, switchEnv, seltype);
ResultInfo valTypInfo = new ResultInfo(KindSelector.VAL_TYP,
!seltype.hasTag(ERROR) ? seltype
: Type.noType);
Type pattype = attribTree(expr, switchEnv, valTypInfo);
if (!pattype.hasTag(ERROR)) {
if (!stringSwitch && !types.isAssignable(seltype, syms.intType)) {
log.error(pat.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype));
}
if (pattype.constValue() == null) {
log.error(expr.pos(),
(stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq));
Symbol s = TreeInfo.symbol(expr);
if (s != null && s.kind == TYP && allowPatternSwitch) {
log.error(expr.pos(),
Errors.PatternExpected);
} else {
log.error(expr.pos(),
(stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq));
}
} else if (!stringSwitch && !types.isAssignable(seltype, syms.intType)) {
log.error(pat.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype));
} else if (!labels.add(pattype.constValue())) {
log.error(c.pos(), Errors.DuplicateCaseLabel);
} else {

@ -1189,6 +1189,9 @@ compiler.err.static.imp.only.classes.and.interfaces=\
compiler.err.string.const.req=\
constant string expression required
compiler.err.pattern.expected=\
type pattern expected
# 0: symbol, 1: fragment
compiler.err.cannot.generate.class=\
error while generating class {0}\n\

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, 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.pattern.expected
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source ${jdk.version}
class PatternSwitch {
private void doSwitch(Object o) {
switch (o) {
case String: break;
default: break;
}
}
}

@ -0,0 +1,42 @@
SwitchErrors.java:35:31: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)
SwitchErrors.java:34:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object
SwitchErrors.java:40:18: compiler.err.constant.label.not.compatible: int, java.lang.Object
SwitchErrors.java:46:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
SwitchErrors.java:47:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.CharSequence)
SwitchErrors.java:52:18: compiler.err.preview.feature.disabled: (compiler.misc.feature.case.null)
SwitchErrors.java:53:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
SwitchErrors.java:54:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.CharSequence)
SwitchErrors.java:60:20: compiler.err.total.pattern.and.default
SwitchErrors.java:66:13: compiler.err.pattern.dominated
SwitchErrors.java:72:18: compiler.err.total.pattern.and.default
SwitchErrors.java:78:18: compiler.err.duplicate.total.pattern
SwitchErrors.java:84:20: compiler.err.duplicate.default.label
SwitchErrors.java:90:20: compiler.err.duplicate.default.label
SwitchErrors.java:101:13: compiler.err.duplicate.case.label
SwitchErrors.java:106:13: compiler.err.duplicate.case.label
SwitchErrors.java:111:28: compiler.err.flows.through.to.pattern
SwitchErrors.java:117:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:124:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:131:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:136:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
SwitchErrors.java:142:18: compiler.err.instanceof.reifiable.not.safe: java.util.List, java.util.List<java.lang.Integer>
SwitchErrors.java:148:18: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:155:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
SwitchErrors.java:172:27: compiler.err.flows.through.to.pattern
SwitchErrors.java:178:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:184:13: compiler.err.pattern.dominated
SwitchErrors.java:196:18: compiler.err.const.expr.req
SwitchErrors.java:202:76: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:208:71: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:33:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:39:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:45:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:51:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:99:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:105:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:110:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:115:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:121:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:128:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:188:9: compiler.err.not.exhaustive.statement
41 errors

@ -26,6 +26,7 @@
* @bug 8262891
* @summary Verify errors related to pattern switches.
* @compile/fail/ref=SwitchErrors.out --enable-preview -source ${jdk.version} -XDrawDiagnostics -XDshould-stop.at=FLOW SwitchErrors.java
* @compile/fail/ref=SwitchErrors-no-preview.out -XDrawDiagnostics -XDshould-stop.at=FLOW SwitchErrors.java
*/
public class SwitchErrors {
void incompatibleSelectorObjectString(Object o) {
@ -190,6 +191,12 @@ public class SwitchErrors {
}
sealed class SealedNonAbstract permits A {}
final class A extends SealedNonAbstract {}
void errorRecoveryNoPattern1(Object o) {
switch (o) {
case String: break;
case Object obj: break;
}
}
Object guardWithMatchingStatement(Object o1, Object o2) {
switch (o1) {
case String s && s.isEmpty() || o2 instanceof Number n: return n;

@ -1,46 +1,47 @@
SwitchErrors.java:33:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object
SwitchErrors.java:39:18: compiler.err.constant.label.not.compatible: int, java.lang.Object
SwitchErrors.java:45:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
SwitchErrors.java:46:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.CharSequence)
SwitchErrors.java:51:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: compiler.misc.type.null, int)
SwitchErrors.java:52:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
SwitchErrors.java:53:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.CharSequence)
SwitchErrors.java:59:20: compiler.err.total.pattern.and.default
SwitchErrors.java:65:13: compiler.err.pattern.dominated
SwitchErrors.java:65:24: compiler.err.total.pattern.and.default
SwitchErrors.java:71:18: compiler.err.total.pattern.and.default
SwitchErrors.java:77:18: compiler.err.duplicate.total.pattern
SwitchErrors.java:83:20: compiler.err.duplicate.default.label
SwitchErrors.java:89:20: compiler.err.duplicate.default.label
SwitchErrors.java:94:27: compiler.err.duplicate.default.label
SwitchErrors.java:100:13: compiler.err.duplicate.case.label
SwitchErrors.java:105:13: compiler.err.duplicate.case.label
SwitchErrors.java:110:28: compiler.err.flows.through.to.pattern
SwitchErrors.java:116:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:123:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:130:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:135:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
SwitchErrors.java:141:18: compiler.err.instanceof.reifiable.not.safe: java.util.List, java.util.List<java.lang.Integer>
SwitchErrors.java:147:18: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:154:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
SwitchErrors.java:160:28: compiler.err.flows.through.from.pattern
SwitchErrors.java:166:18: compiler.err.flows.through.from.pattern
SwitchErrors.java:171:27: compiler.err.flows.through.to.pattern
SwitchErrors.java:177:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:183:13: compiler.err.pattern.dominated
SwitchErrors.java:195:76: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:201:71: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:32:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:38:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:44:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:50:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:98:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:104:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:109:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:114:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:120:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:127:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:187:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:34:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object
SwitchErrors.java:40:18: compiler.err.constant.label.not.compatible: int, java.lang.Object
SwitchErrors.java:46:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
SwitchErrors.java:47:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.CharSequence)
SwitchErrors.java:52:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: compiler.misc.type.null, int)
SwitchErrors.java:53:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
SwitchErrors.java:54:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.CharSequence)
SwitchErrors.java:60:20: compiler.err.total.pattern.and.default
SwitchErrors.java:66:13: compiler.err.pattern.dominated
SwitchErrors.java:66:24: compiler.err.total.pattern.and.default
SwitchErrors.java:72:18: compiler.err.total.pattern.and.default
SwitchErrors.java:78:18: compiler.err.duplicate.total.pattern
SwitchErrors.java:84:20: compiler.err.duplicate.default.label
SwitchErrors.java:90:20: compiler.err.duplicate.default.label
SwitchErrors.java:95:27: compiler.err.duplicate.default.label
SwitchErrors.java:101:13: compiler.err.duplicate.case.label
SwitchErrors.java:106:13: compiler.err.duplicate.case.label
SwitchErrors.java:111:28: compiler.err.flows.through.to.pattern
SwitchErrors.java:117:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:124:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:131:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:136:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
SwitchErrors.java:142:18: compiler.err.instanceof.reifiable.not.safe: java.util.List, java.util.List<java.lang.Integer>
SwitchErrors.java:148:18: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:155:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
SwitchErrors.java:161:28: compiler.err.flows.through.from.pattern
SwitchErrors.java:167:18: compiler.err.flows.through.from.pattern
SwitchErrors.java:172:27: compiler.err.flows.through.to.pattern
SwitchErrors.java:178:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:184:13: compiler.err.pattern.dominated
SwitchErrors.java:196:18: compiler.err.pattern.expected
SwitchErrors.java:202:76: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:208:71: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:33:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:39:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:45:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:51:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:99:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:105:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:110:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:115:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:121:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:128:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:188:9: compiler.err.not.exhaustive.statement
- compiler.note.preview.filename: SwitchErrors.java, DEFAULT
- compiler.note.preview.recompile
43 errors
44 errors

@ -278,7 +278,7 @@ public class Switches {
String testStringWithGuards1(E e) {
switch (e != null ? e.name() : null) {
case "A": return "a";
case "B": return "b";
case Switches.ConstantClassClash: return "b";
case String x && "C".equals(x): return "C";
case "C": return "broken";
case null, String x: return String.valueOf(x);
@ -288,7 +288,7 @@ public class Switches {
String testStringWithGuardsExpression1(E e) {
return switch (e != null ? e.name() : null) {
case "A" -> "a";
case "B" -> "b";
case ConstantClassClash -> "b";
case String x && "C".equals(x) -> "C";
case "C" -> "broken";
case null, String x -> String.valueOf(x);
@ -366,6 +366,12 @@ public class Switches {
}
}
//verify that for cases like:
//case ConstantClassClash ->
//ConstantClassClash is interpreted as a field, not as a class
private static final String ConstantClassClash = "B";
private static class ConstantClassClash {}
sealed interface I {}
final class A implements I {}
final class B implements I {}