You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2021/08/09 06:50:58 UTC

[groovy] branch master updated (3dcce32 -> 53c5681)

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git.


    from 3dcce32  GROOVY-10148: Groovy should support sealed classes (tweak error message and additional test cases)
     new 4248678  GROOVY-10193: Support sealed type grammar
     new 53c5681  GROOVY-10148: Groovy should support sealed classes (additional edge cases)

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/antlr/GroovyLexer.g4                           |  5 +++
 src/antlr/GroovyParser.g4                          |  7 ++++
 .../apache/groovy/parser/antlr4/AstBuilder.java    | 45 ++++++++++++++++++++++
 .../java/org/codehaus/groovy/ast/ModifierNode.java |  4 ++
 .../transform/NonSealedASTTransformation.java      |  8 ++++
 .../core/SealedTypeDeclaration_01x.groovy}         | 29 +++++++-------
 .../SealedTypeDeclaration_01x.groovy}              |  4 +-
 .../SealedTypeDeclaration_02x.groovy}              |  4 +-
 .../fail/SealedTypeDeclaration_03x.groovy}         |  2 +-
 .../fail/SealedTypeDeclaration_04x.groovy}         |  2 +-
 .../fail/SealedTypeDeclaration_05x.groovy}         |  2 +-
 .../SealedTypeDeclaration_06x.groovy}              |  4 +-
 .../SealedTypeDeclaration_07x.groovy}              |  3 +-
 .../SealedTypeDeclaration_08x.groovy}              |  4 +-
 .../SealedTypeDeclaration_09x.groovy}              |  3 +-
 .../groovy/parser/antlr4/GroovyParserTest.groovy   |  4 ++
 .../groovy/parser/antlr4/SyntaxErrorTest.groovy    | 12 ++++++
 .../console/ui/text/SmartDocumentFilter.java       |  5 ++-
 18 files changed, 113 insertions(+), 34 deletions(-)
 copy src/{main/java/org/codehaus/groovy/runtime/powerassert/PowerAssertionError.java => test-resources/core/SealedTypeDeclaration_01x.groovy} (60%)
 copy src/test-resources/{groovy/bugs/groovy9236/Groovy9236.groovy => fail/SealedTypeDeclaration_01x.groovy} (93%)
 copy src/test-resources/{bugs/BUG-GROOVY-9692.groovy => fail/SealedTypeDeclaration_02x.groovy} (94%)
 copy src/{test/groovy/script/scriptHelloWorld.groovy => test-resources/fail/SealedTypeDeclaration_03x.groovy} (96%)
 copy src/{test/groovy/script/scriptHelloWorld.groovy => test-resources/fail/SealedTypeDeclaration_04x.groovy} (96%)
 copy src/{test/groovy/script/scriptHelloWorld.groovy => test-resources/fail/SealedTypeDeclaration_05x.groovy} (96%)
 copy src/test-resources/{groovy/bugs/groovy9236/Groovy9236.groovy => fail/SealedTypeDeclaration_06x.groovy} (93%)
 copy src/test-resources/{core/Literal_03.groovy => fail/SealedTypeDeclaration_07x.groovy} (92%)
 copy src/test-resources/{groovy/bugs/groovy9236/Groovy9236.groovy => fail/SealedTypeDeclaration_08x.groovy} (93%)
 copy src/test-resources/{core/Literal_03.groovy => fail/SealedTypeDeclaration_09x.groovy} (92%)

[groovy] 01/02: GROOVY-10193: Support sealed type grammar

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 424867821831c9044f3adf740bd5d7cb372ae091
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sat Aug 7 12:50:31 2021 +0800

    GROOVY-10193: Support sealed type grammar
---
 src/antlr/GroovyLexer.g4                           |  5 +++
 src/antlr/GroovyParser.g4                          |  7 ++++
 .../apache/groovy/parser/antlr4/AstBuilder.java    | 45 ++++++++++++++++++++++
 .../java/org/codehaus/groovy/ast/ModifierNode.java |  4 ++
 .../core/SealedTypeDeclaration_01x.groovy          | 36 +++++++++++++++++
 .../fail/SealedTypeDeclaration_01x.groovy          | 21 ++++++++++
 .../fail/SealedTypeDeclaration_02x.groovy          | 20 ++++++++++
 .../fail/SealedTypeDeclaration_03x.groovy          | 21 ++++++++++
 .../fail/SealedTypeDeclaration_04x.groovy          | 21 ++++++++++
 .../fail/SealedTypeDeclaration_05x.groovy          | 21 ++++++++++
 .../fail/SealedTypeDeclaration_06x.groovy          | 21 ++++++++++
 .../fail/SealedTypeDeclaration_07x.groovy          | 21 ++++++++++
 .../fail/SealedTypeDeclaration_08x.groovy          | 21 ++++++++++
 .../fail/SealedTypeDeclaration_09x.groovy          | 21 ++++++++++
 .../groovy/parser/antlr4/GroovyParserTest.groovy   |  4 ++
 .../groovy/parser/antlr4/SyntaxErrorTest.groovy    | 12 ++++++
 .../console/ui/text/SmartDocumentFilter.java       |  5 ++-
 17 files changed, 305 insertions(+), 1 deletion(-)

diff --git a/src/antlr/GroovyLexer.g4 b/src/antlr/GroovyLexer.g4
index 2c58768..6c49387 100644
--- a/src/antlr/GroovyLexer.g4
+++ b/src/antlr/GroovyLexer.g4
@@ -464,12 +464,17 @@ LONG          : 'long';
 
 NATIVE        : 'native';
 NEW           : 'new';
+NON_SEALED    : 'non-sealed';
+
 PACKAGE       : 'package';
+PERMITS       : 'permits';
 PRIVATE       : 'private';
 PROTECTED     : 'protected';
 PUBLIC        : 'public';
 RETURN        : 'return';
 
+SEALED        : 'sealed';
+
 fragment
 SHORT         : 'short';
 
diff --git a/src/antlr/GroovyParser.g4 b/src/antlr/GroovyParser.g4
index 3faf7ad..3f486f7 100644
--- a/src/antlr/GroovyParser.g4
+++ b/src/antlr/GroovyParser.g4
@@ -162,6 +162,8 @@ classOrInterfaceModifier
           |   PRIVATE    // class or interface
           |   STATIC     // class or interface
           |   ABSTRACT   // class or interface
+          |   SEALED     // class or interface
+          |   NON_SEALED // class or interface
           |   FINAL      // class only -- does not apply to interfaces
           |   STRICTFP   // class or interface
           |   DEFAULT    // interface only -- does not apply to classes
@@ -227,6 +229,7 @@ locals[ int t ]
         (nls typeParameters)?
         (nls EXTENDS nls scs=typeList)?
         (nls IMPLEMENTS nls is=typeList)?
+        (nls PERMITS nls ps=typeList)?
         nls classBody[$t]
     ;
 
@@ -1208,6 +1211,7 @@ identifier
     |   TRAIT
     |   AS
     |   YIELD
+    |   PERMITS
     ;
 
 builtInType
@@ -1243,8 +1247,11 @@ keywords
     |   INTERFACE
     |   NATIVE
     |   NEW
+    |   NON_SEALED
     |   PACKAGE
+    |   PERMITS
     |   RETURN
+    |   SEALED
     |   STATIC
     |   STRICTFP
     |   SUPER
diff --git a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
index fa6bd54..ba8287b 100644
--- a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
+++ b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -21,6 +21,8 @@ package org.apache.groovy.parser.antlr4;
 import groovy.lang.Tuple2;
 import groovy.lang.Tuple3;
 import groovy.transform.CompileStatic;
+import groovy.transform.NonSealed;
+import groovy.transform.Sealed;
 import groovy.transform.Trait;
 import org.antlr.v4.runtime.ANTLRErrorListener;
 import org.antlr.v4.runtime.CharStream;
@@ -328,6 +330,7 @@ import static org.apache.groovy.parser.antlr4.GroovyParser.CASE;
 import static org.apache.groovy.parser.antlr4.GroovyParser.DEC;
 import static org.apache.groovy.parser.antlr4.GroovyParser.DEF;
 import static org.apache.groovy.parser.antlr4.GroovyParser.DEFAULT;
+import static org.apache.groovy.parser.antlr4.GroovyParser.FINAL;
 import static org.apache.groovy.parser.antlr4.GroovyParser.GE;
 import static org.apache.groovy.parser.antlr4.GroovyParser.GT;
 import static org.apache.groovy.parser.antlr4.GroovyParser.IN;
@@ -335,6 +338,7 @@ import static org.apache.groovy.parser.antlr4.GroovyParser.INC;
 import static org.apache.groovy.parser.antlr4.GroovyParser.INSTANCEOF;
 import static org.apache.groovy.parser.antlr4.GroovyParser.LE;
 import static org.apache.groovy.parser.antlr4.GroovyParser.LT;
+import static org.apache.groovy.parser.antlr4.GroovyParser.NON_SEALED;
 import static org.apache.groovy.parser.antlr4.GroovyParser.NOT_IN;
 import static org.apache.groovy.parser.antlr4.GroovyParser.NOT_INSTANCEOF;
 import static org.apache.groovy.parser.antlr4.GroovyParser.PRIVATE;
@@ -343,6 +347,7 @@ import static org.apache.groovy.parser.antlr4.GroovyParser.RANGE_EXCLUSIVE_LEFT;
 import static org.apache.groovy.parser.antlr4.GroovyParser.RANGE_EXCLUSIVE_RIGHT;
 import static org.apache.groovy.parser.antlr4.GroovyParser.RANGE_INCLUSIVE;
 import static org.apache.groovy.parser.antlr4.GroovyParser.SAFE_INDEX;
+import static org.apache.groovy.parser.antlr4.GroovyParser.SEALED;
 import static org.apache.groovy.parser.antlr4.GroovyParser.STATIC;
 import static org.apache.groovy.parser.antlr4.GroovyParser.SUB;
 import static org.apache.groovy.parser.antlr4.GroovyParser.VAR;
@@ -352,6 +357,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.closureX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.listX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
@@ -1439,6 +1445,34 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         List<ModifierNode> modifierNodeList = ctx.getNodeMetaData(TYPE_DECLARATION_MODIFIERS);
         Objects.requireNonNull(modifierNodeList, "modifierNodeList should not be null");
         ModifierManager modifierManager = new ModifierManager(this, modifierNodeList);
+
+        Optional<ModifierNode> finalModifierNodeOptional = modifierManager.get(FINAL);
+        Optional<ModifierNode> sealedModifierNodeOptional = modifierManager.get(SEALED);
+        Optional<ModifierNode> nonSealedModifierNodeOptional = modifierManager.get(NON_SEALED);
+        boolean isFinal = finalModifierNodeOptional.isPresent();
+        boolean isSealed = sealedModifierNodeOptional.isPresent();
+        boolean isNonSealed = nonSealedModifierNodeOptional.isPresent();
+        if (isSealed && isNonSealed) {
+            throw createParsingFailedException("type cannot be defined with both `sealed` and `non-sealed`", nonSealedModifierNodeOptional.get());
+        }
+
+        if (isFinal && (isSealed || isNonSealed)) {
+            throw createParsingFailedException("type cannot be defined with both " + (isSealed ? "`sealed`" : "`non-sealed`") + " and `final`", finalModifierNodeOptional.get());
+        }
+
+        if ((isAnnotation || isEnum) && (isSealed || isNonSealed)) {
+            ModifierNode mn = isSealed ? sealedModifierNodeOptional.get() : nonSealedModifierNodeOptional.get();
+            throw createParsingFailedException("modifier `" + mn.getText() + "` is not allowed here", mn);
+        }
+
+        boolean hasPermits = asBoolean(ctx.PERMITS());
+        if (isSealed && !hasPermits) {
+            throw createParsingFailedException("sealed type declaration should have `permits` clause", ctx);
+        }
+        if (isNonSealed && hasPermits) {
+            throw createParsingFailedException("non-sealed type declaration should not have `permits` clause", ctx);
+        }
+
         int modifiers = modifierManager.getClassModifiersOpValue();
 
         boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0);
@@ -1474,6 +1508,17 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         classNode.setGenericsTypes(this.visitTypeParameters(ctx.typeParameters()));
         boolean isInterfaceWithDefaultMethods = (isInterface && this.containsDefaultMethods(ctx));
 
+        if (isSealed) {
+            AnnotationNode sealedAnnotationNode = new AnnotationNode(ClassHelper.makeCached(Sealed.class));
+            ListExpression permittedSubclassesListExpression =
+                    listX(Arrays.stream(this.visitTypeList(ctx.ps))
+                            .map(ClassExpression::new)
+                            .collect(Collectors.toList()));
+            sealedAnnotationNode.setMember("permittedSubclasses", permittedSubclassesListExpression);
+            classNode.addAnnotation(sealedAnnotationNode);
+        } else if (isNonSealed) {
+            classNode.addAnnotation(new AnnotationNode(ClassHelper.makeCached(NonSealed.class)));
+        }
         if (isInterfaceWithDefaultMethods || asBoolean(ctx.TRAIT())) {
             classNode.addAnnotation(new AnnotationNode(ClassHelper.makeCached(Trait.class)));
         }
diff --git a/src/main/java/org/codehaus/groovy/ast/ModifierNode.java b/src/main/java/org/codehaus/groovy/ast/ModifierNode.java
index e8a2f9b..e76e1b4 100644
--- a/src/main/java/org/codehaus/groovy/ast/ModifierNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ModifierNode.java
@@ -29,9 +29,11 @@ import static org.apache.groovy.parser.antlr4.GroovyParser.DEF;
 import static org.apache.groovy.parser.antlr4.GroovyParser.DEFAULT;
 import static org.apache.groovy.parser.antlr4.GroovyParser.FINAL;
 import static org.apache.groovy.parser.antlr4.GroovyParser.NATIVE;
+import static org.apache.groovy.parser.antlr4.GroovyParser.NON_SEALED;
 import static org.apache.groovy.parser.antlr4.GroovyParser.PRIVATE;
 import static org.apache.groovy.parser.antlr4.GroovyParser.PROTECTED;
 import static org.apache.groovy.parser.antlr4.GroovyParser.PUBLIC;
+import static org.apache.groovy.parser.antlr4.GroovyParser.SEALED;
 import static org.apache.groovy.parser.antlr4.GroovyParser.STATIC;
 import static org.apache.groovy.parser.antlr4.GroovyParser.STRICTFP;
 import static org.apache.groovy.parser.antlr4.GroovyParser.SYNCHRONIZED;
@@ -66,6 +68,8 @@ public class ModifierNode extends ASTNode {
             PRIVATE, Opcodes.ACC_PRIVATE,
             STATIC, Opcodes.ACC_STATIC,
             ABSTRACT, Opcodes.ACC_ABSTRACT,
+            SEALED, 0,
+            NON_SEALED, 0,
             FINAL, Opcodes.ACC_FINAL,
             STRICTFP, Opcodes.ACC_STRICT,
             DEFAULT, 0 // no flag for specifying a default method in the JVM spec, hence no ACC_DEFAULT flag in ASM
diff --git a/src/test-resources/core/SealedTypeDeclaration_01x.groovy b/src/test-resources/core/SealedTypeDeclaration_01x.groovy
new file mode 100644
index 0000000..eec62f4
--- /dev/null
+++ b/src/test-resources/core/SealedTypeDeclaration_01x.groovy
@@ -0,0 +1,36 @@
+import groovy.transform.NonSealed
+import groovy.transform.Sealed
+
+/*
+ *  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.
+ */
+
+sealed interface ShapeI permits Circle, Rectangle { }
+final class Circle implements ShapeI { }
+non-sealed class Rectangle implements ShapeI { }
+final class Square extends Rectangle { }
+
+def c = new Circle()
+def r = new Rectangle()
+def s = new Square()
+assert [c, r, s]*.class.name == ['Circle', 'Rectangle', 'Square']
+
+def shapeIAnnotations = ShapeI.class.annotations
+assert 1 == shapeIAnnotations.size()
+Sealed sealedAnnotation = (Sealed) shapeIAnnotations[0]
+assert [Circle.class, Rectangle.class] == sealedAnnotation.permittedSubclasses()
diff --git a/src/test-resources/fail/SealedTypeDeclaration_01x.groovy b/src/test-resources/fail/SealedTypeDeclaration_01x.groovy
new file mode 100644
index 0000000..bd3157f
--- /dev/null
+++ b/src/test-resources/fail/SealedTypeDeclaration_01x.groovy
@@ -0,0 +1,21 @@
+/*
+ *  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.
+ */
+
+sealed @interface ShapeI permits Circle, Rectangle { }
+
diff --git a/src/test-resources/fail/SealedTypeDeclaration_02x.groovy b/src/test-resources/fail/SealedTypeDeclaration_02x.groovy
new file mode 100644
index 0000000..3fab635
--- /dev/null
+++ b/src/test-resources/fail/SealedTypeDeclaration_02x.groovy
@@ -0,0 +1,20 @@
+/*
+ *  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.
+ */
+
+sealed enum ShapeI permits Circle, Rectangle { }
diff --git a/src/test-resources/fail/SealedTypeDeclaration_03x.groovy b/src/test-resources/fail/SealedTypeDeclaration_03x.groovy
new file mode 100644
index 0000000..046b9e7
--- /dev/null
+++ b/src/test-resources/fail/SealedTypeDeclaration_03x.groovy
@@ -0,0 +1,21 @@
+/*
+ *  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.
+ */
+
+non-sealed @interface ShapeI { }
+
diff --git a/src/test-resources/fail/SealedTypeDeclaration_04x.groovy b/src/test-resources/fail/SealedTypeDeclaration_04x.groovy
new file mode 100644
index 0000000..e94fcb4
--- /dev/null
+++ b/src/test-resources/fail/SealedTypeDeclaration_04x.groovy
@@ -0,0 +1,21 @@
+/*
+ *  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.
+ */
+
+non-sealed enum ShapeI { }
+
diff --git a/src/test-resources/fail/SealedTypeDeclaration_05x.groovy b/src/test-resources/fail/SealedTypeDeclaration_05x.groovy
new file mode 100644
index 0000000..83ca693
--- /dev/null
+++ b/src/test-resources/fail/SealedTypeDeclaration_05x.groovy
@@ -0,0 +1,21 @@
+/*
+ *  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.
+ */
+
+sealed interface ShapeI { }
+
diff --git a/src/test-resources/fail/SealedTypeDeclaration_06x.groovy b/src/test-resources/fail/SealedTypeDeclaration_06x.groovy
new file mode 100644
index 0000000..40523da
--- /dev/null
+++ b/src/test-resources/fail/SealedTypeDeclaration_06x.groovy
@@ -0,0 +1,21 @@
+/*
+ *  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.
+ */
+
+non-sealed interface ShapeI permits Circle, Rectangle { }
+
diff --git a/src/test-resources/fail/SealedTypeDeclaration_07x.groovy b/src/test-resources/fail/SealedTypeDeclaration_07x.groovy
new file mode 100644
index 0000000..d08a829
--- /dev/null
+++ b/src/test-resources/fail/SealedTypeDeclaration_07x.groovy
@@ -0,0 +1,21 @@
+/*
+ *  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.
+ */
+
+sealed non-sealed interface ShapeI permits Circle, Rectangle { }
+
diff --git a/src/test-resources/fail/SealedTypeDeclaration_08x.groovy b/src/test-resources/fail/SealedTypeDeclaration_08x.groovy
new file mode 100644
index 0000000..85110eb
--- /dev/null
+++ b/src/test-resources/fail/SealedTypeDeclaration_08x.groovy
@@ -0,0 +1,21 @@
+/*
+ *  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.
+ */
+
+sealed final interface ShapeI permits Circle, Rectangle { }
+
diff --git a/src/test-resources/fail/SealedTypeDeclaration_09x.groovy b/src/test-resources/fail/SealedTypeDeclaration_09x.groovy
new file mode 100644
index 0000000..f8e8e06
--- /dev/null
+++ b/src/test-resources/fail/SealedTypeDeclaration_09x.groovy
@@ -0,0 +1,21 @@
+/*
+ *  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.
+ */
+
+non-sealed final interface ShapeI permits Circle, Rectangle { }
+
diff --git a/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy b/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
index b6d87a7..b2b3e90 100644
--- a/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
+++ b/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
@@ -361,6 +361,10 @@ final class GroovyParserTest extends GroovyTestCase {
         doTest('core/AnnotationDeclaration_01.groovy')
     }
 
+    void "test groovy core - SealedTypeDeclaration"() {
+        doRunAndTestAntlr4('core/SealedTypeDeclaration_01x.groovy')
+    }
+
     void "test groovy core - Command"() {
         doTest('core/Command_01.groovy')
         doTest('core/Command_02.groovy')
diff --git a/src/test/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy b/src/test/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy
index e089e02..90eab45 100644
--- a/src/test/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy
+++ b/src/test/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy
@@ -306,6 +306,18 @@ final class SyntaxErrorTest extends GroovyTestCase {
             |'''.stripMargin()
     }
 
+    void 'test groovy core - SealedTypeDeclaration'() {
+        TestUtils.doRunAndShouldFail('fail/SealedTypeDeclaration_01x.groovy')
+        TestUtils.doRunAndShouldFail('fail/SealedTypeDeclaration_02x.groovy')
+        TestUtils.doRunAndShouldFail('fail/SealedTypeDeclaration_03x.groovy')
+        TestUtils.doRunAndShouldFail('fail/SealedTypeDeclaration_04x.groovy')
+        TestUtils.doRunAndShouldFail('fail/SealedTypeDeclaration_05x.groovy')
+        TestUtils.doRunAndShouldFail('fail/SealedTypeDeclaration_06x.groovy')
+        TestUtils.doRunAndShouldFail('fail/SealedTypeDeclaration_07x.groovy')
+        TestUtils.doRunAndShouldFail('fail/SealedTypeDeclaration_08x.groovy')
+        TestUtils.doRunAndShouldFail('fail/SealedTypeDeclaration_09x.groovy')
+    }
+
     void 'test groovy core - MethodDeclaration'() {
         TestUtils.shouldFail('fail/MethodDeclaration_01.groovy')
         TestUtils.doRunAndShouldFail('fail/MethodDeclaration_02x.groovy')
diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/SmartDocumentFilter.java b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/SmartDocumentFilter.java
index cdf55b2..25c8bab 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/SmartDocumentFilter.java
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/SmartDocumentFilter.java
@@ -83,12 +83,15 @@ import static org.apache.groovy.parser.antlr4.GroovyLexer.IntegerLiteral;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.NATIVE;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.NEW;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.NL;
+import static org.apache.groovy.parser.antlr4.GroovyLexer.NON_SEALED;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.NullLiteral;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.PACKAGE;
+import static org.apache.groovy.parser.antlr4.GroovyLexer.PERMITS;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.PRIVATE;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.PROTECTED;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.PUBLIC;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.RETURN;
+import static org.apache.groovy.parser.antlr4.GroovyLexer.SEALED;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.SEMI;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.STATIC;
 import static org.apache.groovy.parser.antlr4.GroovyLexer.STRICTFP;
@@ -378,7 +381,7 @@ public class SmartDocumentFilter extends DocumentFilter {
         for (int t : Arrays.asList(AS, DEF, IN, TRAIT, THREADSAFE,
                 VAR, BuiltInPrimitiveType, ABSTRACT, ASSERT, BREAK, CASE, CATCH, CLASS, CONST, CONTINUE, DEFAULT, DO,
                 ELSE, ENUM, EXTENDS, FINAL, FINALLY, FOR, IF, GOTO, IMPLEMENTS, IMPORT, INSTANCEOF, INTERFACE,
-                NATIVE, NEW, PACKAGE, PRIVATE, PROTECTED, PUBLIC, RETURN, STATIC, STRICTFP, SUPER, SWITCH, SYNCHRONIZED,
+                NATIVE, NEW, NON_SEALED, PACKAGE, PERMITS, PRIVATE, PROTECTED, PUBLIC, RETURN, SEALED, STATIC, STRICTFP, SUPER, SWITCH, SYNCHRONIZED,
                 THIS, THROW, THROWS, TRANSIENT, TRY, VOID, VOLATILE, WHILE, YIELD, NullLiteral, BooleanLiteral)) {
             Style style = createDefaultStyleByTokenType(t);
             StyleConstants.setBold(style, true);

[groovy] 02/02: GROOVY-10148: Groovy should support sealed classes (additional edge cases)

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 53c568145b646fb0463e2b0979dce91d8c5cd1dc
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon Aug 9 16:50:53 2021 +1000

    GROOVY-10148: Groovy should support sealed classes (additional edge cases)
---
 .../org/codehaus/groovy/transform/NonSealedASTTransformation.java | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/main/java/org/codehaus/groovy/transform/NonSealedASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/NonSealedASTTransformation.java
index 07aefe9..f633232 100644
--- a/src/main/java/org/codehaus/groovy/transform/NonSealedASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/NonSealedASTTransformation.java
@@ -43,6 +43,14 @@ public class NonSealedASTTransformation extends AbstractASTTransformation {
 
         if (parent instanceof ClassNode) {
             ClassNode cNode = (ClassNode) parent;
+            if (cNode.isEnum()) {
+                addError("@" + NON_SEALED_CLASS.getSimpleName() + " not allowed for enum", cNode);
+                return;
+            }
+            if (cNode.isAnnotationDefinition()) {
+                addError("@" + NON_SEALED_CLASS.getSimpleName() + " not allowed for annotation definition", cNode);
+                return;
+            }
             cNode.putNodeMetaData(NON_SEALED_CLASS, Boolean.TRUE);
         }
     }