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 dbcb40562d GROOVY-11758, GROOVY-11830: non-public super class method 
shadowing
dbcb40562d is described below

commit dbcb40562d7c4dce30172bc5f0bc8f64db10589a
Author: Eric Milles <[email protected]>
AuthorDate: Sun Jan 4 18:26:00 2026 -0600

    GROOVY-11758, GROOVY-11830: non-public super class method shadowing
    
    4_0_x backport
---
 .../groovy/classgen/ClassCompletionVerifier.java   |  97 +++++++-----
 src/test/groovy/groovy/InterfaceTest.groovy        | 164 +++++++++++++++++++--
 src/test/groovy/groovy/OverrideTest.groovy         | 148 +++++++------------
 3 files changed, 272 insertions(+), 137 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java 
b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
index a6745708d3..f68e2523c7 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
@@ -412,9 +412,71 @@ public class ClassCompletionVerifier extends 
ClassCodeVisitorSupport {
     }
 
     private void checkMethodsForWeakerAccess(final ClassNode cn) {
-        for (MethodNode method : cn.getMethods()) {
-            checkMethodForWeakerAccessPrivileges(method, cn);
+        for (MethodNode cnMethod : cn.getMethods()) {
+            if (!cnMethod.isPublic() && !cnMethod.isStatic()) {
+            sc: for (MethodNode scMethod : 
cn.getSuperClass().getMethods(cnMethod.getName())) {
+                    if (!scMethod.isStatic()
+                            && !scMethod.isPrivate()
+                            && (cnMethod.isPrivate()
+                            || (cnMethod.isProtected() && scMethod.isPublic())
+                            || (cnMethod.isPackageScope() && 
(scMethod.isPublic() || scMethod.isProtected())))) {
+                        if 
(ParameterUtils.parametersEqual(cnMethod.getParameters(), 
scMethod.getParameters())) {
+                            addWeakerAccessError(cn, cnMethod, scMethod);
+                            break sc;
+                        }
+                    }
+                }
+            }
         }
+
+        // Verifier: checks weaker access of cn's methods against abstract or 
default interface methods
+
+        // GROOVY-11758, GROOVY-11830: check for non-public super class method 
that hides public method
+        Map<String, MethodNode> interfaceMethods = 
ClassNodeUtils.getDeclaredMethodsFromInterfaces(cn);
+        if (!interfaceMethods.isEmpty()) {
+            for (MethodNode cnMethod : cn.getMethods()) {
+                if (!cnMethod.isPrivate() && !cnMethod.isStatic()) {
+                    interfaceMethods.remove(cnMethod.getTypeDescriptor());
+                }
+            }
+            for (MethodNode publicMethod : interfaceMethods.values()) {
+                for (MethodNode scMethod : 
cn.getSuperClass().getMethods(publicMethod.getName())) {
+                    if (!scMethod.isPublic() && !scMethod.isStatic() && 
(!scMethod.isPrivate() || !publicMethod.isDefault())
+                                && 
ParameterUtils.parametersEqual(scMethod.getParameters(), 
publicMethod.getParameters())) {
+                        addWeakerAccessError2(cn, scMethod, publicMethod);
+                    }
+                }
+            }
+        }
+    }
+
+    private void addWeakerAccessError(final ClassNode cn, final MethodNode 
cnMethod, final MethodNode scMethod) {
+        StringBuilder msg = new StringBuilder();
+        msg.append(cnMethod.getName());
+        appendParamsDescription(cnMethod.getParameters(), msg);
+        msg.append(" in ");
+        msg.append(cn.getName());
+        msg.append(" cannot override ");
+        msg.append(scMethod.getName());
+        msg.append(" in ");
+        msg.append(scMethod.getDeclaringClass().getName());
+        msg.append("; attempting to assign weaker access privileges; was ");
+        msg.append(scMethod.isPublic() ? "public" : (scMethod.isProtected() ? 
"protected" : "package-private"));
+
+        addError(msg.toString(), cnMethod);
+    }
+
+    private void addWeakerAccessError2(final ClassNode cn, final MethodNode 
scMethod, final MethodNode ifMethod) {
+        StringBuilder msg = new StringBuilder();
+        msg.append(scMethod.isPrivate() ? "private" : (scMethod.isProtected() 
? "protected" : "package-private"));
+        msg.append(" method ");
+        msg.append(scMethod.getName());
+        appendParamsDescription(scMethod.getParameters(), msg);
+        msg.append(" from ");
+        msg.append(scMethod.getDeclaringClass().getName());
+        msg.append(" cannot shadow the public method in ");
+        msg.append(ifMethod.getDeclaringClass().getName());
+        addError(msg.toString(), cn);
     }
 
     private void checkMethodsForOverridingFinal(final ClassNode cn) {
@@ -459,22 +521,6 @@ public class ClassCompletionVerifier extends 
ClassCodeVisitorSupport {
         msg.append(')');
     }
 
-    private void addWeakerAccessError(final ClassNode cn, final MethodNode 
method, final Parameter[] parameters, final MethodNode superMethod) {
-        StringBuilder msg = new StringBuilder();
-        msg.append(method.getName());
-        appendParamsDescription(parameters, msg);
-        msg.append(" in ");
-        msg.append(cn.getName());
-        msg.append(" cannot override ");
-        msg.append(superMethod.getName());
-        msg.append(" in ");
-        msg.append(superMethod.getDeclaringClass().getName());
-        msg.append("; attempting to assign weaker access privileges; was ");
-        msg.append(superMethod.isPublic() ? "public" : 
(superMethod.isProtected() ? "protected" : "package-private"));
-
-        addError(msg.toString(), method);
-    }
-
     @Override
     public void visitMethod(final MethodNode node) {
         inConstructor = false;
@@ -514,21 +560,6 @@ public class ClassCompletionVerifier extends 
ClassCodeVisitorSupport {
         }
     }
 
-    private void checkMethodForWeakerAccessPrivileges(final MethodNode mn, 
final ClassNode cn) {
-        if (mn.isPublic()) return;
-        Parameter[] params = mn.getParameters();
-        for (MethodNode superMethod : 
cn.getSuperClass().getMethods(mn.getName())) {
-            Parameter[] superParams = superMethod.getParameters();
-            if (!ParameterUtils.parametersEqual(params, superParams)) continue;
-            if ((mn.isPrivate() && !superMethod.isPrivate())
-                    || (mn.isProtected() && !superMethod.isProtected() && 
!superMethod.isPackageScope() && !superMethod.isPrivate())
-                    || (!mn.isPrivate() && !mn.isProtected() && !mn.isPublic() 
&& (superMethod.isPublic() || superMethod.isProtected()))) {
-                addWeakerAccessError(cn, mn, params, superMethod);
-                return;
-            }
-        }
-    }
-
     private void checkOverloadingPrivateAndPublic(final MethodNode node) {
         if (node.isStaticConstructor()) return;
         boolean hasPrivate = node.isPrivate();
diff --git a/src/test/groovy/groovy/InterfaceTest.groovy 
b/src/test/groovy/groovy/InterfaceTest.groovy
index 50afb49569..d52cf36319 100644
--- a/src/test/groovy/groovy/InterfaceTest.groovy
+++ b/src/test/groovy/groovy/InterfaceTest.groovy
@@ -18,42 +18,186 @@
  */
 package groovy
 
-import gls.CompilableTestSupport
+import groovy.test.NotYetImplemented
+import org.junit.Test
 
-final class InterfaceTest extends CompilableTestSupport {
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
 
-    void testGenericsInInterfaceMembers() {
-        // control
-        shouldCompile '''
+final class InterfaceTest {
+
+    @Test
+    void testGenericsInInterfaceMembers1() {
+        assertScript '''
             interface I {
                 def <T>                      T m1(T x)
                 def <U extends CharSequence> U m2(U x)
                 def <V, W>                   V m3(W x)
                 def <N extends Number>    void m4(   )
             }
+
+            print 'works'
+        '''
+    }
+
+    @Test
+    void testGenericsInInterfaceMembers2() {
+        shouldFail '''
+            interface I {
+                def <?> m(x)
+            }
+        '''
+    }
+
+    @Test
+    void testGenericsInInterfaceMembers3() {
+        shouldFail '''
+            interface I {
+                def <? extends CharSequence> m(x)
+            }
         '''
-        // erroneous
-        shouldNotCompile 'interface I { def <?> m(x) }'
-        shouldNotCompile 'interface I { def <? extends CharSequence> m(x) }'
     }
 
     // GROOVY-5106
+    @Test
     void testReImplementsInterface1() {
         def err = shouldFail '''
             interface I<T> {}
             interface J<T> extends I<T> {}
             class X implements I<String>, J<Number> {}
         '''
-        assert err.contains('The interface I cannot be implemented more than 
once with different arguments: I<java.lang.String> and I<java.lang.Number>')
+        assert err.message.contains('The interface I cannot be implemented 
more than once with different arguments: I<java.lang.String> and 
I<java.lang.Number>')
     }
 
     // GROOVY-5106
+    @Test
     void testReImplementsInterface2() {
         def err = shouldFail '''
             interface I<T> {}
             class X implements I<Number> {}
             class Y extends X implements I<String> {}
         '''
-        assert err.contains('The interface I cannot be implemented more than 
once with different arguments: I<java.lang.String> and I<java.lang.Number>')
+        assert err.message.contains('The interface I cannot be implemented 
more than once with different arguments: I<java.lang.String> and 
I<java.lang.Number>')
+    }
+
+    // GROOVY-11707
+    @NotYetImplemented @Test
+    void testReImplementsInterface3() {
+        assertScript '''
+            abstract class A implements Comparable {
+            }
+            abstract class B extends A implements Comparable {
+            }
+
+            print 'works'
+        '''
+    }
+
+    // GROOVY-11736
+    @Test
+    void testReImplementsInterface4() {
+        def err = shouldFail '''
+            abstract class A implements Comparable<Object> {
+            }
+            abstract class B extends A implements Comparable {
+            }
+        '''
+        assert err.message.contains('The interface Comparable cannot be 
implemented more than once with different arguments: java.lang.Comparable and 
java.lang.Comparable<java.lang.Object>')
+    }
+
+    // GROOVY-11803
+    @Test
+    void testDefaultInterfaceMethod1() {
+        assertScript '''
+            interface Foo {
+                default int barSize() {
+                    return bar.size()
+                }
+                String getBar()
+            }
+            class Baz implements Foo {
+                final String bar = 'BAR'
+            }
+
+            assert new Baz().barSize() == 3
+        '''
+    }
+
+    // GROOVY-11803
+    @Test
+    void testDefaultInterfaceMethod2() {
+        assertScript '''
+            interface Foo {
+                default int barSize() {
+                    return bar.size()
+                }
+                default String getBar() {
+                    return 'fizzbuzz'
+                }
+            }
+            class Baz implements Foo {
+            }
+
+            assert new Baz().barSize() == 8
+        '''
+    }
+
+    // GROOVY-11548
+    @Test
+    void testDefaultInterfaceMethod3() {
+        assertScript '''
+            interface A {
+                default m() { 'A' }
+            }
+            class B {
+                public final m() { 'B' }
+            }
+            class C extends B implements A {
+            }
+
+            assert new C().m() == 'B'
+        '''
+    }
+
+    // GROOVY-11758, GROOVY-11830
+    @Test
+    void testSuperClassAndInterfaceMethod() {
+        for (spec in ['protected 
final','protected','@PackageScope','private']) {
+            def err = shouldFail """import groovy.transform.*
+                interface A {
+                    def m()
+                }
+                class B {
+                    $spec def m() { 'B' }
+                }
+                class C extends B implements A {
+                }
+            """
+            spec = spec.split()[0]
+            if (spec == '@PackageScope') spec = 'package-private'
+            assert err =~ /$spec method m\(\) from B cannot shadow the public 
method in A/
+        }
+    }
+
+    // GROOVY-11753
+    @Test
+    void testSuperClassCovariantOfParameterizedInterface() {
+        assertScript '''
+            class A extends B {
+            }
+            class B implements C<String> {
+                static class NestMate {
+                }
+                @Override
+                void p(String s) {
+                    print(s)
+                }
+            }
+            interface C<T> {
+                void p(T t)
+            }
+
+            new A().p("")
+        '''
     }
 }
diff --git a/src/test/groovy/groovy/OverrideTest.groovy 
b/src/test/groovy/groovy/OverrideTest.groovy
index a7699d7002..5eec323bd3 100644
--- a/src/test/groovy/groovy/OverrideTest.groovy
+++ b/src/test/groovy/groovy/OverrideTest.groovy
@@ -33,15 +33,13 @@ final class OverrideTest {
                 void methodTakeT(T t) { }
                 T methodMakeT() { return null }
             }
-
             interface Intf<U> {
                 def method4()
                 void method5(U u)
                 U method6()
             }
-
-            interface IntfString extends Intf<String> {}
-
+            interface IntfString extends Intf<String> {
+            }
             class OverrideAnnotationTest extends Parent<Integer> implements 
IntfString {
                 @Override method() {}
                 @Override void methodTakeT(Integer arg) {}
@@ -63,15 +61,13 @@ final class OverrideTest {
                 void methodTakeT(T t) { }
                 T methodMakeT() { return null }
             }
-
             interface Intf<U> {
                 def method4()
                 void method5(U u)
                 U method6()
             }
-
-            interface IntfString extends Intf<String> {}
-
+            interface IntfString extends Intf<String> {
+            }
             class OverrideAnnotationTest extends Parent<Integer> implements 
IntfString {
                 @Override method() {}
                 @Override void methodTakeT(arg) {}
@@ -80,11 +76,9 @@ final class OverrideTest {
                 @Override void method5(String arg) {}
                 @Override String method6() {}
             }
-
-            new OverrideAnnotationTest()
         '''
-        assert err.message.contains(/The return type of java.lang.Double 
methodMakeT() in OverrideAnnotationTest is incompatible with java.lang.Integer 
in Parent/)
-        assert err.message.contains(/Method 'methodTakeT' from class 
'OverrideAnnotationTest' does not override method from its superclass or 
interfaces but is annotated with @Override./)
+        assert err.message.contains("The return type of java.lang.Double 
methodMakeT() in OverrideAnnotationTest is incompatible with java.lang.Integer 
in Parent")
+        assert err.message.contains("Method 'methodTakeT' from class 
'OverrideAnnotationTest' does not override method from its superclass or 
interfaces but is annotated with @Override")
     }
 
     @Test
@@ -93,9 +87,8 @@ final class OverrideTest {
             interface Intf<U> {
                 def method()
             }
-
-            interface IntfString extends Intf<String> {}
-
+            interface IntfString extends Intf<String> {
+            }
             class HasSpuriousMethod implements IntfString {
                 @Override method() {}
                 @Override someOtherMethod() {}
@@ -111,9 +104,8 @@ final class OverrideTest {
                 def method()
                 U method6()
             }
-
-            interface IntfString extends Intf<String> {}
-
+            interface IntfString extends Intf<String> {
+            }
             class HasMethodWithBadReturnType implements IntfString {
                 @Override method() {}
                 @Override methodReturnsObject() {}
@@ -129,9 +121,8 @@ final class OverrideTest {
                 def method()
                 void method6(U u)
             }
-
-            interface IntfString extends Intf<String> {}
-
+            interface IntfString extends Intf<String> {
+            }
             class HasMethodWithBadArgType implements IntfString {
                 @Override method() {}
                 @Override void methodTakesObject(arg) {}
@@ -140,99 +131,86 @@ final class OverrideTest {
         assert err.message.contains("Method 'methodTakesObject' from class 
'HasMethodWithBadArgType' does not override method from its superclass or 
interfaces but is annotated with @Override.")
     }
 
-    // GROOVY-6654
     @Test
     void testCovariantParameterType1() {
         assertScript '''
-            class C<T> {
-                void proc(T t) {}
-            }
-
-            class D extends C<String> {
-                @Override
-                void proc(String s) {}
+            class C implements Comparable<C> {
+                int index
+                int compareTo(C c) {
+                    index <=> c.index
+                }
             }
 
-            def d = new D()
+            def one = new C(index:1)
+            def two = new C(index:2)
+            assert one < two
         '''
     }
 
-    // GROOVY-10675
     @Test
     void testCovariantParameterType2() {
         assertScript '''
-            @FunctionalInterface
-            interface A<I, O> {
-                O apply(I in)
-            }
-            interface B<X, Y> extends A<X, Y> {
+            interface I<T> {
+               int handle(long n, T t)
             }
-            class C implements B<Number, String> {
-                @Override String apply(Number n) { 'x' }
+            class C implements I<String> {
+                int handle(long n, String something) {
+                    1
+                }
             }
 
-            def result = new C().apply(42)
-            assert result == 'x'
+            def c = new C()
+            assert c.handle(5,"hi") == 1
         '''
     }
 
-    // GROOVY-7849
     @Test
-    void testCovariantArrayReturnType1() {
+    void testCovariantParameterType3() {
         assertScript '''
-            interface A {
-            }
-            interface B extends A {
-            }
-            interface I {
-                A[] foo()
+            interface I<T> {
+                int testMethod(T t)
             }
-            class C implements I {
-                B[] foo() { null }
+            class C implements I<Date> {
+                int testMethod(Date date) {}
+                int testMethod(Object obj) {}
             }
 
-            new C().foo()
+            assert C.declaredMethods.count{ it.name=="testMethod" } == 2
         '''
     }
 
-    // GROOVY-7185
+    // GROOVY-6654
     @Test
-    void testCovariantArrayReturnType2() {
+    void testCovariantParameterType4() {
         assertScript '''
-            interface A<T> {
-                T[] process();
-            }
-            class B implements A<String> {
-                @Override
-                public String[] process() {
-                    ['foo']
-                }
+            class C<T> {
+                void proc(T t) {}
             }
-            class C extends B {
+            class D extends C<String> {
                 @Override
-                String[] process() {
-                    super.process()
-                }
+                void proc(String s) {}
             }
 
-            assert new C().process()[0] == 'foo'
+            def d = new D()
         '''
     }
 
-    // GROOVY-11579
+    // GROOVY-10675
     @Test
-    void testCovariantBridgeReturnType() {
+    void testCovariantParameterType5() {
         assertScript '''
-            interface I<T> {
-                T m()
+            @FunctionalInterface
+            interface A<I, O> {
+                O apply(I in)
             }
-            abstract class A {
-                final String m() { 'A' }
+            interface B<X, Y> extends A<X, Y> {
             }
-            class C extends A implements I<String> {
+            class C implements B<Number, String> {
+                @Override String apply(Number n) { 'x' }
             }
 
-            assert new C().m() == 'A'
+            def result = new C().apply(42)
+            assert result == 'x'
         '''
     }
 
@@ -242,13 +220,12 @@ final class OverrideTest {
             interface TemplatedInterface {
                 String execute(Map argument)
             }
-
             class TemplatedInterfaceImplementation implements 
TemplatedInterface {
                 @Override
                 String execute(Map argument = [:]) {
-                    return null
                 }
             }
+
             new TemplatedInterfaceImplementation()
         '''
     }
@@ -259,31 +236,14 @@ final class OverrideTest {
             interface TemplatedInterface {
                 String execute(Map argument)
             }
-
             class TemplatedInterfaceImplementation implements 
TemplatedInterface {
                 @Override
                 String execute(Map argument, String foo = null) {
                     return foo
                 }
             }
-            new TemplatedInterfaceImplementation()
-        '''
-    }
 
-    // GROOVY-11548
-    @Test
-    void testDefaultMethodDoesNotOverride() {
-        assertScript '''
-            class A {
-                final func() { 'A' }
-            }
-            interface B {
-                default func() { 'B' }
-            }
-            class C extends A implements B {
-            }
-
-            assert new C().func() == 'A'
+            new TemplatedInterfaceImplementation()
         '''
     }
 }

Reply via email to