[java] Add initial support for Java 14 Preview record types

This commit is contained in:
Andreas Dangel
2020-02-28 14:58:52 +01:00
parent 524925f8cb
commit 8a224462fd
17 changed files with 466 additions and 2 deletions

View File

@ -1,5 +1,9 @@
/**
* Add support for pattern matching for instance of introduced as
* Add support for record types introduced as a preview language
* feature with Java 14. See JEP 359.
* Andreas Dangel 02/2020
*====================================================================
* Add support for pattern matching for instance of introduced
* as a preview language feature with Java 14. See JEP 305.
* Clément Fournier 02/2020
*====================================================================
@ -409,6 +413,9 @@ public class JavaParser {
if (jdkVersion >= 10 && "var".equals(image)) {
throwParseException("With JDK 10, 'var' is a restricted local variable type and cannot be used for type declarations!");
}
if (jdkVersion >= 14 && preview && "record".equals(image)) {
throwParseException("With JDK 14 Preview, 'record' is a restricted identifier and cannot be used for type declarations!");
}
}
private void checkForMultipleCaseLabels() {
if (!switchExprAllowed()) {
@ -468,6 +475,12 @@ public class JavaParser {
}
}
private void checkForRecordType() {
if (jdkVersion != 14 || !preview) {
throwParseException("Records are only supported with Java 14 Preview");
}
}
// This is a semantic LOOKAHEAD to determine if we're dealing with an assert
// Note that this can't be replaced with a syntactic lookahead
@ -1024,6 +1037,8 @@ void TypeDeclaration():
LOOKAHEAD({isKeyword("enum")}) EnumDeclaration(modifiers)
|
AnnotationTypeDeclaration(modifiers)
|
LOOKAHEAD({isKeyword("record")}) RecordDeclaration(modifiers)
)
}
@ -1102,6 +1117,66 @@ void EnumConstant():
t=<IDENTIFIER> {jjtThis.setImage(t.image);} [ Arguments() ] [ ClassOrInterfaceBody() ]
}
void RecordDeclaration(int modifiers):
{
Token t;
jjtThis.setModifiers(modifiers);
checkForRecordType();
}
{
t = <IDENTIFIER> {
if (!"record".equals(t.image)) {
throw new ParseException("ERROR: expecting record");
}
}
t=<IDENTIFIER> {checkForBadTypeIdentifierUsage(t.image); jjtThis.setImage(t.image);}
[ TypeParameters() ]
"(" RecordComponents() ")"
[ ImplementsList() ]
RecordBody()
}
void RecordComponents() :
{}
{
RecordComponent() ("," RecordComponent())*
}
void RecordComponent():
{}
{
(TypeAnnotation())* Type() <IDENTIFIER> { jjtThis.setImage(token.image); }
}
void RecordBody():
{}
{
"{"
( RecordBodyDeclaration() )*
"}"
}
void RecordBodyDeclaration():
{}
{
LOOKAHEAD(4) RecordConstructorDeclaration()
|
ClassOrInterfaceBodyDeclaration()
}
void RecordConstructorDeclaration():
{
int modifiers;
}
{
(TypeAnnotation())*
modifiers = Modifiers() { jjtThis.setModifiers(modifiers); }
[TypeParameters()]
Name()
[ "throws" NameList() ]
[ "{" ( BlockStatement() )* "}" ]
}
void TypeParameters():
{}
{
@ -1135,6 +1210,7 @@ void ClassOrInterfaceBodyDeclaration():
| modifiers = Modifiers()
( LOOKAHEAD(3) ClassOrInterfaceDeclaration(modifiers)
| LOOKAHEAD({isKeyword("enum")}) EnumDeclaration(modifiers)
| LOOKAHEAD({isKeyword("record")}) RecordDeclaration(modifiers)
| LOOKAHEAD( [ TypeParameters() ] <IDENTIFIER> "(" ) ConstructorDeclaration(modifiers)
| LOOKAHEAD( Type() <IDENTIFIER> ( "[" "]" )* ( "," | "=" | ";" ) ) FieldDeclaration(modifiers)
| LOOKAHEAD(2) MethodDeclaration(modifiers)

View File

@ -12,7 +12,7 @@ import net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName;
/**
* Groups enum, class, annotation and interface declarations.
* Groups class, enum, record, annotation and interface declarations.
*
* @author Clément Fournier
*/

View File

@ -0,0 +1,24 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
@Experimental
public class ASTRecordBody extends AbstractJavaNode {
ASTRecordBody(int id) {
super(id);
}
ASTRecordBody(JavaParser p, int id) {
super(p, id);
}
@Override
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}

View File

@ -0,0 +1,24 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
@Experimental
public class ASTRecordBodyDeclaration extends AbstractJavaNode {
ASTRecordBodyDeclaration(int id) {
super(id);
}
ASTRecordBodyDeclaration(JavaParser p, int id) {
super(p, id);
}
@Override
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}

View File

@ -0,0 +1,24 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
@Experimental
public class ASTRecordComponent extends AbstractJavaNode {
ASTRecordComponent(int id) {
super(id);
}
ASTRecordComponent(JavaParser p, int id) {
super(p, id);
}
@Override
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}

View File

@ -0,0 +1,24 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
@Experimental
public class ASTRecordComponents extends AbstractJavaNode {
ASTRecordComponents(int id) {
super(id);
}
ASTRecordComponents(JavaParser p, int id) {
super(p, id);
}
@Override
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}

View File

@ -0,0 +1,24 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
@Experimental
public class ASTRecordConstructorDeclaration extends AbstractJavaAccessNode {
ASTRecordConstructorDeclaration(int id) {
super(id);
}
ASTRecordConstructorDeclaration(JavaParser p, int id) {
super(p, id);
}
@Override
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}

View File

@ -0,0 +1,37 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import java.util.List;
import net.sourceforge.pmd.annotation.Experimental;
@Experimental
public class ASTRecordDeclaration extends AbstractAnyTypeDeclaration {
ASTRecordDeclaration(int id) {
super(id);
}
ASTRecordDeclaration(JavaParser p, int id) {
super(p, id);
}
@Override
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
@Override
public TypeKind getTypeKind() {
return null;
}
@Override
public List<ASTAnyTypeBodyDeclaration> getDeclarations() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -907,4 +907,46 @@ public class JavaParserDecoratedVisitor implements JavaParserVisitor {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordDeclaration node, Object data) {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordComponents node, Object data) {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordComponent node, Object data) {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordBody node, Object data) {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordBodyDeclaration node, Object data) {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordConstructorDeclaration node, Object data) {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
}

View File

@ -4,6 +4,8 @@
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
public class JavaParserVisitorAdapter implements JavaParserVisitor {
@Override
@ -627,7 +629,44 @@ public class JavaParserVisitorAdapter implements JavaParserVisitor {
}
@Override
@Experimental
public Object visit(ASTTypeTestPattern node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordComponents node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordComponent node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordBody node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordBodyDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordConstructorDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
}

View File

@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.annotation.InternalApi;
/**
@ -760,7 +761,44 @@ public class JavaParserVisitorDecorator implements JavaParserControllessVisitor
}
@Override
@Experimental
public Object visit(ASTTypeTestPattern node, Object data) {
return visitor.visit(node, data);
}
@Override
@Experimental
public Object visit(ASTRecordDeclaration node, Object data) {
return visitor.visit(node, data);
}
@Override
@Experimental
public Object visit(ASTRecordComponents node, Object data) {
return visitor.visit(node, data);
}
@Override
@Experimental
public Object visit(ASTRecordComponent node, Object data) {
return visitor.visit(node, data);
}
@Override
@Experimental
public Object visit(ASTRecordBody node, Object data) {
return visitor.visit(node, data);
}
@Override
@Experimental
public Object visit(ASTRecordBodyDeclaration node, Object data) {
return visitor.visit(node, data);
}
@Override
@Experimental
public Object visit(ASTRecordConstructorDeclaration node, Object data) {
return visitor.visit(node, data);
}
}

View File

@ -29,6 +29,12 @@ public class JavaParserVisitorReducedAdapter extends JavaParserVisitorAdapter {
}
@Override
public Object visit(ASTRecordDeclaration node, Object data) {
return visit((ASTAnyTypeDeclaration) node, data);
}
public Object visit(ASTAnyTypeDeclaration node, Object data) {
return visit((JavaNode) node, data);
}

View File

@ -96,6 +96,12 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
import net.sourceforge.pmd.lang.java.ast.ASTRSIGNEDSHIFT;
import net.sourceforge.pmd.lang.java.ast.ASTRUNSIGNEDSHIFT;
import net.sourceforge.pmd.lang.java.ast.ASTRecordBody;
import net.sourceforge.pmd.lang.java.ast.ASTRecordBodyDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTRecordComponent;
import net.sourceforge.pmd.lang.java.ast.ASTRecordComponents;
import net.sourceforge.pmd.lang.java.ast.ASTRecordConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTRecordDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
import net.sourceforge.pmd.lang.java.ast.ASTRelationalExpression;
import net.sourceforge.pmd.lang.java.ast.ASTResource;
@ -842,4 +848,40 @@ public abstract class AbstractJavaRule extends AbstractRule implements JavaParse
public Object visit(ASTTypeTestPattern node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordComponents node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordComponent node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordBody node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordBodyDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
@Override
@Experimental
public Object visit(ASTRecordConstructorDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
}

View File

@ -21,6 +21,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTRecordDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement;
import net.sourceforge.pmd.lang.java.ast.ASTTryStatement;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
@ -188,6 +189,13 @@ public class ScopeAndDeclarationFinder extends JavaParserVisitorAdapter {
return data;
}
@Override
public Object visit(ASTRecordDeclaration node, Object data) {
createClassScope(node);
cont(node);
return data;
}
@Override
public Object visit(ASTClassOrInterfaceBody node, Object data) {
if (node.isAnonymousInnerClass() || node.isEnumChild()) {

View File

@ -90,4 +90,33 @@ public class Java14PreviewTest {
public void patternMatchingInstanceofBeforeJava14PreviewShouldFail() {
java14.parseResource("PatternMatchingInstanceof.java");
}
@Test
public void recordPoint() {
ASTCompilationUnit compilationUnit = java14p.parseResource("Point.java");
ASTRecordDeclaration recordDecl = compilationUnit.getFirstDescendantOfType(ASTRecordDeclaration.class);
Assert.assertEquals("Point", recordDecl.getImage());
List<ASTRecordComponent> components = recordDecl.getFirstChildOfType(ASTRecordComponents.class)
.findChildrenOfType(ASTRecordComponent.class);
Assert.assertEquals(2, components.size());
Assert.assertEquals("x", components.get(0).getImage());
Assert.assertEquals("y", components.get(1).getImage());
}
@Test(expected = ParseException.class)
public void recordPointBeforeJava14PreviewShouldFail() {
java14.parseResource("Point.java");
}
@Test
public void innerRecords() {
ASTCompilationUnit compilationUnit = java14p.parseResource("Records.java");
List<ASTRecordDeclaration> recordDecls = compilationUnit.findDescendantsOfType(ASTRecordDeclaration.class);
Assert.assertEquals(2, recordDecls.size());
}
@Test(expected = ParseException.class)
public void recordIsARestrictedIdentifier() {
java14p.parse("public class record {}");
}
}

View File

@ -0,0 +1,10 @@
/**
* @see <a href="https://openjdk.java.net/jeps/359">JEP 359: Records (Preview)</a>
*/
public record Point(int x, int y) {
public static void main(String[] args) {
Point p = new Point(1, 2);
System.out.println("p = " + p);
}
}

View File

@ -0,0 +1,17 @@
/**
* @see <a href="https://openjdk.java.net/jeps/359">JEP 359: Records (Preview)</a>
*/
public class Records {
public record MyComplex(int real, int imaginary) {
};
public record Range(int lo, int hi) {
public Range {
if (lo > hi) /* referring here to the implicit constructor parameters */
throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi));
}
}
}