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 { }
+