8235474: JShell does not handle records properly

Reviewed-by: vromero, rfield
This commit is contained in:
Jan Lahoda 2019-12-11 13:08:42 +01:00
parent a20fa5b995
commit 308aea3e0b
9 changed files with 136 additions and 16 deletions

View File

@ -140,7 +140,8 @@ module java.base {
exports jdk.internal to
java.compiler,
jdk.jfr,
jdk.compiler;
jdk.compiler,
jdk.jshell;
exports jdk.internal.access to
java.desktop,
java.logging,

View File

@ -1238,6 +1238,7 @@ public class Check {
if ((flags & RECORD) != 0) {
// records can't be declared abstract
mask &= ~ABSTRACT;
implicit |= FINAL;
}
// Imply STRICTFP if owner has STRICTFP set.
implicit |= sym.owner.flags_field & STRICTFP;

View File

@ -3722,7 +3722,7 @@ public class JavacParser implements Parser {
protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) {
int pos = token.pos;
nextToken();
mods.flags |= Flags.RECORD | Flags.FINAL;
mods.flags |= Flags.RECORD;
Name name = typeName();
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 &&
(peekToken(TokenKind.IDENTIFIER, TokenKind.LPAREN) ||
peekToken(TokenKind.IDENTIFIER, TokenKind.EOF) ||
peekToken(TokenKind.IDENTIFIER, TokenKind.LT))) {
checkSourceLevel(Feature.RECORDS);
return true;

View File

@ -670,12 +670,12 @@ class CompletenessAnalyzer {
public Completeness parseDeclaration() {
boolean isImport = token.kind == IMPORT;
boolean isDatum = false;
boolean isRecord = false;
boolean afterModifiers = false;
boolean isBracesNeeded = false;
while (token.kind.isDeclaration()) {
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();
nextToken();
}
@ -696,17 +696,11 @@ class CompletenessAnalyzer {
case SEMI:
return Completeness.COMPLETE;
case IDENTIFIER:
return isBracesNeeded
return isBracesNeeded || isRecord
? Completeness.DEFINITELY_INCOMPLETE
: Completeness.COMPLETE_WITH_SEMI;
case BRACKETS:
return Completeness.COMPLETE_WITH_SEMI;
case PARENS:
if (isDatum) {
return Completeness.COMPLETE_WITH_SEMI;
} else {
return Completeness.DEFINITELY_INCOMPLETE;
}
case DOTSTAR:
if (isImport) {
return Completeness.COMPLETE_WITH_SEMI;
@ -780,6 +774,7 @@ class CompletenessAnalyzer {
case ENUM:
case ANNOTATION_TYPE:
case INTERFACE:
case RECORD:
case METHOD:
return parseDeclaration();
default:

View File

@ -229,6 +229,10 @@ class Eval {
return processClass(userSource, unitTree, compileSourceInt, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
case INTERFACE:
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:
return processMethod(userSource, unitTree, compileSourceInt, pt);
default:

View File

@ -179,7 +179,7 @@ class ReplParser extends JavacParser {
default:
JCModifiers mods = modifiersOpt(pmods);
if (token.kind == CLASS
|| token.kind == IDENTIFIER && token.name() == token.name().table.names.record
|| isRecordStart()
|| token.kind == INTERFACE
|| token.kind == ENUM) {
return List.<JCTree>of(classOrRecordOrInterfaceOrEnumDeclaration(mods, dc));

View File

@ -235,6 +235,23 @@ public abstract class Snippet {
*/
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
* {@link Kind#TYPE_DECL}.

View File

@ -23,7 +23,7 @@
/*
* @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
* @build KullaTesting TestingInputStream
* @run testng CompletenessTest
@ -31,11 +31,15 @@
import java.util.Map;
import java.util.HashMap;
import java.util.function.Consumer;
import javax.lang.model.SourceVersion;
import jdk.jshell.JShell;
import org.testng.annotations.Test;
import jdk.jshell.SourceCodeAnalysis.Completeness;
import static jdk.jshell.SourceCodeAnalysis.Completeness.*;
import org.testng.annotations.BeforeMethod;
@Test
public class CompletenessTest extends KullaTesting {
@ -82,6 +86,10 @@ public class CompletenessTest extends KullaTesting {
"i >= 0 && Character.isWhitespace(s.charAt(i))",
"int.class",
"String.class",
"record.any",
"record()",
"record(1)",
"record.length()"
};
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}, n = null",
"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[] {
@ -193,6 +201,28 @@ public class CompletenessTest extends KullaTesting {
"var v = switch (x) { case ",
"var v = switch (x) { case 0:",
"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[] {
@ -241,7 +271,7 @@ public class CompletenessTest extends KullaTesting {
}
public void test_complete() {
assertStatus(complete, COMPLETE);
assertStatus(complete, COMPLETE);
}
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,
"int[] m = {1, 2}, n = new int[0];");
}
@BeforeMethod
public void setUp() {
setUp(b -> b.compilerOptions("--enable-preview", "-source", String.valueOf(SourceVersion.latest().ordinal())));
}
}

View 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"));
}
}