[java] JEP 397: Sealed Classes (Second Preview) for Java16 Preview

This commit is contained in:
Andreas Dangel
2021-02-15 19:33:36 +01:00
parent 8bc26f95aa
commit 3b151e31c4
20 changed files with 314 additions and 15 deletions

View File

@ -1,4 +1,5 @@
/**
* JEP 397: Sealed Classes (Second Preview) for Java16 Preview
* JEP 395: Records for Java16
* JEP 394: Pattern Matching for instanceof for Java16
* Andreas Dangel 02/2021
@ -425,19 +426,19 @@ public class JavaParser {
}
private void checkForBadTypeIdentifierUsage(String image) {
if (jdkVersion >= 10 && "var".equals(image)) {
throwParseException("With JDK 10, 'var' is a restricted local variable type and cannot be used for type declarations!");
throwParseException("With JDK 10, 'var' is a contextual keyword and cannot be used for type declarations!");
}
if (jdkVersion >= 14 && "yield".equals(image)) {
throwParseException("With JDK 14, 'yield' is a restricted local variable type and cannot be used for type declarations!");
throwParseException("With JDK 14, 'yield' is a contextual keyword and cannot be used for type declarations!");
}
if ((jdkVersion >= 14 && preview || jdkVersion >= 16) && "record".equals(image)) {
throwParseException("With JDK 14 Preview and JDK 15 Preview and Java >= 16, 'record' is a restricted identifier and cannot be used for type declarations!");
if ((jdkVersion == 14 && preview || jdkVersion == 15 && preview || jdkVersion >= 16) && "record".equals(image)) {
throwParseException("With JDK 14 Preview and JDK 15 Preview and Java >= 16, 'record' is a contextual keyword and cannot be used for type declarations!");
}
if (jdkVersion >= 15 && preview && "sealed".equals(image)) {
throwParseException("With JDK 15 Preview, 'sealed' is a restricted identifier and cannot be used for type declarations!");
if ((jdkVersion == 15 && preview || jdkVersion == 16 && preview) && "sealed".equals(image)) {
throwParseException("With JDK 15 Preview and JDK 16 Preview, 'sealed' is a contextual keyword and cannot be used for type declarations!");
}
if (jdkVersion >= 15 && preview && "permits".equals(image)) {
throwParseException("With JDK 15 Preview, 'permits' is a restricted identifier and cannot be used for type declarations!");
if ((jdkVersion == 15 && preview || jdkVersion == 16 && preview) && "permits".equals(image)) {
throwParseException("With JDK 15 Preview and JDK 16 Preview, 'permits' is a contextual keyword and cannot be used for type declarations!");
}
}
private void checkForMultipleCaseLabels() {
@ -501,7 +502,7 @@ public class JavaParser {
}
private boolean isSealedClassSupported() {
return jdkVersion == 15 && preview;
return jdkVersion == 15 && preview || jdkVersion == 16 && preview;
}
private void checkForSealedClassUsage() {
@ -1219,8 +1220,8 @@ void PermittedSubclasses() #PermitsList:
throw new ParseException("ERROR: expecting permits");
}
}
(TypeAnnotation())* ClassOrInterfaceType()
( "," (TypeAnnotation())* ClassOrInterfaceType() )*
TypeName()
( "," TypeName() )*
}
void EnumDeclaration(int modifiers):
@ -1536,6 +1537,17 @@ void ClassOrInterfaceType():
{jjtThis.setImage(s.toString());}
}
void TypeName() #ClassOrInterfaceType:
{
StringBuilder s = new StringBuilder();
Token t;
}
{
t=<IDENTIFIER> {s.append(t.image);}
( "." t=<IDENTIFIER> {s.append('.').append(t.image);} )*
{jjtThis.setImage(s.toString());}
}
void TypeArguments():
{}
{

View File

@ -12,14 +12,14 @@ import net.sourceforge.pmd.annotation.Experimental;
/**
* Represents the {@code permits} clause of a (sealed) class declaration.
*
* <p>This is a Java 15 Preview feature.
* <p>This is a Java 15 Preview and Java 16 Preview feature.
*
* <p>See https://openjdk.java.net/jeps/360
* <p>See https://openjdk.java.net/jeps/397
*
* <pre class="grammar">
*
* PermittedSubclasses ::= "permits" (TypeAnnotation)* ClassOrInterfaceType
* ( "," (TypeAnnotation)* ClassOrInterfaceType )*
* PermittedSubclasses ::= "permits" ClassOrInterfaceType
* ( "," ClassOrInterfaceType )*
* </pre>
*/
@Experimental

View File

@ -0,0 +1,54 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import org.junit.Test;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper;
import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest;
import net.sourceforge.pmd.lang.ast.test.RelevantAttributePrinter;
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class Java16PreviewTreeDumpTest extends BaseTreeDumpTest {
private final JavaParsingHelper java16p =
JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("16-preview")
.withResourceContext(Java16PreviewTreeDumpTest.class, "jdkversiontests/java16p/");
private final JavaParsingHelper java16 = java16p.withDefaultVersion("16");
public Java16PreviewTreeDumpTest() {
super(new RelevantAttributePrinter(), ".java");
}
@Override
public BaseParsingHelper<?, ?> getParser() {
return java16p;
}
@Test(expected = ParseException.class)
public void sealedClassBeforeJava16Preview() {
java16.parseResource("geometry/Shape.java");
}
@Test
public void sealedClass() {
doTest("geometry/Shape");
}
@Test
public void nonSealedClass() {
doTest("geometry/Square");
}
@Test(expected = ParseException.class)
public void sealedInterfaceBeforeJava15Preview() {
java16.parseResource("expression/Expr.java");
}
@Test
public void sealedInterface() {
doTest("expression/Expr");
}
}

View File

@ -112,4 +112,10 @@ public class Java16TreeDumpTest extends BaseTreeDumpTest {
public void recordIsARestrictedIdentifier() {
java16.parse("public class record {}");
}
@Test
public void sealedAndNonSealedIdentifiers() {
doTest("NonSealedIdentifier");
java16p.parseResource("NonSealedIdentifier.java"); // make sure we can parse it with preview as well
}
}

View File

@ -0,0 +1,15 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
public class NonSealedIdentifier {
public static void main(String[] args) {
int result = 0;
int non = 1;
// sealed is a valid identifier name in both Java16 and Java16 Preview
int sealed = 2;
// non-sealed is a valid subtraction expression in both Java16 and Java16 Preview
result = non-sealed;
System.out.println(result);
}
}

View File

@ -0,0 +1,76 @@
+- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = true]
+- TypeDeclaration[]
+- ClassOrInterfaceDeclaration[@Abstract = false, @BinaryName = "NonSealedIdentifier", @Default = false, @Final = false, @Image = "NonSealedIdentifier", @Interface = false, @Local = false, @Modifiers = 1, @Native = false, @Nested = false, @NonSealed = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = true, @Sealed = false, @SimpleName = "NonSealedIdentifier", @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeKind = TypeKind.CLASS, @Volatile = false]
+- ClassOrInterfaceBody[@AnonymousInnerClass = false, @EnumChild = false]
+- ClassOrInterfaceBodyDeclaration[@AnonymousInnerClass = false, @EnumChild = false, @Kind = DeclarationKind.METHOD]
+- MethodDeclaration[@Abstract = false, @Arity = 1, @Default = false, @Final = false, @InterfaceMember = false, @Kind = MethodLikeKind.METHOD, @MethodName = "main", @Modifiers = 17, @Name = "main", @Native = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = true, @Static = true, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyPublic = true, @Transient = false, @Void = true, @Volatile = false]
+- ResultType[@Void = true, @returnsArray = false]
+- MethodDeclarator[@Image = "main", @ParameterCount = 1]
| +- FormalParameters[@ParameterCount = 1, @Size = 1]
| +- FormalParameter[@Abstract = false, @Array = true, @ArrayDepth = 1, @Default = false, @ExplicitReceiverParameter = false, @Final = false, @Modifiers = 0, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @Varargs = false, @Volatile = false]
| +- Type[@Array = true, @ArrayDepth = 1, @ArrayType = true, @TypeImage = "String"]
| | +- ReferenceType[@Array = true, @ArrayDepth = 1]
| | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = true, @ArrayDepth = 1, @Image = "String", @ReferenceToClassSameCompilationUnit = false]
| +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = true, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @FormalParameter = true, @Image = "args", @LambdaParameter = false, @LocalVariable = false, @Name = "args", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "args"]
+- Block[@containsComment = false]
+- BlockStatement[@Allocation = false]
| +- LocalVariableDeclaration[@Abstract = false, @Array = false, @ArrayDepth = 0, @Default = false, @Final = false, @Modifiers = 0, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @VariableName = "result", @Volatile = false]
| +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "int"]
| | +- PrimitiveType[@Array = false, @ArrayDepth = 0, @Boolean = false, @Image = "int"]
| +- VariableDeclarator[@Initializer = true, @Name = "result"]
| +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @FormalParameter = false, @Image = "result", @LambdaParameter = false, @LocalVariable = true, @Name = "result", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "result"]
| +- VariableInitializer[]
| +- Expression[@StandAlonePrimitive = true]
| +- PrimaryExpression[]
| +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
| +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = "0", @FloatLiteral = false, @Image = "0", @IntLiteral = true, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = "0", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0]
+- BlockStatement[@Allocation = false]
| +- LocalVariableDeclaration[@Abstract = false, @Array = false, @ArrayDepth = 0, @Default = false, @Final = false, @Modifiers = 0, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @VariableName = "non", @Volatile = false]
| +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "int"]
| | +- PrimitiveType[@Array = false, @ArrayDepth = 0, @Boolean = false, @Image = "int"]
| +- VariableDeclarator[@Initializer = true, @Name = "non"]
| +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @FormalParameter = false, @Image = "non", @LambdaParameter = false, @LocalVariable = true, @Name = "non", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "non"]
| +- VariableInitializer[]
| +- Expression[@StandAlonePrimitive = true]
| +- PrimaryExpression[]
| +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
| +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = "1", @FloatLiteral = false, @Image = "1", @IntLiteral = true, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = "1", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 1, @ValueAsLong = 1]
+- BlockStatement[@Allocation = false]
| +- LocalVariableDeclaration[@Abstract = false, @Array = false, @ArrayDepth = 0, @Default = false, @Final = false, @Modifiers = 0, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @VariableName = "sealed", @Volatile = false]
| +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "int"]
| | +- PrimitiveType[@Array = false, @ArrayDepth = 0, @Boolean = false, @Image = "int"]
| +- VariableDeclarator[@Initializer = true, @Name = "sealed"]
| +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @FormalParameter = false, @Image = "sealed", @LambdaParameter = false, @LocalVariable = true, @Name = "sealed", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "sealed"]
| +- VariableInitializer[]
| +- Expression[@StandAlonePrimitive = true]
| +- PrimaryExpression[]
| +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
| +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = "2", @FloatLiteral = false, @Image = "2", @IntLiteral = true, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = "2", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 2, @ValueAsLong = 2]
+- BlockStatement[@Allocation = false]
| +- Statement[]
| +- StatementExpression[]
| +- PrimaryExpression[]
| | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
| | +- Name[@Image = "result"]
| +- AssignmentOperator[@Compound = false, @Image = "="]
| +- Expression[@StandAlonePrimitive = false]
| +- AdditiveExpression[@Image = "-", @Operator = "-"]
| +- PrimaryExpression[]
| | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
| | +- Name[@Image = "non"]
| +- PrimaryExpression[]
| +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
| +- Name[@Image = "sealed"]
+- BlockStatement[@Allocation = false]
+- Statement[]
+- StatementExpression[]
+- PrimaryExpression[]
+- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
| +- Name[@Image = "System.out.println"]
+- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false]
+- Arguments[@ArgumentCount = 1, @Size = 1]
+- ArgumentList[@Size = 1]
+- Expression[@StandAlonePrimitive = false]
+- PrimaryExpression[]
+- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
+- Name[@Image = "result"]

View File

@ -0,0 +1,9 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.expression;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public final class ConstantExpr implements Expr { }

View File

@ -0,0 +1,10 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.expression;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public sealed interface Expr
permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { }

View File

@ -0,0 +1,11 @@
+- CompilationUnit[@PackageName = "com.example.expression", @declarationsAreInDefaultPackage = false]
+- PackageDeclaration[@Name = "com.example.expression", @PackageNameImage = "com.example.expression"]
| +- Name[@Image = "com.example.expression"]
+- TypeDeclaration[]
+- ClassOrInterfaceDeclaration[@Abstract = false, @BinaryName = "com.example.expression.Expr", @Default = false, @Final = false, @Image = "Expr", @Interface = true, @Local = false, @Modifiers = 16385, @Native = false, @Nested = false, @NonSealed = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = true, @Sealed = true, @SimpleName = "Expr", @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeKind = TypeKind.INTERFACE, @Volatile = false]
+- PermitsList[]
| +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "ConstantExpr", @ReferenceToClassSameCompilationUnit = false]
| +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "PlusExpr", @ReferenceToClassSameCompilationUnit = false]
| +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "TimesExpr", @ReferenceToClassSameCompilationUnit = false]
| +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "NegExpr", @ReferenceToClassSameCompilationUnit = false]
+- ClassOrInterfaceBody[@AnonymousInnerClass = false, @EnumChild = false]

View File

@ -0,0 +1,9 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.expression;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public final class NegExpr implements Expr { }

View File

@ -0,0 +1,9 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.expression;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public final class PlusExpr implements Expr { }

View File

@ -0,0 +1,9 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.expression;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public final class TimesExpr implements Expr { }

View File

@ -0,0 +1,9 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.geometry;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public final class Circle extends Shape { }

View File

@ -0,0 +1,10 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.geometry;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public final class FilledRectangle extends Rectangle { }

View File

@ -0,0 +1,11 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.geometry;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public sealed class Rectangle extends Shape
permits TransparentRectangle, FilledRectangle { }

View File

@ -0,0 +1,11 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.geometry;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public sealed class Shape
permits Circle, Rectangle, Square { }

View File

@ -0,0 +1,10 @@
+- CompilationUnit[@PackageName = "com.example.geometry", @declarationsAreInDefaultPackage = false]
+- PackageDeclaration[@Name = "com.example.geometry", @PackageNameImage = "com.example.geometry"]
| +- Name[@Image = "com.example.geometry"]
+- TypeDeclaration[]
+- ClassOrInterfaceDeclaration[@Abstract = false, @BinaryName = "com.example.geometry.Shape", @Default = false, @Final = false, @Image = "Shape", @Interface = false, @Local = false, @Modifiers = 16385, @Native = false, @Nested = false, @NonSealed = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = true, @Sealed = true, @SimpleName = "Shape", @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeKind = TypeKind.CLASS, @Volatile = false]
+- PermitsList[]
| +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "Circle", @ReferenceToClassSameCompilationUnit = false]
| +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "Rectangle", @ReferenceToClassSameCompilationUnit = false]
| +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "Square", @ReferenceToClassSameCompilationUnit = false]
+- ClassOrInterfaceBody[@AnonymousInnerClass = false, @EnumChild = false]

View File

@ -0,0 +1,10 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.geometry;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public non-sealed class Square extends Shape { }

View File

@ -0,0 +1,8 @@
+- CompilationUnit[@PackageName = "com.example.geometry", @declarationsAreInDefaultPackage = false]
+- PackageDeclaration[@Name = "com.example.geometry", @PackageNameImage = "com.example.geometry"]
| +- Name[@Image = "com.example.geometry"]
+- TypeDeclaration[]
+- ClassOrInterfaceDeclaration[@Abstract = false, @BinaryName = "com.example.geometry.Square", @Default = false, @Final = false, @Image = "Square", @Interface = false, @Local = false, @Modifiers = 32769, @Native = false, @Nested = false, @NonSealed = true, @PackagePrivate = false, @Private = false, @Protected = false, @Public = true, @Sealed = false, @SimpleName = "Square", @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeKind = TypeKind.CLASS, @Volatile = false]
+- ExtendsList[]
| +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "Shape", @ReferenceToClassSameCompilationUnit = false]
+- ClassOrInterfaceBody[@AnonymousInnerClass = false, @EnumChild = false]

View File

@ -0,0 +1,10 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.example.geometry;
/**
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
public final class TransparentRectangle extends Rectangle { }