mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 18:03:44 +00:00
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:
parent
8128ca1381
commit
bf70620745
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
157
test/langtools/tools/javac/patterns/SourceLevelChecks.java
Normal file
157
test/langtools/tools/javac/patterns/SourceLevelChecks.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user