From e87e909ac57437205ca607fa095e412c678f8a84 Mon Sep 17 00:00:00 2001 From: Paul King Date: Sat, 25 Jan 2025 14:55:21 +1000 Subject: [PATCH] GROOVY-11560: Invalid compiler error for class which overrides a method having duplicate default definitions from interfaces --- .../codehaus/groovy/classgen/Verifier.java | 11 ++++++- src/test/groovy/bugs/Groovy10381.groovy | 32 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java b/src/main/java/org/codehaus/groovy/classgen/Verifier.java index 6f2c3d338e5..1f766ce3711 100644 --- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java +++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java @@ -28,6 +28,7 @@ import groovy.transform.Sealed; import groovy.transform.stc.POJO; import org.apache.groovy.ast.tools.ClassNodeUtils; +import org.apache.groovy.ast.tools.MethodNodeUtils; import org.apache.groovy.util.BeanUtils; import org.codehaus.groovy.GroovyBugError; import org.codehaus.groovy.ast.ASTNode; @@ -85,6 +86,7 @@ import java.util.ListIterator; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import static java.lang.reflect.Modifier.isFinal; import static java.lang.reflect.Modifier.isPrivate; @@ -281,11 +283,18 @@ private static void checkForDuplicateDefaultMethods(ClassNode node) { if (node.getInterfaces().length < 2) return; Map defaultMethods = new HashMap<>(8); + Set declared = node.getAllDeclaredMethods().stream() + .filter(m -> !m.isDefault()) + .map(MethodNodeUtils::methodDescriptorWithoutReturnType) + .collect(Collectors.toSet()); node.getAllInterfaces().stream() .flatMap(i -> i.getAllDeclaredMethods().stream()) .filter(MethodNode::isDefault) .forEach(m -> { String signature = methodDescriptorWithoutReturnType(m); + if (declared.contains(signature)) { + return; + } MethodNode existing = defaultMethods.get(signature); if (existing == null) { defaultMethods.put(signature, m); @@ -301,7 +310,7 @@ private static void checkForDuplicateDefaultMethods(ClassNode node) { (node.isInterface() ? "interface" : "class") + " " + node.getName() + " inherits unrelated defaults for " + m.getTypeDescriptor() + " from types " + existingDeclaringClass.getName() - + " and " + currentDeclaringClass.getName(), sourceOf(m)); + + " and " + currentDeclaringClass.getName(), node); } }); } diff --git a/src/test/groovy/bugs/Groovy10381.groovy b/src/test/groovy/bugs/Groovy10381.groovy index a49867a722b..568bb9b87c3 100644 --- a/src/test/groovy/bugs/Groovy10381.groovy +++ b/src/test/groovy/bugs/Groovy10381.groovy @@ -22,9 +22,41 @@ 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.shouldFail final class Groovy10381 { + @Test + void testDuplicateDefaultMethodsFromGroovyClasses_implements0() { + assertScript ''' + interface A { + default String m(String n) { n.toLowerCase() } + } + + interface B { + default String m(String n) { n.toUpperCase() } + } + + class C1 implements A, B { + @Override + String m(String n) { A.super.m(n) } + } + assert new C1().m('Hi') == 'hi' + + class C2 implements A, B { + @Override + String m(String n) { B.super.m(n) } + } + assert new C2().m('Hi') == 'HI' + + class C3 implements A, B { + @Override + String m(String n) { 'overridden' } + } + assert new C3().m('Hi') == 'overridden' + ''' + } + @Test void testDuplicateDefaultMethodsFromGroovyClasses_implements1() { def err = shouldFail '''