This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
new be6a0d7fba GROOVY-11292, GROOVY-11750: non-sealed super class
be6a0d7fba is described below
commit be6a0d7fbaed722889b2160c3168a305ffaa17c0
Author: Eric Milles <[email protected]>
AuthorDate: Tue Sep 2 11:26:11 2025 -0500
GROOVY-11292, GROOVY-11750: non-sealed super class
4_0_X backport
---
.github/workflows/groovy-build-test-ea.yml | 6 +-
.github/workflows/groovy-build-test.yml | 2 +-
.../groovy/classgen/ClassCompletionVerifier.java | 12 +--
src/test/groovy/bugs/Groovy11292.groovy | 90 ++++++++++++++++++++++
.../groovy/groovy/BreakContinueLabelTest.groovy | 4 +-
5 files changed, 102 insertions(+), 12 deletions(-)
diff --git a/.github/workflows/groovy-build-test-ea.yml
b/.github/workflows/groovy-build-test-ea.yml
index 4a70f0a0d9..49d0d7f309 100644
--- a/.github/workflows/groovy-build-test-ea.yml
+++ b/.github/workflows/groovy-build-test-ea.yml
@@ -26,10 +26,8 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
- java: [17]
# The jdk links of "install-jdk.sh" are sometimes outdated, so we have
to download openjdk releases from https://jdk.java.net/ by ourselves.
- jdk:
["https://download.java.net/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_linux-x64_bin.tar.gz",
-
"https://download.java.net/java/GA/jdk21/fd2272bbf8e04c3dbaee13770090416c/35/GPL/openjdk-21_linux-x64_bin.tar.gz"]
+ jdk:
['https://download.java.net/java/early_access/jdk25/15/GPL/openjdk-25-ea+15_linux-x64_bin.tar.gz']
runs-on: ${{ matrix.os }}
env:
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
@@ -43,8 +41,8 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'zulu'
- java-version: ${{ matrix.java }}
check-latest: true
+ java-version: 17
- uses: gradle/gradle-build-action@v2
- name: Test with Gradle
run: ./gradlew test -Ptarget.java.home=/home/runner/openjdk
diff --git a/.github/workflows/groovy-build-test.yml
b/.github/workflows/groovy-build-test.yml
index 4fed3ba4ef..72cb68a5b5 100644
--- a/.github/workflows/groovy-build-test.yml
+++ b/.github/workflows/groovy-build-test.yml
@@ -53,7 +53,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- java: [9, 10, 12, 13, 14, 15, 16, 18, 19, 20, 22, 23]
+ java: [9, 10, 12, 13, 14, 15, 16, 18, 19, 20, 22, 23, 24]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
diff --git
a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
index f749f96711..bbdd5a7add 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
@@ -18,8 +18,6 @@
*/
package org.codehaus.groovy.classgen;
-import groovy.transform.NonSealed;
-import groovy.transform.Sealed;
import org.apache.groovy.ast.tools.AnnotatedNodeUtils;
import org.apache.groovy.ast.tools.ClassNodeUtils;
import org.codehaus.groovy.ast.ASTNode;
@@ -317,7 +315,7 @@ public class ClassCompletionVerifier extends
ClassCodeVisitorSupport {
}
private void checkClassForExtendingFinalOrSealed(final ClassNode cn) {
- boolean sealed = Boolean.TRUE.equals(cn.getNodeMetaData(Sealed.class));
+ boolean sealed =
Boolean.TRUE.equals(cn.getNodeMetaData(groovy.transform.Sealed.class));
if (sealed && cn.getPermittedSubclasses().isEmpty()) {
addError("Sealed " + getDescription(cn) + " has no explicit or
implicit permitted subclasses.", cn);
return;
@@ -362,8 +360,12 @@ public class ClassCompletionVerifier extends
ClassCodeVisitorSupport {
addError("You are not allowed to extend the final " +
getDescription(superCN) + ".", cn);
}
- private boolean nonSealed(final ClassNode node) {
- return Boolean.TRUE.equals(node.getNodeMetaData(NonSealed.class));
+ private boolean nonSealed(final ClassNode cn) {
+ if
(Boolean.TRUE.equals(cn.getNodeMetaData(groovy.transform.NonSealed.class))) {
+ return true;
+ }
+ ClassNode sc = cn.getSuperClass(); // GROOVY-11292, GROOVY-11750:
check super class
+ return (sc != null && sc.isSealed() && !(cn.isSealed() ||
isFinal(cn.getModifiers())));
}
private void checkSealedParent(final ClassNode cn, final ClassNode parent)
{
diff --git a/src/test/groovy/bugs/Groovy11292.groovy
b/src/test/groovy/bugs/Groovy11292.groovy
new file mode 100644
index 0000000000..07c167b1d8
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy11292.groovy
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package bugs
+
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.isAtLeastJdk
+import static org.junit.Assume.assumeTrue
+
+final class Groovy11292 {
+
+ @Test
+ void testClassWithNonSealedParent1() {
+ assumeTrue(isAtLeastJdk('17.0'))
+
+ def config = new CompilerConfiguration(
+ targetDirectory: File.createTempDir(),
+ jointCompilationOptions: [memStub: true]
+ )
+
+ def parentDir = File.createTempDir()
+ try {
+ def a = new File(parentDir, 'A.java')
+ a.write '''
+ public sealed class A permits B {}
+ '''
+ def b = new File(parentDir, 'B.java')
+ b.write '''
+ public non-sealed class B extends A {}
+ '''
+ def c = new File(parentDir, 'C.groovy')
+ c.write '''
+ class C extends B {}
+ '''
+ def d = new File(parentDir, 'D.groovy')
+ d.write '''
+ class D extends C {}
+ '''
+ def e = new File(parentDir, 'E.groovy')
+ e.write '''
+ class E extends B {}
+ '''
+ def f = new File(parentDir, 'F.groovy')
+ f.write '''
+ class F extends E {}
+ '''
+
+ def loader = new GroovyClassLoader(this.class.classLoader)
+ def cu = new JavaAwareCompilationUnit(config, loader)
+ cu.addSources(a, b, c, d, e, f)
+ cu.compile()
+ } finally {
+ config.targetDirectory.deleteDir()
+ parentDir.deleteDir()
+ }
+ }
+
+ @Test
+ void testClassWithNonSealedParent2() {
+ assertScript '''import java.lang.ref.SoftReference // non-sealed type
+
+ class TestReference<T> extends SoftReference<T> {
+ TestReference(T referent) {
+ super(referent)
+ }
+ }
+
+ assert new TestReference(null)
+ '''
+ }
+}
diff --git a/src/test/groovy/groovy/BreakContinueLabelTest.groovy
b/src/test/groovy/groovy/BreakContinueLabelTest.groovy
index 703fb7ffdb..f500cfd313 100644
--- a/src/test/groovy/groovy/BreakContinueLabelTest.groovy
+++ b/src/test/groovy/groovy/BreakContinueLabelTest.groovy
@@ -18,9 +18,9 @@
*/
package groovy
-import org.junit.jupiter.api.Test
+import org.junit.Test
-import static org.junit.jupiter.api.Assertions.fail
+import static org.junit.Assert.fail
final class BreakContinueLabelTest {