Skip to content

Commit

Permalink
GROOVY-11560: Invalid compiler error for class which overrides a meth…
Browse files Browse the repository at this point in the history
…od having duplicate default definitions from interfaces
  • Loading branch information
paulk-asert authored and eric-milles committed Jan 27, 2025
1 parent 9acdef1 commit 7671c2f
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/main/java/org/codehaus/groovy/classgen/Verifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -281,11 +283,18 @@ private static void checkForDuplicateDefaultMethods(ClassNode node) {
if (node.getInterfaces().length < 2) return;

Map<String, MethodNode> defaultMethods = new HashMap<>(8);
Set<String> 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);
Expand All @@ -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);
}
});
}
Expand Down
32 changes: 32 additions & 0 deletions src/test/groovy/bugs/Groovy10381.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -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 '''
Expand Down

0 comments on commit 7671c2f

Please sign in to comment.