This is an automated email from the ASF dual-hosted git repository. blackdrag pushed a commit to branch feature/GROOVY-8299/default_methods in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 796985f78ebd562134a761af2772c60cca2e7632 Author: Jochen Theodorou <blackd...@gmx.org> AuthorDate: Mon Sep 11 14:38:30 2023 +0200 GROOVY-8299: implement interfaces with default methods (initial commit) --- .../apache/groovy/parser/antlr4/AstBuilder.java | 10 +--- .../groovy/classgen/ClassCompletionVerifier.java | 2 +- .../groovy/lang/DefaultInterfaceMethodsTest.groovy | 54 ++++++++++++++++++++++ 3 files changed, 57 insertions(+), 9 deletions(-) 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 62c08cd53c..add2787794 100644 --- a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java +++ b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java @@ -1313,9 +1313,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> { } else if (isNonSealed) { classNode.addAnnotation(makeAnnotationNode(NonSealed.class)); } - if (isInterfaceWithDefaultMethods || asBoolean(ctx.TRAIT())) { - classNode.addAnnotation(makeAnnotationNode(Trait.class)); - } + classNode.addAnnotations(modifierManager.getAnnotations()); if (isRecord && classNode.getAnnotations().stream().noneMatch(a -> a.getClassNode().getName().equals(RECORD_TYPE_NAME))) { @@ -1338,10 +1336,6 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> { classNode.setInterfaces(this.visitTypeList(ctx.is)); this.initUsingGenerics(classNode); - } else if (isInterfaceWithDefaultMethods) { // GROOVY-9259 - classNode.setInterfaces(this.visitTypeList(ctx.scs)); - this.initUsingGenerics(classNode); - } else if (isInterface) { classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT); classNode.setInterfaces(this.visitTypeList(ctx.scs)); @@ -1877,7 +1871,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> { } - modifiers |= !modifierManager.containsAny(STATIC) && (classNode.isInterface() || (isTrue(classNode, IS_INTERFACE_WITH_DEFAULT_METHODS) && !modifierManager.containsAny(DEFAULT))) ? Opcodes.ACC_ABSTRACT : 0; + modifiers |= !modifierManager.containsAny(STATIC) && classNode.isInterface() && !(isTrue(classNode, IS_INTERFACE_WITH_DEFAULT_METHODS) && modifierManager.containsAny(DEFAULT)) ? Opcodes.ACC_ABSTRACT : 0; MethodNode methodNode = new MethodNode(methodName, modifiers, returnType, parameters, exceptions, code); classNode.addMethod(methodNode); diff --git a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java index 8ff9c1ef00..43abd86f23 100644 --- a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java +++ b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java @@ -310,7 +310,7 @@ public class ClassCompletionVerifier extends ClassCodeVisitorSupport { } private void checkAbstractDeclaration(final MethodNode methodNode) { - if (!methodNode.isAbstract() || currentClass.isAbstract()) return; + if (!methodNode.isAbstract() || currentClass.isAbstract() || methodNode.isDefault()) return; addError("Can't have an abstract method in a non-abstract class." + " The " + getDescription(currentClass) + " must be declared abstract or the method '" + diff --git a/src/test/groovy/lang/DefaultInterfaceMethodsTest.groovy b/src/test/groovy/lang/DefaultInterfaceMethodsTest.groovy new file mode 100644 index 0000000000..bc833b8069 --- /dev/null +++ b/src/test/groovy/lang/DefaultInterfaceMethodsTest.groovy @@ -0,0 +1,54 @@ +/* + * 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 groovy.lang + +import groovy.test.GroovyTestCase + +/** + * Test for the BenchmarkInterceptor + */ +class DefaultInterfaceMethodsTest extends GroovyTestCase { + + void testSimpleDeclarationAndCall() { + assertScript ''' + public interface InterfaceWithDefault { + default String hello() { + return 'Hello' + } + } + class UseDefault implements InterfaceWithDefault {} + assert new UseDefault().hello() == 'Hello' + ''' + } + void testSimpleDeclarationAndOverride() { + assertScript ''' + public interface InterfaceWithDefault { + default String hello() { + return 'Hello' + } + } + class OverrideDefault implements InterfaceWithDefault { + public String hello() { + return 'Bon jour' + } + } + assert new OverrideDefault().hello() == 'Bon jour' + ''' + } +}