Skip to content

Commit 820f290

Browse files
committed
8268859: jshell throws exception while parsing illegal "case true"
Reviewed-by: mcimadamore
1 parent 815e4af commit 820f290

11 files changed

Lines changed: 245 additions & 172 deletions

src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java

Lines changed: 91 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -772,8 +772,8 @@ public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType
772772
accept(RPAREN);
773773
pattern = toP(F.at(startPos).ParenthesizedPattern(p));
774774
} else {
775-
JCExpression e = parsedType == null ? term(EXPR | TYPE | NOLAMBDA) : parsedType;
776-
mods = mods != null ? mods : F.at(token.pos).Modifiers(0);
775+
mods = mods != null ? mods : optFinal(0);
776+
JCExpression e = parsedType == null ? term(TYPE | NOLAMBDA) : parsedType;
777777
JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null));
778778
pattern = toP(F.at(pos).BindingPattern(var));
779779
}
@@ -1694,16 +1694,12 @@ boolean isUnboundMemberRef() {
16941694
* method reference or a binary expression. To disambiguate, look for a
16951695
* matching '>' and see if the subsequent terminal is either '.' or '::'.
16961696
*/
1697-
ParensResult analyzeParens() {
1698-
return analyzeParens(0);
1699-
}
1700-
17011697
@SuppressWarnings("fallthrough")
1702-
ParensResult analyzeParens(int startLookahead) {
1698+
ParensResult analyzeParens() {
17031699
int depth = 0;
17041700
boolean type = false;
17051701
ParensResult defaultResult = ParensResult.PARENS;
1706-
outer: for (int lookahead = startLookahead; ; lookahead++) {
1702+
outer: for (int lookahead = 0; ; lookahead++) {
17071703
TokenKind tk = S.token(lookahead).kind;
17081704
switch (tk) {
17091705
case COMMA:
@@ -1729,7 +1725,7 @@ ParensResult analyzeParens(int startLookahead) {
17291725
}
17301726
break;
17311727
case LPAREN:
1732-
if (lookahead != startLookahead) {
1728+
if (lookahead != 0) {
17331729
// '(' in a non-starting position -> parens
17341730
return ParensResult.PARENS;
17351731
} else if (peekToken(lookahead, RPAREN)) {
@@ -1780,31 +1776,7 @@ ParensResult analyzeParens(int startLookahead) {
17801776
return ParensResult.EXPLICIT_LAMBDA;
17811777
case MONKEYS_AT:
17821778
type = true;
1783-
lookahead += 1; //skip '@'
1784-
while (peekToken(lookahead, DOT)) {
1785-
lookahead += 2;
1786-
}
1787-
if (peekToken(lookahead, LPAREN)) {
1788-
lookahead++;
1789-
//skip annotation values
1790-
int nesting = 0;
1791-
for (; ; lookahead++) {
1792-
TokenKind tk2 = S.token(lookahead).kind;
1793-
switch (tk2) {
1794-
case EOF:
1795-
return ParensResult.PARENS;
1796-
case LPAREN:
1797-
nesting++;
1798-
break;
1799-
case RPAREN:
1800-
nesting--;
1801-
if (nesting == 0) {
1802-
continue outer;
1803-
}
1804-
break;
1805-
}
1806-
}
1807-
}
1779+
lookahead = skipAnnotation(lookahead);
18081780
break;
18091781
case LBRACKET:
18101782
if (peekToken(lookahead, RBRACKET, LAX_IDENTIFIER)) {
@@ -1861,6 +1833,35 @@ ParensResult analyzeParens(int startLookahead) {
18611833
}
18621834
}
18631835

1836+
private int skipAnnotation(int lookahead) {
1837+
lookahead += 1; //skip '@'
1838+
while (peekToken(lookahead, DOT)) {
1839+
lookahead += 2;
1840+
}
1841+
if (peekToken(lookahead, LPAREN)) {
1842+
lookahead++;
1843+
//skip annotation values
1844+
int nesting = 0;
1845+
for (; ; lookahead++) {
1846+
TokenKind tk2 = S.token(lookahead).kind;
1847+
switch (tk2) {
1848+
case EOF:
1849+
return lookahead;
1850+
case LPAREN:
1851+
nesting++;
1852+
break;
1853+
case RPAREN:
1854+
nesting--;
1855+
if (nesting == 0) {
1856+
return lookahead;
1857+
}
1858+
break;
1859+
}
1860+
}
1861+
}
1862+
return lookahead;
1863+
}
1864+
18641865
/** Accepts all identifier-like tokens */
18651866
protected Predicate<TokenKind> LAX_IDENTIFIER = t -> t == IDENTIFIER || t == UNDERSCORE || t == ASSERT || t == ENUM;
18661867

@@ -3067,34 +3068,69 @@ private JCCaseLabel parseCaseLabel() {
30673068
nextToken();
30683069
label = toP(F.at(patternPos).DefaultCaseLabel());
30693070
} else {
3070-
if (token.kind == LPAREN) {
3071-
int lookahead = 0;
3072-
while (S.token(lookahead + 1).kind == LPAREN) {
3073-
lookahead++;
3074-
}
3075-
boolean pattern = analyzeParens(lookahead) == ParensResult.EXPLICIT_LAMBDA;
3076-
if (pattern) {
3077-
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
3078-
return parsePattern(token.pos, null, null, false);
3079-
} else {
3080-
return term(EXPR | TYPE | NOLAMBDA);
3081-
}
3071+
int lookahead = 0;
3072+
while (S.token(lookahead).kind == LPAREN) {
3073+
lookahead++;
3074+
}
3075+
JCModifiers mods = optFinal(0);
3076+
boolean pattern = mods.flags != 0 || mods.annotations.nonEmpty() ||
3077+
analyzePattern(lookahead) == PatternResult.PATTERN;
3078+
if (pattern) {
3079+
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
3080+
return parsePattern(patternPos, mods, null, false);
30823081
} else {
3083-
JCModifiers mods = optFinal(0);
3084-
JCExpression e = term(EXPR | TYPE | NOLAMBDA);
3085-
3086-
if (token.kind == IDENTIFIER || mods.flags != 0 || mods.annotations.nonEmpty()) {
3087-
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
3088-
return parsePattern(patternPos, null, e, false);
3089-
} else {
3090-
return e;
3091-
}
3082+
return term(EXPR | NOLAMBDA);
30923083
}
30933084
}
30943085

30953086
return label;
30963087
}
30973088

3089+
@SuppressWarnings("fallthrough")
3090+
PatternResult analyzePattern(int lookahead) {
3091+
int depth = 0;
3092+
while (true) {
3093+
TokenKind token = S.token(lookahead).kind;
3094+
switch (token) {
3095+
case BYTE: case SHORT: case INT: case LONG: case FLOAT:
3096+
case DOUBLE: case BOOLEAN: case CHAR: case VOID:
3097+
case ASSERT, ENUM, IDENTIFIER, UNDERSCORE:
3098+
if (depth == 0 && peekToken(lookahead, LAX_IDENTIFIER)) return PatternResult.PATTERN;
3099+
break;
3100+
case DOT, QUES, EXTENDS, SUPER, COMMA: break;
3101+
case LT: depth++; break;
3102+
case GTGTGT: depth--;
3103+
case GTGT: depth--;
3104+
case GT:
3105+
depth--;
3106+
if (depth == 0) {
3107+
return peekToken(lookahead, LAX_IDENTIFIER) ? PatternResult.PATTERN
3108+
: PatternResult.EXPRESSION;
3109+
} else if (depth < 0) return PatternResult.EXPRESSION;
3110+
break;
3111+
case MONKEYS_AT:
3112+
lookahead = skipAnnotation(lookahead);
3113+
break;
3114+
case LBRACKET:
3115+
if (peekToken(lookahead, RBRACKET, LAX_IDENTIFIER)) {
3116+
return PatternResult.PATTERN;
3117+
} else if (peekToken(lookahead, RBRACKET)) {
3118+
lookahead++;
3119+
break;
3120+
} else {
3121+
return PatternResult.EXPRESSION;
3122+
}
3123+
default: return PatternResult.EXPRESSION;
3124+
}
3125+
lookahead++;
3126+
}
3127+
}
3128+
3129+
private enum PatternResult {
3130+
EXPRESSION,
3131+
PATTERN;
3132+
}
3133+
30983134
/** MoreStatementExpressions = { COMMA StatementExpression }
30993135
*/
31003136
<T extends ListBuffer<? super JCExpressionStatement>> T moreStatementExpressions(int pos,

test/langtools/tools/javac/patterns/DisambiguateParenthesizedPattern.java renamed to test/langtools/tools/javac/patterns/DisambiguatePatterns.java

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,35 @@
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+
124
/**
225
* @test
326
* @modules jdk.compiler/com.sun.tools.javac.file
427
* jdk.compiler/com.sun.tools.javac.main
528
* jdk.compiler/com.sun.tools.javac.parser
629
* jdk.compiler/com.sun.tools.javac.tree
730
* jdk.compiler/com.sun.tools.javac.util
8-
* @compile --enable-preview -source ${jdk.version} DisambiguateParenthesizedPattern.java
9-
* @run main/othervm --enable-preview DisambiguateParenthesizedPattern
31+
* @compile --enable-preview -source ${jdk.version} DisambiguatePatterns.java
32+
* @run main/othervm --enable-preview DisambiguatePatterns
1033
*/
1134

1235
import com.sun.source.tree.CaseLabelTree;
@@ -24,16 +47,18 @@
2447
import com.sun.tools.javac.util.Options;
2548
import java.nio.charset.Charset;
2649

27-
public class DisambiguateParenthesizedPattern {
50+
public class DisambiguatePatterns {
2851

2952
public static void main(String... args) throws Throwable {
30-
DisambiguateParenthesizedPattern test = new DisambiguateParenthesizedPattern();
53+
DisambiguatePatterns test = new DisambiguatePatterns();
3154
test.disambiguationTest("String s",
3255
ExpressionType.PATTERN);
3356
test.disambiguationTest("String s && s.isEmpty()",
3457
ExpressionType.PATTERN);
3558
test.disambiguationTest("(String s)",
3659
ExpressionType.PATTERN);
60+
test.disambiguationTest("(@Ann String s)",
61+
ExpressionType.PATTERN);
3762
test.disambiguationTest("((String s))",
3863
ExpressionType.PATTERN);
3964
test.disambiguationTest("(String) s",
@@ -56,11 +81,41 @@ public static void main(String... args) throws Throwable {
5681
ExpressionType.EXPRESSION);
5782
test.disambiguationTest("(a < c.d > b)",
5883
ExpressionType.PATTERN);
84+
test.disambiguationTest("a<? extends c.d> b",
85+
ExpressionType.PATTERN);
86+
test.disambiguationTest("@Ann a<? extends c.d> b",
87+
ExpressionType.PATTERN);
88+
test.disambiguationTest("a<? extends @Ann c.d> b",
89+
ExpressionType.PATTERN);
90+
test.disambiguationTest("a<? super c.d> b",
91+
ExpressionType.PATTERN);
92+
test.disambiguationTest("a<? super @Ann c.d> b",
93+
ExpressionType.PATTERN);
94+
test.disambiguationTest("a<b<c.d>> b",
95+
ExpressionType.PATTERN);
96+
test.disambiguationTest("a<b<@Ann c.d>> b",
97+
ExpressionType.PATTERN);
98+
test.disambiguationTest("a<b<c<d>>> b",
99+
ExpressionType.PATTERN);
100+
test.disambiguationTest("a[] b",
101+
ExpressionType.PATTERN);
102+
test.disambiguationTest("a[][] b",
103+
ExpressionType.PATTERN);
104+
test.disambiguationTest("int i",
105+
ExpressionType.PATTERN);
106+
test.disambiguationTest("int[] i",
107+
ExpressionType.PATTERN);
108+
test.disambiguationTest("a[a]",
109+
ExpressionType.EXPRESSION);
110+
test.disambiguationTest("a[b][c]",
111+
ExpressionType.EXPRESSION);
112+
test.disambiguationTest("a & b",
113+
ExpressionType.EXPRESSION);
59114
}
60115

61116
private final ParserFactory factory;
62117

63-
public DisambiguateParenthesizedPattern() {
118+
public DisambiguatePatterns() {
64119
Context context = new Context();
65120
JavacFileManager jfm = new JavacFileManager(context, true, Charset.defaultCharset());
66121
Options.instance(context).put(Option.PREVIEW, "");
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* @test /nodynamiccopyright/
3+
* @bug 8268859
4+
* @summary Verify error recovery/disambiguation of case labels that mix expressions and patterns
5+
* @compile/fail/ref=PatternCaseErrorRecovery.out --enable-preview -source ${jdk.version} -XDrawDiagnostics PatternCaseErrorRecovery.java
6+
*/
7+
8+
public class PatternCaseErrorRecovery {
9+
Object expressionLikeType(Object o1, Object o2) {
10+
final int a = 1;
11+
final int b = 2;
12+
return switch (o1) {
13+
case true t -> o2;
14+
case 1 + 1 e -> o2;
15+
case a < b ? a : b e -> o2;
16+
default -> null;
17+
};
18+
}
19+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
PatternCaseErrorRecovery.java:13:22: compiler.err.expected2: :, ->
2+
PatternCaseErrorRecovery.java:13:23: compiler.err.not.stmt
3+
PatternCaseErrorRecovery.java:14:23: compiler.err.expected2: :, ->
4+
PatternCaseErrorRecovery.java:14:24: compiler.err.not.stmt
5+
PatternCaseErrorRecovery.java:15:31: compiler.err.expected2: :, ->
6+
PatternCaseErrorRecovery.java:15:32: compiler.err.not.stmt
7+
6 errors
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
PatternErrorRecovery.java:12:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)
2+
PatternErrorRecovery.java:11:18: compiler.err.const.expr.req
3+
2 errors
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* @test /nodynamiccopyright/
3+
* @bug 8268320
4+
* @summary Verify user-friendly errors are reported for ill-formed pattern.
5+
* @compile/fail/ref=PatternErrorRecovery.out -XDrawDiagnostics -XDshould-stop.at=FLOW --enable-preview -source ${jdk.version} PatternErrorRecovery.java
6+
* @compile/fail/ref=PatternErrorRecovery-no-preview.out -XDrawDiagnostics -XDshould-stop.at=FLOW PatternErrorRecovery.java
7+
*/
8+
public class PatternErrorRecovery {
9+
void errorRecoveryNoPattern1(Object o) {
10+
switch (o) {
11+
case String: break;
12+
case Object obj: break;
13+
}
14+
}
15+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
PatternErrorRecovery.java:11:18: compiler.err.pattern.expected
2+
- compiler.note.preview.filename: PatternErrorRecovery.java, DEFAULT
3+
- compiler.note.preview.recompile
4+
1 error

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ private void test(Integer i) {
7676
}
7777
}
7878
""",
79-
"Test.java:5:26: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)",
79+
"Test.java:5:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)",
8080
"1 error");
8181
}
8282

0 commit comments

Comments
 (0)