mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8235474: JShell does not handle records properly
Reviewed-by: vromero, rfield
This commit is contained in:
parent
a20fa5b995
commit
308aea3e0b
9 changed files with 136 additions and 16 deletions
|
@ -140,7 +140,8 @@ module java.base {
|
||||||
exports jdk.internal to
|
exports jdk.internal to
|
||||||
java.compiler,
|
java.compiler,
|
||||||
jdk.jfr,
|
jdk.jfr,
|
||||||
jdk.compiler;
|
jdk.compiler,
|
||||||
|
jdk.jshell;
|
||||||
exports jdk.internal.access to
|
exports jdk.internal.access to
|
||||||
java.desktop,
|
java.desktop,
|
||||||
java.logging,
|
java.logging,
|
||||||
|
|
|
@ -1238,6 +1238,7 @@ public class Check {
|
||||||
if ((flags & RECORD) != 0) {
|
if ((flags & RECORD) != 0) {
|
||||||
// records can't be declared abstract
|
// records can't be declared abstract
|
||||||
mask &= ~ABSTRACT;
|
mask &= ~ABSTRACT;
|
||||||
|
implicit |= FINAL;
|
||||||
}
|
}
|
||||||
// Imply STRICTFP if owner has STRICTFP set.
|
// Imply STRICTFP if owner has STRICTFP set.
|
||||||
implicit |= sym.owner.flags_field & STRICTFP;
|
implicit |= sym.owner.flags_field & STRICTFP;
|
||||||
|
|
|
@ -3722,7 +3722,7 @@ public class JavacParser implements Parser {
|
||||||
protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) {
|
protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) {
|
||||||
int pos = token.pos;
|
int pos = token.pos;
|
||||||
nextToken();
|
nextToken();
|
||||||
mods.flags |= Flags.RECORD | Flags.FINAL;
|
mods.flags |= Flags.RECORD;
|
||||||
Name name = typeName();
|
Name name = typeName();
|
||||||
|
|
||||||
List<JCTypeParameter> typarams = typeParametersOpt();
|
List<JCTypeParameter> typarams = typeParametersOpt();
|
||||||
|
@ -4117,9 +4117,10 @@ public class JavacParser implements Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isRecordStart() {
|
protected boolean isRecordStart() {
|
||||||
if (token.kind == IDENTIFIER && token.name() == names.record &&
|
if (token.kind == IDENTIFIER && token.name() == names.record &&
|
||||||
(peekToken(TokenKind.IDENTIFIER, TokenKind.LPAREN) ||
|
(peekToken(TokenKind.IDENTIFIER, TokenKind.LPAREN) ||
|
||||||
|
peekToken(TokenKind.IDENTIFIER, TokenKind.EOF) ||
|
||||||
peekToken(TokenKind.IDENTIFIER, TokenKind.LT))) {
|
peekToken(TokenKind.IDENTIFIER, TokenKind.LT))) {
|
||||||
checkSourceLevel(Feature.RECORDS);
|
checkSourceLevel(Feature.RECORDS);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -670,12 +670,12 @@ class CompletenessAnalyzer {
|
||||||
|
|
||||||
public Completeness parseDeclaration() {
|
public Completeness parseDeclaration() {
|
||||||
boolean isImport = token.kind == IMPORT;
|
boolean isImport = token.kind == IMPORT;
|
||||||
boolean isDatum = false;
|
boolean isRecord = false;
|
||||||
boolean afterModifiers = false;
|
boolean afterModifiers = false;
|
||||||
boolean isBracesNeeded = false;
|
boolean isBracesNeeded = false;
|
||||||
while (token.kind.isDeclaration()) {
|
while (token.kind.isDeclaration()) {
|
||||||
isBracesNeeded |= token.kind.isBracesNeeded();
|
isBracesNeeded |= token.kind.isBracesNeeded();
|
||||||
isDatum |= !afterModifiers && token.kind == TK.IDENTIFIER && token.tok.name() == names.record;
|
isRecord |= !afterModifiers && token.kind == TK.IDENTIFIER && token.tok.name() == names.record;
|
||||||
afterModifiers |= !token.kind.isModifier();
|
afterModifiers |= !token.kind.isModifier();
|
||||||
nextToken();
|
nextToken();
|
||||||
}
|
}
|
||||||
|
@ -696,17 +696,11 @@ class CompletenessAnalyzer {
|
||||||
case SEMI:
|
case SEMI:
|
||||||
return Completeness.COMPLETE;
|
return Completeness.COMPLETE;
|
||||||
case IDENTIFIER:
|
case IDENTIFIER:
|
||||||
return isBracesNeeded
|
return isBracesNeeded || isRecord
|
||||||
? Completeness.DEFINITELY_INCOMPLETE
|
? Completeness.DEFINITELY_INCOMPLETE
|
||||||
: Completeness.COMPLETE_WITH_SEMI;
|
: Completeness.COMPLETE_WITH_SEMI;
|
||||||
case BRACKETS:
|
case BRACKETS:
|
||||||
return Completeness.COMPLETE_WITH_SEMI;
|
return Completeness.COMPLETE_WITH_SEMI;
|
||||||
case PARENS:
|
|
||||||
if (isDatum) {
|
|
||||||
return Completeness.COMPLETE_WITH_SEMI;
|
|
||||||
} else {
|
|
||||||
return Completeness.DEFINITELY_INCOMPLETE;
|
|
||||||
}
|
|
||||||
case DOTSTAR:
|
case DOTSTAR:
|
||||||
if (isImport) {
|
if (isImport) {
|
||||||
return Completeness.COMPLETE_WITH_SEMI;
|
return Completeness.COMPLETE_WITH_SEMI;
|
||||||
|
@ -780,6 +774,7 @@ class CompletenessAnalyzer {
|
||||||
case ENUM:
|
case ENUM:
|
||||||
case ANNOTATION_TYPE:
|
case ANNOTATION_TYPE:
|
||||||
case INTERFACE:
|
case INTERFACE:
|
||||||
|
case RECORD:
|
||||||
case METHOD:
|
case METHOD:
|
||||||
return parseDeclaration();
|
return parseDeclaration();
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -229,6 +229,10 @@ class Eval {
|
||||||
return processClass(userSource, unitTree, compileSourceInt, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
|
return processClass(userSource, unitTree, compileSourceInt, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
|
||||||
case INTERFACE:
|
case INTERFACE:
|
||||||
return processClass(userSource, unitTree, compileSourceInt, SubKind.INTERFACE_SUBKIND, pt);
|
return processClass(userSource, unitTree, compileSourceInt, SubKind.INTERFACE_SUBKIND, pt);
|
||||||
|
case RECORD:
|
||||||
|
@SuppressWarnings("preview")
|
||||||
|
List<Snippet> snippets = processClass(userSource, unitTree, compileSourceInt, SubKind.RECORD_SUBKIND, pt);
|
||||||
|
return snippets;
|
||||||
case METHOD:
|
case METHOD:
|
||||||
return processMethod(userSource, unitTree, compileSourceInt, pt);
|
return processMethod(userSource, unitTree, compileSourceInt, pt);
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -179,7 +179,7 @@ class ReplParser extends JavacParser {
|
||||||
default:
|
default:
|
||||||
JCModifiers mods = modifiersOpt(pmods);
|
JCModifiers mods = modifiersOpt(pmods);
|
||||||
if (token.kind == CLASS
|
if (token.kind == CLASS
|
||||||
|| token.kind == IDENTIFIER && token.name() == token.name().table.names.record
|
|| isRecordStart()
|
||||||
|| token.kind == INTERFACE
|
|| token.kind == INTERFACE
|
||||||
|| token.kind == ENUM) {
|
|| token.kind == ENUM) {
|
||||||
return List.<JCTree>of(classOrRecordOrInterfaceOrEnumDeclaration(mods, dc));
|
return List.<JCTree>of(classOrRecordOrInterfaceOrEnumDeclaration(mods, dc));
|
||||||
|
|
|
@ -235,6 +235,23 @@ public abstract class Snippet {
|
||||||
*/
|
*/
|
||||||
ENUM_SUBKIND(Kind.TYPE_DECL),
|
ENUM_SUBKIND(Kind.TYPE_DECL),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@preview Associated with records, a preview feature of the Java language.
|
||||||
|
*
|
||||||
|
* This enum constant is associated with <i>records</i>, a preview
|
||||||
|
* feature of the Java language. Preview features
|
||||||
|
* may be removed in a future release, or upgraded to permanent
|
||||||
|
* features of the Java language.}
|
||||||
|
*
|
||||||
|
* A record declaration.
|
||||||
|
* A {@code SubKind} of {@link Kind#TYPE_DECL}.
|
||||||
|
* @jls 8.10 Record Types
|
||||||
|
* @since 14
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS)
|
||||||
|
RECORD_SUBKIND(Kind.TYPE_DECL),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An annotation interface declaration. A {@code SubKind} of
|
* An annotation interface declaration. A {@code SubKind} of
|
||||||
* {@link Kind#TYPE_DECL}.
|
* {@link Kind#TYPE_DECL}.
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8149524 8131024 8165211 8080071 8130454 8167343 8129559 8114842 8182268 8223782
|
* @bug 8149524 8131024 8165211 8080071 8130454 8167343 8129559 8114842 8182268 8223782 8235474
|
||||||
* @summary Test SourceCodeAnalysis
|
* @summary Test SourceCodeAnalysis
|
||||||
* @build KullaTesting TestingInputStream
|
* @build KullaTesting TestingInputStream
|
||||||
* @run testng CompletenessTest
|
* @run testng CompletenessTest
|
||||||
|
@ -31,11 +31,15 @@
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import javax.lang.model.SourceVersion;
|
||||||
|
import jdk.jshell.JShell;
|
||||||
|
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
import jdk.jshell.SourceCodeAnalysis.Completeness;
|
import jdk.jshell.SourceCodeAnalysis.Completeness;
|
||||||
|
|
||||||
import static jdk.jshell.SourceCodeAnalysis.Completeness.*;
|
import static jdk.jshell.SourceCodeAnalysis.Completeness.*;
|
||||||
|
import org.testng.annotations.BeforeMethod;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public class CompletenessTest extends KullaTesting {
|
public class CompletenessTest extends KullaTesting {
|
||||||
|
@ -82,6 +86,10 @@ public class CompletenessTest extends KullaTesting {
|
||||||
"i >= 0 && Character.isWhitespace(s.charAt(i))",
|
"i >= 0 && Character.isWhitespace(s.charAt(i))",
|
||||||
"int.class",
|
"int.class",
|
||||||
"String.class",
|
"String.class",
|
||||||
|
"record.any",
|
||||||
|
"record()",
|
||||||
|
"record(1)",
|
||||||
|
"record.length()"
|
||||||
};
|
};
|
||||||
|
|
||||||
static final String[] complete_with_semi = new String[] {
|
static final String[] complete_with_semi = new String[] {
|
||||||
|
@ -123,7 +131,7 @@ public class CompletenessTest extends KullaTesting {
|
||||||
"int[] m = {1, 2}",
|
"int[] m = {1, 2}",
|
||||||
"int[] m = {1, 2}, n = null",
|
"int[] m = {1, 2}, n = null",
|
||||||
"int[] m = {1, 2}, n",
|
"int[] m = {1, 2}, n",
|
||||||
"int[] m = {1, 2}, n = {3, 4}"
|
"int[] m = {1, 2}, n = {3, 4}",
|
||||||
};
|
};
|
||||||
|
|
||||||
static final String[] considered_incomplete = new String[] {
|
static final String[] considered_incomplete = new String[] {
|
||||||
|
@ -193,6 +201,28 @@ public class CompletenessTest extends KullaTesting {
|
||||||
"var v = switch (x) { case ",
|
"var v = switch (x) { case ",
|
||||||
"var v = switch (x) { case 0:",
|
"var v = switch (x) { case 0:",
|
||||||
"var v = switch (x) { case 0: break 12; ",
|
"var v = switch (x) { case 0: break 12; ",
|
||||||
|
"record D",
|
||||||
|
"record D(",
|
||||||
|
"record D(String",
|
||||||
|
"record D(String i",
|
||||||
|
"record D(String i,",
|
||||||
|
"record D(String i, String",
|
||||||
|
"record D(String i, String j",
|
||||||
|
"record D(String i)",
|
||||||
|
"record D(String i, String j)",
|
||||||
|
"record D(String i) {",
|
||||||
|
"record D(String i, String j) {",
|
||||||
|
"static record D",
|
||||||
|
"static record D(",
|
||||||
|
"static record D(String",
|
||||||
|
"static record D(String i",
|
||||||
|
"static record D(String i,",
|
||||||
|
"static record D(String i, String",
|
||||||
|
"static record D(String i, String j",
|
||||||
|
"static record D(String i)",
|
||||||
|
"static record D(String i, String j)",
|
||||||
|
"static record D(String i) {",
|
||||||
|
"static record D(String i, String j) {",
|
||||||
};
|
};
|
||||||
|
|
||||||
static final String[] unknown = new String[] {
|
static final String[] unknown = new String[] {
|
||||||
|
@ -241,7 +271,7 @@ public class CompletenessTest extends KullaTesting {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void test_complete() {
|
public void test_complete() {
|
||||||
assertStatus(complete, COMPLETE);
|
assertStatus(complete, COMPLETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void test_expression() {
|
public void test_expression() {
|
||||||
|
@ -349,4 +379,10 @@ public class CompletenessTest extends KullaTesting {
|
||||||
assertStatus("int[] m = {1, 2}, n = new int[0]; int i;", COMPLETE,
|
assertStatus("int[] m = {1, 2}, n = new int[0]; int i;", COMPLETE,
|
||||||
"int[] m = {1, 2}, n = new int[0];");
|
"int[] m = {1, 2}, n = new int[0];");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@BeforeMethod
|
||||||
|
public void setUp() {
|
||||||
|
setUp(b -> b.compilerOptions("--enable-preview", "-source", String.valueOf(SourceVersion.latest().ordinal())));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
65
test/langtools/jdk/jshell/RecordsTest.java
Normal file
65
test/langtools/jdk/jshell/RecordsTest.java
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019, 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 8235474
|
||||||
|
* @summary Tests for evalution of records
|
||||||
|
* @modules jdk.jshell
|
||||||
|
* @build KullaTesting TestingInputStream ExpectedDiagnostic
|
||||||
|
* @run testng RecordsTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import javax.lang.model.SourceVersion;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import org.testng.annotations.BeforeMethod;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public class RecordsTest extends KullaTesting {
|
||||||
|
|
||||||
|
public void testRecordClass() {
|
||||||
|
assertEval("record R(String s, int i) { }");
|
||||||
|
assertEquals(varKey(assertEval("R r = new R(\"r\", 42);")).name(), "r");
|
||||||
|
assertEval("r.s()", "\"r\"");
|
||||||
|
assertEval("r.i()", "42");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRecordField() {
|
||||||
|
assertEquals(varKey(assertEval("String record = \"\";")).name(), "record");
|
||||||
|
assertEval("record.length()", "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRecordMethod() {
|
||||||
|
assertEquals(methodKey(assertEval("String record(String record) { return record + record; }")).name(), "record");
|
||||||
|
assertEval("record(\"r\")", "\"rr\"");
|
||||||
|
assertEval("record(\"r\").length()", "2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMethod
|
||||||
|
public void setUp() {
|
||||||
|
setUp(b -> b.compilerOptions("--enable-preview", "-source", String.valueOf(SourceVersion.latest().ordinal()))
|
||||||
|
.remoteVMOptions("--enable-preview"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue