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() {