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

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


The following commit(s) were added to refs/heads/master by this push:
     new 5c668fcb61 GROOVY-11381: return getter from interface (default method)
5c668fcb61 is described below

commit 5c668fcb616097aaf54c41ca43fff2a61a84b537
Author: Eric Milles <[email protected]>
AuthorDate: Sun May 19 13:43:16 2024 -0500

    GROOVY-11381: return getter from interface (default method)
---
 .../java/org/codehaus/groovy/ast/ClassNode.java    | 24 ++++++---
 .../transform/DelegateASTTransformation.java       | 11 ++--
 .../transform/stc/StaticTypeCheckingSupport.java   |  1 +
 .../transform/stc/StaticTypeCheckingVisitor.java   | 22 ++++----
 .../stc/FieldsAndPropertiesSTCTest.groovy          | 61 ++++++++++++++++++----
 5 files changed, 86 insertions(+), 33 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/ClassNode.java 
b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
index b01d8770fc..2c7533305e 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
@@ -909,8 +909,10 @@ public class ClassNode extends AnnotatedNode {
      * @return method node or null
      */
     public MethodNode getDeclaredMethod(String name, Parameter[] parameters) {
+        boolean zeroParameters = ArrayGroovyMethods.asBoolean(parameters);
         for (MethodNode method : getDeclaredMethods(name)) {
-            if (parametersEqual(method.getParameters(), parameters)) {
+            if (zeroParameters ? method.getParameters().length == 0
+                    : parametersEqual(method.getParameters(), parameters)) {
                 return method;
             }
         }
@@ -1044,7 +1046,7 @@ public class ClassNode extends AnnotatedNode {
         return getGetterMethod(getterName, true);
     }
 
-    public MethodNode getGetterMethod(String getterName, boolean 
searchSuperClasses) {
+    public MethodNode getGetterMethod(String getterName, boolean searchSupers) 
{
         MethodNode getterMethod = null;
 
         java.util.function.Predicate<MethodNode> isNullOrSynthetic = (method) 
->
@@ -1075,14 +1077,24 @@ public class ClassNode extends AnnotatedNode {
             }
         }
 
-        if (searchSuperClasses && isNullOrSynthetic.test(getterMethod)) {
-            ClassNode parent = getSuperClass();
-            if (parent != null) {
-                MethodNode method = parent.getGetterMethod(getterName);
+        if (searchSupers && isNullOrSynthetic.test(getterMethod)) {
+            ClassNode superClass = getSuperClass();
+            if (superClass != null) {
+                MethodNode method = superClass.getGetterMethod(getterName);
                 if (getterMethod == null || !isNullOrSynthetic.test(method)) {
                     getterMethod = method;
                 }
             }
+            // GROOVY-11381:
+            if (getterMethod == null && 
ArrayGroovyMethods.asBoolean(getInterfaces())) {
+                for (ClassNode anInterface : getAllInterfaces()) {
+                    MethodNode method = 
anInterface.getDeclaredMethod(getterName, Parameter.EMPTY_ARRAY);
+                    if (method != null && method.isDefault() && 
(booleanReturnOnly ? ClassHelper.isPrimitiveBoolean(method.getReturnType()) : 
!method.isVoidMethod())) {
+                        getterMethod = method;
+                        break;
+                    }
+                }
+            }
         }
 
         return getterMethod;
diff --git 
a/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java 
b/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
index 38d586219b..b3146475fb 100644
--- a/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
@@ -49,7 +49,6 @@ import java.util.Set;
 import static java.util.Arrays.copyOf;
 import static java.util.stream.Collectors.toSet;
 import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
-import static org.apache.groovy.util.BeanUtils.capitalize;
 import static org.codehaus.groovy.ast.ClassHelper.DEPRECATED_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.isGroovyObjectType;
 import static org.codehaus.groovy.ast.ClassHelper.isObjectType;
@@ -292,7 +291,7 @@ public class DelegateASTTransformation extends 
AbstractASTTransformation {
             }
             mNames.add(getGetterName(name));
             if (isPrimitiveBoolean(pNode.getOriginType())) {
-                mNames.add(getPredicateName(name));
+                mNames.add(getGetterName(name, Boolean.TYPE));
             }
         }
         for (MethodNode mNode : cNode.getAllDeclaredMethods()) {
@@ -331,7 +330,7 @@ public class DelegateASTTransformation extends 
AbstractASTTransformation {
         boolean willHaveGetAccessor = true;
         boolean willHaveIsAccessor = isPrimBool;
         String getterName = getGetterName(name);
-        String isserName = getPredicateName(name);
+        String  isserName = getGetterName(name, Boolean.TYPE);
         if (isPrimBool) {
             ClassNode cNode = prop.getDeclaringClass();
             if (cNode.getGetterMethod(isserName) != null && 
cNode.getGetterMethod(getterName) == null)
@@ -372,7 +371,7 @@ public class DelegateASTTransformation extends 
AbstractASTTransformation {
 
     private static void extractAccessorInfo(final ClassNode owner, final 
String name, final Reference<Boolean> willHaveGetAccessor, final 
Reference<Boolean> willHaveIsAccessor) {
         boolean hasGetAccessor = owner.getGetterMethod(getGetterName(name)) != 
null;
-        boolean hasIsAccessor = owner.getGetterMethod(getPredicateName(name)) 
!= null;
+        boolean hasIsAccessor  = owner.getGetterMethod(getGetterName(name, 
Boolean.TYPE)) != null;
         PropertyNode prop = owner.getProperty(name);
         willHaveGetAccessor.set(hasGetAccessor || (prop != null && 
!hasIsAccessor));
         willHaveIsAccessor.set(hasIsAccessor || (prop != null && 
!hasGetAccessor && isPrimitiveBoolean(prop.getOriginType())));
@@ -471,10 +470,6 @@ public class DelegateASTTransformation extends 
AbstractASTTransformation {
         return false;
     }
 
-    private static String getPredicateName(final String propertyName) {
-        return "is" + capitalize(propertyName);
-    }
-
     static class DelegateDescription {
         AnnotationNode annotation;
         AnnotatedNode delegate;
diff --git 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index 54dbcbffec..fcc00109dc 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -2290,6 +2290,7 @@ public abstract class StaticTypeCheckingSupport {
                 && !genericsTypes[0].isWildcard();
     }
 
+    @Deprecated(since = "5.0.0")
     public static List<MethodNode> findSetters(final ClassNode cn, final 
String setterName, final boolean voidOnly) {
         List<MethodNode> result = new ArrayList<>();
         if (!cn.isInterface()) {
diff --git 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index e846faeeec..b9c5f70b6b 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -285,7 +285,6 @@ import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.extrac
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.extractGenericsParameterMapOfThis;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.filterMethodsByVisibility;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsForClassNode;
-import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findSetters;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findTargetVariable;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.fullyResolve;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.fullyResolveType;
@@ -1585,14 +1584,14 @@ out:    if ((samParameterTypes.length == 1 && 
isOrImplements(samParameterTypes[0
         final String setterName = getSetterName(propertyName);
 
         boolean foundGetterOrSetter = false;
-        Set<ClassNode> handledNodes = new HashSet<>();
+        Set<ClassNode> handledTypes = new HashSet<>();
         List<Receiver<String>> receivers = new ArrayList<>();
         addReceivers(receivers, makeOwnerList(objectExpression), 
pexp.isImplicitThis());
 
         for (Receiver<String> receiver : receivers) {
             ClassNode receiverType = receiver.getType();
 
-            if (receiverType.isArray() && "length".equals(propertyName)) {
+            if (receiverType.isArray() && propertyName.equals("length")) {
                 pexp.putNodeMetaData(READONLY_PROPERTY, Boolean.TRUE);
                 storeType(pexp, int_TYPE);
                 if (visitor != null) {
@@ -1608,10 +1607,14 @@ out:    if ((samParameterTypes.length == 1 && 
isOrImplements(samParameterTypes[0
             boolean staticOnly = 
isClassClassNodeWrappingConcreteType(receiverType) ? false
                                    : (receiver.getData() == null ? 
staticOnlyAccess : false);
 
-            List<MethodNode> setters = 
findSetters(wrapTypeIfNecessary(receiverType), setterName, /*voidOnly:*/false);
-            setters = allowStaticAccessToMember(setters, staticOnly);
-            // GROOVY-11319:
-            setters.removeIf(setter -> 
!hasAccessToMember(typeCheckingContext.getEnclosingClassNode(), 
setter.getDeclaringClass(), setter.getModifiers()));
+            List<MethodNode> setters = new ArrayList<>(4);
+            for (MethodNode method : 
findMethodsWithGenerated(wrapTypeIfNecessary(receiverType), setterName)) {
+                if ((!staticOnly || method.isStatic()) && 
method.getParameters().length == 1
+                        // GROOVY-11319:
+                        && 
hasAccessToMember(typeCheckingContext.getEnclosingClassNode(), 
method.getDeclaringClass(), method.getModifiers())) {
+                    setters.add(method);
+                }
+            }
             // GROOVY-11372:
             var loader = getSourceUnit().getClassLoader();
             var dgmSet = (TreeSet<MethodNode>) 
findDGMMethodsForClassNode(loader,            receiverType,  setterName);
@@ -1629,7 +1632,7 @@ out:    if ((samParameterTypes.length == 1 && 
isOrImplements(samParameterTypes[0
             }
             while (!queue.isEmpty()) {
                 ClassNode current = queue.remove();
-                if (!handledNodes.add(current)) continue;
+                if (!handledTypes.add(current)) continue;
 
                 FieldNode field = current.getDeclaredField(propertyName);
                 if (field == null) {
@@ -1637,8 +1640,7 @@ out:    if ((samParameterTypes.length == 1 && 
isOrImplements(samParameterTypes[0
                         queue.addFirst(current.getSuperClass());
                     Collections.addAll(queue, current.getInterfaces());
                 }
-
-                field = allowStaticAccessToMember(field, staticOnly);
+                else field = allowStaticAccessToMember(field, staticOnly);
 
                 // skip property/accessor checks for "x.@field"
                 if (pexp instanceof AttributeExpression) {
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy 
b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index b078243307..4fc8912be7 100644
--- a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -524,7 +524,7 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
         '''
     }
 
-    void testSetterUsingPropertyNotationOnInterface() {
+    void testSetterUsingPropertyNotation2() {
         assertScript '''
             interface FooAware { void setFoo(String arg) }
             class C implements FooAware {
@@ -539,7 +539,7 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
     }
 
     // GROOVY-11372
-    void testSetterUsingPropertyNotationViaExtension() {
+    void testSetterUsingPropertyNotation3() {
         assertScript '''
             def baos = new ByteArrayOutputStream()
             assert baos.size() == 0
@@ -1247,6 +1247,20 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-6277
+    void testPublicFieldVersusPrivateGetter() {
+        assertScript '''
+            class C {
+                private String getWho() { 'C' }
+            }
+            class D extends C {
+                public String who = 'D'
+            }
+            String result = new D().who
+            assert result == 'D'
+        '''
+    }
+
     // GROOVY-9973
     void testPrivateFieldVersusPublicGetter() {
         assertScript '''
@@ -1262,18 +1276,47 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
         '''
     }
 
-    // GROOVY-6277
-    void testPublicFieldVersusPrivateGetter() {
+    // GROOVY-11381
+    void testPrivateFieldVersusPublicGetterOnInterface() {
         assertScript '''
-            class C {
-                private String getWho() { 'C' }
+            interface I {
+                default int getP() { 42 }
+            }
+            class C implements I {
+                private String p
+            }
+            def c = new C()
+            assert c.p == 42
+            Number c_p = c.p // Cannot assign value of type String to variable 
of type Number
+        '''
+    }
+
+    void testPrivateFieldVersusPublicSetterOnInterface() {
+        assertScript '''
+            interface I {
+                Object[] set = new Object[1]
+                default void setP(int p) { set[0] = true }
+            }
+            class C implements I {
+                private String p
             }
             class D extends C {
-                public String who = 'D'
             }
-            String result = new D().who
-            assert result == 'D'
+            def d = new D()
+            d.p = 42
+            assert I.set[0]
         '''
+        shouldFailWithMessages '''
+            interface I {
+                default void setP(int p) { }
+            }
+            class C implements I {
+                private String p
+            }
+            def c = new C()
+            c.p = 'xxx'
+        ''',
+        'Cannot assign value of type java.lang.String to variable of type int'
     }
 
     void testProtectedAccessorFromSamePackage() {

Reply via email to