8268961: Parenthesized pattern with guards does not work

8268896: Parenthesized pattern is not guarded by source level check

Reviewed-by: vromero
This commit is contained in:
Jan Lahoda 2021-06-23 10:16:42 +00:00
parent 8128ca1381
commit bf70620745
8 changed files with 255 additions and 30 deletions

View File

@ -4064,24 +4064,19 @@ public class Attr extends JCTree.Visitor {
tree.expr.pos(), attribExpr(tree.expr, env));
Type clazztype;
JCTree typeTree;
boolean checkRawTypes;
if (tree.pattern.getTag() == BINDINGPATTERN) {
if (tree.pattern.getTag() == BINDINGPATTERN ||
tree.pattern.getTag() == PARENTHESIZEDPATTERN) {
attribTree(tree.pattern, env, unknownExprInfo);
clazztype = tree.pattern.type;
if (types.isSubtype(exprtype, clazztype) &&
!exprtype.isErroneous() && !clazztype.isErroneous()) {
log.error(tree.pos(), Errors.InstanceofPatternNoSubtype(exprtype, clazztype));
}
JCBindingPattern pattern = (JCBindingPattern) tree.pattern;
typeTree = pattern.var.vartype;
if (!clazztype.hasTag(TYPEVAR)) {
clazztype = chk.checkClassOrArrayType(pattern.var.vartype.pos(), clazztype);
}
checkRawTypes = true;
typeTree = TreeInfo.primaryPatternTree((JCPattern) tree.pattern).var.vartype;
} else {
clazztype = attribType(tree.pattern, env);
typeTree = tree.pattern;
checkRawTypes = false;
chk.validate(typeTree, env, false);
}
if (!clazztype.hasTag(TYPEVAR)) {
clazztype = chk.checkClassOrArrayType(typeTree.pos(), clazztype);
@ -4099,7 +4094,6 @@ public class Attr extends JCTree.Visitor {
clazztype = types.createErrorType(clazztype);
}
}
chk.validate(typeTree, env, checkRawTypes);
chk.checkCastable(tree.expr.pos(), exprtype, clazztype);
result = check(tree, syms.booleanType, KindSelector.VAL, resultInfo);
}
@ -4133,6 +4127,7 @@ public class Attr extends JCTree.Visitor {
annotate.annotateLater(tree.var.mods.annotations, env, v, tree.pos());
annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v, tree.var.pos());
annotate.flush();
chk.validate(tree.var.vartype, env, true);
result = tree.type;
matchBindings = new MatchBindings(List.of(v), List.nil());
}
@ -4140,6 +4135,7 @@ public class Attr extends JCTree.Visitor {
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
attribExpr(tree.pattern, env);
result = tree.type = tree.pattern.type;
}
@Override

View File

@ -764,26 +764,26 @@ public class JavacParser implements Parser {
*/
public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType, boolean inInstanceOf) {
JCPattern pattern;
if (token.kind == LPAREN && parsedType == null) {
int startPos = token.pos;
accept(LPAREN);
JCPattern p = parsePattern(token.pos, null, null, false);
accept(RPAREN);
return toP(F.at(startPos).ParenthesizedPattern(p));
pattern = toP(F.at(startPos).ParenthesizedPattern(p));
} else {
JCPattern pattern;
JCExpression e = parsedType == null ? term(EXPR | TYPE | NOLAMBDA) : parsedType;
mods = mods != null ? mods : F.at(token.pos).Modifiers(0);
JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null));
pattern = toP(F.at(pos).BindingPattern(var));
if (!inInstanceOf && token.kind == AMPAMP) {
checkSourceLevel(Feature.PATTERN_SWITCH);
nextToken();
JCExpression guard = term(EXPR | NOLAMBDA);
pattern = F.at(pos).GuardPattern(pattern, guard);
}
return pattern;
}
if (!inInstanceOf && token.kind == AMPAMP) {
checkSourceLevel(Feature.PATTERN_SWITCH);
nextToken();
JCExpression guard = term(EXPR | NOLAMBDA);
pattern = F.at(pos).GuardPattern(pattern, guard);
}
return pattern;
}
/**
@ -1694,12 +1694,16 @@ public class JavacParser implements Parser {
* method reference or a binary expression. To disambiguate, look for a
* matching '>' and see if the subsequent terminal is either '.' or '::'.
*/
@SuppressWarnings("fallthrough")
ParensResult analyzeParens() {
return analyzeParens(0);
}
@SuppressWarnings("fallthrough")
ParensResult analyzeParens(int startLookahead) {
int depth = 0;
boolean type = false;
ParensResult defaultResult = ParensResult.PARENS;
outer: for (int lookahead = 0; ; lookahead++) {
outer: for (int lookahead = startLookahead; ; lookahead++) {
TokenKind tk = S.token(lookahead).kind;
switch (tk) {
case COMMA:
@ -1725,7 +1729,7 @@ public class JavacParser implements Parser {
}
break;
case LPAREN:
if (lookahead != 0) {
if (lookahead != startLookahead) {
// '(' in a non-starting position -> parens
return ParensResult.PARENS;
} else if (peekToken(lookahead, RPAREN)) {
@ -3065,15 +3069,12 @@ public class JavacParser implements Parser {
} else {
if (token.kind == LPAREN) {
int lookahead = 0;
Token ahead;
while ((ahead = S.token(lookahead)).kind != EOF && ahead.kind != RPAREN && ahead.kind != AMPAMP) {
while (S.token(lookahead + 1).kind == LPAREN) {
lookahead++;
}
Token twoBack;
boolean pattern = S.token(lookahead - 1).kind == IDENTIFIER &&
((twoBack = S.token(lookahead - 2)).kind == IDENTIFIER ||
twoBack.kind == GT || twoBack.kind == GTGT || twoBack.kind == GTGTGT);
boolean pattern = analyzeParens(lookahead) == ParensResult.EXPLICIT_LAMBDA;
if (pattern) {
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
return parsePattern(token.pos, null, null, false);
} else {
return term(EXPR | TYPE | NOLAMBDA);

View File

@ -1367,6 +1367,15 @@ public class TreeInfo {
};
}
public static JCBindingPattern primaryPatternTree(JCPattern pat) {
return switch (pat.getTag()) {
case BINDINGPATTERN -> (JCBindingPattern) pat;
case GUARDPATTERN -> primaryPatternTree(((JCGuardPattern) pat).patt);
case PARENTHESIZEDPATTERN -> primaryPatternTree(((JCParenthesizedPattern) pat).pattern);
default -> throw new AssertionError();
};
}
public record PatternPrimaryType(Type type, boolean unconditional) {}
}

View File

@ -42,6 +42,20 @@ public class DisambiguateParenthesizedPattern {
ExpressionType.EXPRESSION);
test.disambiguationTest("((0x1))",
ExpressionType.EXPRESSION);
test.disambiguationTest("(a > b)",
ExpressionType.EXPRESSION);
test.disambiguationTest("(a >> b)",
ExpressionType.EXPRESSION);
test.disambiguationTest("(a >>> b)",
ExpressionType.EXPRESSION);
test.disambiguationTest("(a < b | a > b)",
ExpressionType.EXPRESSION);
test.disambiguationTest("(a << b | a >> b)",
ExpressionType.EXPRESSION);
test.disambiguationTest("(a << b || a < b | a >>> b)",
ExpressionType.EXPRESSION);
test.disambiguationTest("(a < c.d > b)",
ExpressionType.PATTERN);
}
private final ParserFactory factory;

View File

@ -46,6 +46,9 @@ public class Guards {
runIfTrue(this::typeGuardIfTrueIfStatement);
runIfTrue(this::typeGuardIfTrueSwitchExpression);
runIfTrue(this::typeGuardIfTrueSwitchStatement);
runIfTrue(this::typeGuardAfterParenthesizedTrueSwitchStatement);
runIfTrue(this::typeGuardAfterParenthesizedTrueSwitchExpression);
runIfTrue(this::typeGuardAfterParenthesizedTrueIfStatement);
}
void run(Function<Object, String> convert) {
@ -122,6 +125,32 @@ public class Guards {
}
}
String typeGuardAfterParenthesizedTrueSwitchStatement(Object o) {
switch (o) {
case (Integer i) && i == 0: o = String.valueOf(i); return "true";
case ((Integer i) && i == 2): o = String.valueOf(i); return "second";
case Object x: return "any";
}
}
String typeGuardAfterParenthesizedTrueSwitchExpression(Object o) {
return switch (o) {
case (Integer i) && i == 0: o = String.valueOf(i); yield "true";
case ((Integer i) && i == 2): o = String.valueOf(i); yield "second";
case Object x: yield "any";
};
}
String typeGuardAfterParenthesizedTrueIfStatement(Object o) {
if (o != null && o instanceof ((Integer i) && i == 0)) {
return "true";
} else if (o != null && o instanceof (((Integer i) && i == 2)) && (o = i) != null) {
return "second";
} else {
return "any";
}
}
String testPatternInGuard(Object o) {
if (o instanceof (CharSequence cs && cs instanceof String s)) {
return s;

View File

@ -2,10 +2,24 @@
* @test /nodynamiccopyright/
* @bug 8263590
* @summary Verify correct warnings are produced for raw types in bindings
* @compile/ref=RawTypeBindingWarning.out -Xlint:rawtypes -XDrawDiagnostics RawTypeBindingWarning.java
* @compile/ref=RawTypeBindingWarning.out -Xlint:rawtypes -XDrawDiagnostics --enable-preview -source ${jdk.version} RawTypeBindingWarning.java
*/
public class RawTypeBindingWarning<T> {
public static boolean t(Object o) {
return o instanceof RawTypeBindingWarning w;
}
public static void t2(Object o) {
switch (o) {
case RawTypeBindingWarning w -> {}
default -> {}
}
switch (o) {
case (RawTypeBindingWarning w) -> {}
default -> {}
}
switch (o) {
case (RawTypeBindingWarning w && false) -> {}
default -> {}
}
}
}

View File

@ -1,2 +1,7 @@
RawTypeBindingWarning.java:9:29: compiler.warn.raw.class.use: RawTypeBindingWarning, RawTypeBindingWarning<T>
1 warning
RawTypeBindingWarning.java:13:18: compiler.warn.raw.class.use: RawTypeBindingWarning, RawTypeBindingWarning<T>
RawTypeBindingWarning.java:17:19: compiler.warn.raw.class.use: RawTypeBindingWarning, RawTypeBindingWarning<T>
RawTypeBindingWarning.java:21:19: compiler.warn.raw.class.use: RawTypeBindingWarning, RawTypeBindingWarning<T>
- compiler.note.preview.filename: RawTypeBindingWarning.java, DEFAULT
- compiler.note.preview.recompile
4 warnings

View File

@ -0,0 +1,157 @@
/*
* 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.
*/
/**
* @test
* @bug 8268896
* @summary Verify source level checks are performed properly
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.util
* @build toolbox.ToolBox toolbox.JavacTask
* @run main SourceLevelChecks
*/
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import toolbox.TestRunner;
import toolbox.JavacTask;
import toolbox.Task;
import toolbox.ToolBox;
public class SourceLevelChecks extends TestRunner {
private static final String JAVA_VERSION = System.getProperty("java.specification.version");
ToolBox tb;
public static void main(String... args) throws Exception {
new SourceLevelChecks().runTests();
}
SourceLevelChecks() {
super(System.err);
tb = new ToolBox();
}
public void runTests() throws Exception {
runTests(m -> new Object[] { Paths.get(m.getName()) });
}
@Test
public void testPattern(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(Integer i) {
switch (i) {
case Integer d:
}
}
}
""",
"Test.java:5:26: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)",
"1 error");
}
@Test
public void testParenthesizedPatternIf(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(Object o) {
if (o instanceof (Integer d)) {
}
}
}
""",
"Test.java:4:26: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)",
"1 error");
}
@Test
public void testParenthesizedPatternSwitch(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(Integer i) {
switch (i) {
case (Integer d):
}
}
}
""",
"Test.java:5:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)",
"1 error");
}
@Test
public void testCaseDefault(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(Integer i) {
switch (i) {
case default:
}
}
}
""",
"Test.java:5:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)",
"1 error");
}
private void doTest(Path base, String testCode, String... expectedErrors) throws IOException {
Path current = base.resolve(".");
Path src = current.resolve("src");
Path classes = current.resolve("classes");
tb.writeJavaFiles(src, testCode);
Files.createDirectories(classes);
var log =
new JavacTask(tb)
.options("-source", "11",
"-Xlint:-options",
"-XDrawDiagnostics")
.outdir(classes)
.files(tb.findJavaFiles(src))
.run(Task.Expect.FAIL)
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
if (!List.of(expectedErrors).equals(log)) {
throw new AssertionError("Incorrect errors, expected: " + List.of(expectedErrors) +
", actual: " + log);
}
}
}