Skip to content

Commit 4ee400a

Browse files
committed
8268320: Better error recovery for broken patterns in switch
Reviewed-by: vromero
1 parent ca283c3 commit 4ee400a

7 files changed

Lines changed: 161 additions & 52 deletions

File tree

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ protected Attr(Context context) {
169169
allowStaticInterfaceMethods = Feature.STATIC_INTERFACE_METHODS.allowedInSource(source);
170170
allowReifiableTypesInInstanceof = Feature.REIFIABLE_TYPES_INSTANCEOF.allowedInSource(source);
171171
allowRecords = Feature.RECORDS.allowedInSource(source);
172+
allowPatternSwitch = (preview.isEnabled() || !preview.isPreview(Feature.PATTERN_SWITCH)) &&
173+
Feature.PATTERN_SWITCH.allowedInSource(source);
172174
sourceName = source.name;
173175
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
174176

@@ -209,6 +211,10 @@ protected Attr(Context context) {
209211
*/
210212
private final boolean allowRecords;
211213

214+
/** Are patterns in switch allowed
215+
*/
216+
private final boolean allowPatternSwitch;
217+
212218
/**
213219
* Switch: warn about use of variable before declaration?
214220
* RFE: 6425594
@@ -1724,14 +1730,22 @@ private void handleSwitch(JCTree switchTree,
17241730
rs.basicLogResolveHelper = prevResolveHelper;
17251731
}
17261732
} else {
1727-
Type pattype = attribExpr(expr, switchEnv, seltype);
1733+
ResultInfo valTypInfo = new ResultInfo(KindSelector.VAL_TYP,
1734+
!seltype.hasTag(ERROR) ? seltype
1735+
: Type.noType);
1736+
Type pattype = attribTree(expr, switchEnv, valTypInfo);
17281737
if (!pattype.hasTag(ERROR)) {
1729-
if (!stringSwitch && !types.isAssignable(seltype, syms.intType)) {
1730-
log.error(pat.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype));
1731-
}
17321738
if (pattype.constValue() == null) {
1733-
log.error(expr.pos(),
1734-
(stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq));
1739+
Symbol s = TreeInfo.symbol(expr);
1740+
if (s != null && s.kind == TYP && allowPatternSwitch) {
1741+
log.error(expr.pos(),
1742+
Errors.PatternExpected);
1743+
} else {
1744+
log.error(expr.pos(),
1745+
(stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq));
1746+
}
1747+
} else if (!stringSwitch && !types.isAssignable(seltype, syms.intType)) {
1748+
log.error(pat.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype));
17351749
} else if (!labels.add(pattype.constValue())) {
17361750
log.error(c.pos(), Errors.DuplicateCaseLabel);
17371751
} else {

src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,9 @@ compiler.err.static.imp.only.classes.and.interfaces=\
11891189
compiler.err.string.const.req=\
11901190
constant string expression required
11911191

1192+
compiler.err.pattern.expected=\
1193+
type pattern expected
1194+
11921195
# 0: symbol, 1: fragment
11931196
compiler.err.cannot.generate.class=\
11941197
error while generating class {0}\n\
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
// key: compiler.err.pattern.expected
25+
// key: compiler.note.preview.filename
26+
// key: compiler.note.preview.recompile
27+
// options: --enable-preview -source ${jdk.version}
28+
29+
class PatternSwitch {
30+
private void doSwitch(Object o) {
31+
switch (o) {
32+
case String: break;
33+
default: break;
34+
}
35+
}
36+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
SwitchErrors.java:35:31: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)
2+
SwitchErrors.java:34:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object
3+
SwitchErrors.java:40:18: compiler.err.constant.label.not.compatible: int, java.lang.Object
4+
SwitchErrors.java:46:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
5+
SwitchErrors.java:47:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.CharSequence)
6+
SwitchErrors.java:52:18: compiler.err.preview.feature.disabled: (compiler.misc.feature.case.null)
7+
SwitchErrors.java:53:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
8+
SwitchErrors.java:54:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.CharSequence)
9+
SwitchErrors.java:60:20: compiler.err.total.pattern.and.default
10+
SwitchErrors.java:66:13: compiler.err.pattern.dominated
11+
SwitchErrors.java:72:18: compiler.err.total.pattern.and.default
12+
SwitchErrors.java:78:18: compiler.err.duplicate.total.pattern
13+
SwitchErrors.java:84:20: compiler.err.duplicate.default.label
14+
SwitchErrors.java:90:20: compiler.err.duplicate.default.label
15+
SwitchErrors.java:101:13: compiler.err.duplicate.case.label
16+
SwitchErrors.java:106:13: compiler.err.duplicate.case.label
17+
SwitchErrors.java:111:28: compiler.err.flows.through.to.pattern
18+
SwitchErrors.java:117:18: compiler.err.flows.through.to.pattern
19+
SwitchErrors.java:124:18: compiler.err.flows.through.to.pattern
20+
SwitchErrors.java:131:18: compiler.err.flows.through.to.pattern
21+
SwitchErrors.java:136:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
22+
SwitchErrors.java:142:18: compiler.err.instanceof.reifiable.not.safe: java.util.List, java.util.List<java.lang.Integer>
23+
SwitchErrors.java:148:18: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
24+
SwitchErrors.java:155:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
25+
SwitchErrors.java:172:27: compiler.err.flows.through.to.pattern
26+
SwitchErrors.java:178:18: compiler.err.flows.through.to.pattern
27+
SwitchErrors.java:184:13: compiler.err.pattern.dominated
28+
SwitchErrors.java:196:18: compiler.err.const.expr.req
29+
SwitchErrors.java:202:76: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
30+
SwitchErrors.java:208:71: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
31+
SwitchErrors.java:33:9: compiler.err.not.exhaustive.statement
32+
SwitchErrors.java:39:9: compiler.err.not.exhaustive.statement
33+
SwitchErrors.java:45:9: compiler.err.not.exhaustive.statement
34+
SwitchErrors.java:51:9: compiler.err.not.exhaustive.statement
35+
SwitchErrors.java:99:9: compiler.err.not.exhaustive.statement
36+
SwitchErrors.java:105:9: compiler.err.not.exhaustive.statement
37+
SwitchErrors.java:110:9: compiler.err.not.exhaustive.statement
38+
SwitchErrors.java:115:9: compiler.err.not.exhaustive.statement
39+
SwitchErrors.java:121:9: compiler.err.not.exhaustive.statement
40+
SwitchErrors.java:128:9: compiler.err.not.exhaustive.statement
41+
SwitchErrors.java:188:9: compiler.err.not.exhaustive.statement
42+
41 errors

test/langtools/tools/javac/patterns/SwitchErrors.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* @bug 8262891
2727
* @summary Verify errors related to pattern switches.
2828
* @compile/fail/ref=SwitchErrors.out --enable-preview -source ${jdk.version} -XDrawDiagnostics -XDshould-stop.at=FLOW SwitchErrors.java
29+
* @compile/fail/ref=SwitchErrors-no-preview.out -XDrawDiagnostics -XDshould-stop.at=FLOW SwitchErrors.java
2930
*/
3031
public class SwitchErrors {
3132
void incompatibleSelectorObjectString(Object o) {
@@ -190,6 +191,12 @@ void sealedNonAbstract(SealedNonAbstract obj) {
190191
}
191192
sealed class SealedNonAbstract permits A {}
192193
final class A extends SealedNonAbstract {}
194+
void errorRecoveryNoPattern1(Object o) {
195+
switch (o) {
196+
case String: break;
197+
case Object obj: break;
198+
}
199+
}
193200
Object guardWithMatchingStatement(Object o1, Object o2) {
194201
switch (o1) {
195202
case String s && s.isEmpty() || o2 instanceof Number n: return n;
Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,47 @@
1-
SwitchErrors.java:33:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object
2-
SwitchErrors.java:39:18: compiler.err.constant.label.not.compatible: int, java.lang.Object
3-
SwitchErrors.java:45:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
4-
SwitchErrors.java:46:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.CharSequence)
5-
SwitchErrors.java:51:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: compiler.misc.type.null, int)
6-
SwitchErrors.java:52:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
7-
SwitchErrors.java:53:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.CharSequence)
8-
SwitchErrors.java:59:20: compiler.err.total.pattern.and.default
9-
SwitchErrors.java:65:13: compiler.err.pattern.dominated
10-
SwitchErrors.java:65:24: compiler.err.total.pattern.and.default
11-
SwitchErrors.java:71:18: compiler.err.total.pattern.and.default
12-
SwitchErrors.java:77:18: compiler.err.duplicate.total.pattern
13-
SwitchErrors.java:83:20: compiler.err.duplicate.default.label
14-
SwitchErrors.java:89:20: compiler.err.duplicate.default.label
15-
SwitchErrors.java:94:27: compiler.err.duplicate.default.label
16-
SwitchErrors.java:100:13: compiler.err.duplicate.case.label
17-
SwitchErrors.java:105:13: compiler.err.duplicate.case.label
18-
SwitchErrors.java:110:28: compiler.err.flows.through.to.pattern
19-
SwitchErrors.java:116:18: compiler.err.flows.through.to.pattern
20-
SwitchErrors.java:123:18: compiler.err.flows.through.to.pattern
21-
SwitchErrors.java:130:18: compiler.err.flows.through.to.pattern
22-
SwitchErrors.java:135:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
23-
SwitchErrors.java:141:18: compiler.err.instanceof.reifiable.not.safe: java.util.List, java.util.List<java.lang.Integer>
24-
SwitchErrors.java:147:18: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
25-
SwitchErrors.java:154:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
26-
SwitchErrors.java:160:28: compiler.err.flows.through.from.pattern
27-
SwitchErrors.java:166:18: compiler.err.flows.through.from.pattern
28-
SwitchErrors.java:171:27: compiler.err.flows.through.to.pattern
29-
SwitchErrors.java:177:18: compiler.err.flows.through.to.pattern
30-
SwitchErrors.java:183:13: compiler.err.pattern.dominated
31-
SwitchErrors.java:195:76: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
32-
SwitchErrors.java:201:71: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
33-
SwitchErrors.java:32:9: compiler.err.not.exhaustive.statement
34-
SwitchErrors.java:38:9: compiler.err.not.exhaustive.statement
35-
SwitchErrors.java:44:9: compiler.err.not.exhaustive.statement
36-
SwitchErrors.java:50:9: compiler.err.not.exhaustive.statement
37-
SwitchErrors.java:98:9: compiler.err.not.exhaustive.statement
38-
SwitchErrors.java:104:9: compiler.err.not.exhaustive.statement
39-
SwitchErrors.java:109:9: compiler.err.not.exhaustive.statement
40-
SwitchErrors.java:114:9: compiler.err.not.exhaustive.statement
41-
SwitchErrors.java:120:9: compiler.err.not.exhaustive.statement
42-
SwitchErrors.java:127:9: compiler.err.not.exhaustive.statement
43-
SwitchErrors.java:187:9: compiler.err.not.exhaustive.statement
1+
SwitchErrors.java:34:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object
2+
SwitchErrors.java:40:18: compiler.err.constant.label.not.compatible: int, java.lang.Object
3+
SwitchErrors.java:46:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
4+
SwitchErrors.java:47:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.CharSequence)
5+
SwitchErrors.java:52:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: compiler.misc.type.null, int)
6+
SwitchErrors.java:53:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
7+
SwitchErrors.java:54:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.CharSequence)
8+
SwitchErrors.java:60:20: compiler.err.total.pattern.and.default
9+
SwitchErrors.java:66:13: compiler.err.pattern.dominated
10+
SwitchErrors.java:66:24: compiler.err.total.pattern.and.default
11+
SwitchErrors.java:72:18: compiler.err.total.pattern.and.default
12+
SwitchErrors.java:78:18: compiler.err.duplicate.total.pattern
13+
SwitchErrors.java:84:20: compiler.err.duplicate.default.label
14+
SwitchErrors.java:90:20: compiler.err.duplicate.default.label
15+
SwitchErrors.java:95:27: compiler.err.duplicate.default.label
16+
SwitchErrors.java:101:13: compiler.err.duplicate.case.label
17+
SwitchErrors.java:106:13: compiler.err.duplicate.case.label
18+
SwitchErrors.java:111:28: compiler.err.flows.through.to.pattern
19+
SwitchErrors.java:117:18: compiler.err.flows.through.to.pattern
20+
SwitchErrors.java:124:18: compiler.err.flows.through.to.pattern
21+
SwitchErrors.java:131:18: compiler.err.flows.through.to.pattern
22+
SwitchErrors.java:136:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
23+
SwitchErrors.java:142:18: compiler.err.instanceof.reifiable.not.safe: java.util.List, java.util.List<java.lang.Integer>
24+
SwitchErrors.java:148:18: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
25+
SwitchErrors.java:155:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
26+
SwitchErrors.java:161:28: compiler.err.flows.through.from.pattern
27+
SwitchErrors.java:167:18: compiler.err.flows.through.from.pattern
28+
SwitchErrors.java:172:27: compiler.err.flows.through.to.pattern
29+
SwitchErrors.java:178:18: compiler.err.flows.through.to.pattern
30+
SwitchErrors.java:184:13: compiler.err.pattern.dominated
31+
SwitchErrors.java:196:18: compiler.err.pattern.expected
32+
SwitchErrors.java:202:76: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
33+
SwitchErrors.java:208:71: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
34+
SwitchErrors.java:33:9: compiler.err.not.exhaustive.statement
35+
SwitchErrors.java:39:9: compiler.err.not.exhaustive.statement
36+
SwitchErrors.java:45:9: compiler.err.not.exhaustive.statement
37+
SwitchErrors.java:51:9: compiler.err.not.exhaustive.statement
38+
SwitchErrors.java:99:9: compiler.err.not.exhaustive.statement
39+
SwitchErrors.java:105:9: compiler.err.not.exhaustive.statement
40+
SwitchErrors.java:110:9: compiler.err.not.exhaustive.statement
41+
SwitchErrors.java:115:9: compiler.err.not.exhaustive.statement
42+
SwitchErrors.java:121:9: compiler.err.not.exhaustive.statement
43+
SwitchErrors.java:128:9: compiler.err.not.exhaustive.statement
44+
SwitchErrors.java:188:9: compiler.err.not.exhaustive.statement
4445
- compiler.note.preview.filename: SwitchErrors.java, DEFAULT
4546
- compiler.note.preview.recompile
46-
43 errors
47+
44 errors

test/langtools/tools/javac/patterns/Switches.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ String testEnumWithGuardsExpression2(E e) {
278278
String testStringWithGuards1(E e) {
279279
switch (e != null ? e.name() : null) {
280280
case "A": return "a";
281-
case "B": return "b";
281+
case Switches.ConstantClassClash: return "b";
282282
case String x && "C".equals(x): return "C";
283283
case "C": return "broken";
284284
case null, String x: return String.valueOf(x);
@@ -288,7 +288,7 @@ String testStringWithGuards1(E e) {
288288
String testStringWithGuardsExpression1(E e) {
289289
return switch (e != null ? e.name() : null) {
290290
case "A" -> "a";
291-
case "B" -> "b";
291+
case ConstantClassClash -> "b";
292292
case String x && "C".equals(x) -> "C";
293293
case "C" -> "broken";
294294
case null, String x -> String.valueOf(x);
@@ -366,6 +366,12 @@ void exhaustiveStatementSane(Object o) {
366366
}
367367
}
368368

369+
//verify that for cases like:
370+
//case ConstantClassClash ->
371+
//ConstantClassClash is interpreted as a field, not as a class
372+
private static final String ConstantClassClash = "B";
373+
private static class ConstantClassClash {}
374+
369375
sealed interface I {}
370376
final class A implements I {}
371377
final class B implements I {}

0 commit comments

Comments
 (0)