[java] Add initial support for Java 14 Preview record types
This commit is contained in:
@ -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)
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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 {}");
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user