diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index a5756cad3e..7b5e981fd9 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -1,4 +1,7 @@ /** + * Promote "JEP 409: Sealed Classes" as permanent language feature with Java 17. + * Andreas Dangel 07/2021 + *==================================================================== * Fix #3117 - infinite loop when parsing invalid code nested in lambdas * Andreas Dangel 03/2021 *==================================================================== @@ -446,11 +449,11 @@ public class JavaParser { if ((jdkVersion == 15 && preview || jdkVersion >= 16) && "record".equals(image)) { throwParseException("With JDK 15 Preview and Java >= 16, 'record' is a contextual keyword 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 || jdkVersion == 16 && preview || jdkVersion >= 17) && "sealed".equals(image)) { + throwParseException("With JDK 15 Preview and JDK 16 Preview and JDK >= 17, 'sealed' is a contextual keyword 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!"); + if ((jdkVersion == 15 && preview || jdkVersion == 16 && preview || jdkVersion >= 17) && "permits".equals(image)) { + throwParseException("With JDK 15 Preview and JDK 16 Preview and JDK >= 17, 'permits' is a contextual keyword and cannot be used for type declarations!"); } } private void checkForMultipleCaseLabels() { @@ -514,12 +517,12 @@ public class JavaParser { } private boolean isSealedClassSupported() { - return jdkVersion == 15 && preview || jdkVersion == 16 && preview; + return jdkVersion == 15 && preview || jdkVersion == 16 && preview || jdkVersion >= 17; } private void checkForSealedClassUsage() { if (!isSealedClassSupported()) { - throwParseException("Sealed Classes are only supported with Java 15 Preview"); + throwParseException("Sealed Classes are only supported with JDK 15 Preview, JDK 16 Preview and JDK >= 17."); } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java index ca809cc0e7..a6b9cbe848 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java @@ -18,7 +18,7 @@ import net.sourceforge.pmd.lang.java.JavaParsingHelper; public class Java16TreeDumpTest extends BaseTreeDumpTest { private final JavaParsingHelper java16 = JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("16") - .withResourceContext(Java15TreeDumpTest.class, "jdkversiontests/java16/"); + .withResourceContext(Java16TreeDumpTest.class, "jdkversiontests/java16/"); private final JavaParsingHelper java16p = java16.withDefaultVersion("16-preview"); private final JavaParsingHelper java15 = java16.withDefaultVersion("15"); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17TreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17TreeDumpTest.java new file mode 100644 index 0000000000..53024e60a1 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17TreeDumpTest.java @@ -0,0 +1,59 @@ +/* + * 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 Java17TreeDumpTest extends BaseTreeDumpTest { + private final JavaParsingHelper java17 = + JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("17") + .withResourceContext(Java17TreeDumpTest.class, "jdkversiontests/java17/"); + private final JavaParsingHelper java17p = java17.withDefaultVersion("17-preview"); + private final JavaParsingHelper java16 = java17.withDefaultVersion("16"); + + public Java17TreeDumpTest() { + super(new RelevantAttributePrinter(), ".java"); + } + + @Override + public BaseParsingHelper getParser() { + return java17; + } + + @Test(expected = ParseException.class) + public void sealedClassBeforeJava17() { + java16.parseResource("geometry/Shape.java"); + } + + @Test + public void sealedClass() { + doTest("geometry/Shape"); + java17p.parseResource("geometry/Shape.java"); // make sure we can parse it with preview as well + } + + @Test + public void nonSealedClass() { + doTest("geometry/Square"); + java17p.parseResource("geometry/Square.java"); // make sure we can parse it with preview as well + } + + @Test(expected = ParseException.class) + public void sealedInterfaceBeforeJava17() { + java16.parseResource("expression/Expr.java"); + } + + @Test + public void sealedInterface() { + doTest("expression/Expr"); + java17p.parseResource("expression/Expr.java"); // make sure we can parse it with preview as well + } + +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/ConstantExpr.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/ConstantExpr.java new file mode 100644 index 0000000000..22d335c466 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/ConstantExpr.java @@ -0,0 +1,9 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.expression; + +/** + * @see JEP 409: Sealed Classes + */ +public final class ConstantExpr implements Expr { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.java new file mode 100644 index 0000000000..5be7b03c0a --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.java @@ -0,0 +1,10 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.expression; + +/** + * @see JEP 409: Sealed Classes + */ +public sealed interface Expr + permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.txt new file mode 100644 index 0000000000..12d87ee160 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.txt @@ -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] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/NegExpr.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/NegExpr.java new file mode 100644 index 0000000000..416645a6a1 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/NegExpr.java @@ -0,0 +1,9 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.expression; + +/** + * @see JEP 409: Sealed Classes + */ +public final class NegExpr implements Expr { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/PlusExpr.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/PlusExpr.java new file mode 100644 index 0000000000..13b2cbb8fb --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/PlusExpr.java @@ -0,0 +1,9 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.expression; + +/** + * @see JEP 409: Sealed Classes + */ +public final class PlusExpr implements Expr { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/TimesExpr.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/TimesExpr.java new file mode 100644 index 0000000000..cdde6fa214 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/TimesExpr.java @@ -0,0 +1,9 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.expression; + +/** + * @see JEP 409: Sealed Classes + */ +public final class TimesExpr implements Expr { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Circle.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Circle.java new file mode 100644 index 0000000000..feca615807 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Circle.java @@ -0,0 +1,9 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.geometry; + +/** + * @see JEP 409: Sealed Classes + */ +public final class Circle extends Shape { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/FilledRectangle.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/FilledRectangle.java new file mode 100644 index 0000000000..198ce630d4 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/FilledRectangle.java @@ -0,0 +1,10 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.geometry; + +/** + * @see JEP 409: Sealed Classes + */ +public final class FilledRectangle extends Rectangle { } + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Rectangle.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Rectangle.java new file mode 100644 index 0000000000..470c26d436 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Rectangle.java @@ -0,0 +1,11 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.geometry; + +/** + * @see JEP 409: Sealed Classes + */ +public sealed class Rectangle extends Shape + permits TransparentRectangle, FilledRectangle { } + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.java new file mode 100644 index 0000000000..c6687dc3f6 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.java @@ -0,0 +1,11 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.geometry; + +/** + * @see JEP 409: Sealed Classes + */ +public sealed class Shape + permits Circle, Rectangle, Square { } + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.txt new file mode 100644 index 0000000000..2a2f3f01fc --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.txt @@ -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] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.java new file mode 100644 index 0000000000..036d8d00e7 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.java @@ -0,0 +1,10 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.geometry; + +/** + * @see JEP 409: Sealed Classes + */ +public non-sealed class Square extends Shape { } + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.txt new file mode 100644 index 0000000000..94cd4275e2 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.txt @@ -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] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/TransparentRectangle.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/TransparentRectangle.java new file mode 100644 index 0000000000..420ef3e792 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/TransparentRectangle.java @@ -0,0 +1,10 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.example.geometry; + +/** + * @see JEP 409: Sealed Classes + */ +public final class TransparentRectangle extends Rectangle { } +