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 195541397f GROOVY-5881, GROOVY-6324: STC: implicit ".call" for
property expression
195541397f is described below
commit 195541397fffa580fe7ee4782446cd1372a421aa
Author: Eric Milles <[email protected]>
AuthorDate: Fri Sep 13 15:34:22 2024 -0500
GROOVY-5881, GROOVY-6324: STC: implicit ".call" for property expression
GROOVY-5705, GROOVY-11366: STC: implicit ".call" for variable expression
---
.../MethodCallExpressionTransformer.java | 34 ++++---------
.../transform/stc/StaticTypeCheckingVisitor.java | 55 +++++++---------------
.../groovy/transform/stc/ClosuresSTCTest.groovy | 24 ++++++++++
3 files changed, 50 insertions(+), 63 deletions(-)
diff --git
a/src/main/java/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java
b/src/main/java/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java
index d24c32571a..2f74882c80 100644
---
a/src/main/java/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java
+++
b/src/main/java/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java
@@ -28,10 +28,8 @@ import org.codehaus.groovy.ast.stmt.EmptyStatement;
import org.codehaus.groovy.classgen.asm.MopWriter;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.transform.stc.ExtensionMethodNode;
-import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor;
import org.codehaus.groovy.transform.stc.StaticTypesMarker;
-import static org.apache.groovy.ast.tools.ClassNodeUtils.getField;
import static org.codehaus.groovy.classgen.AsmClassGenerator.argumentSize;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
@@ -61,20 +59,15 @@ class MethodCallExpressionTransformer {
return
transformMethodCallExpression(transformToMopSuperCall((ClassNode)
superCallReceiver, mce));
}
- if (isCallOnClosure(mce)) {
- var field = getField(scTransformer.getClassNode(),
mce.getMethodAsString());
- if (field != null) {
- var closureFieldCall = new MethodCallExpression(
- new VariableExpression(field),
- "call",
- scTransformer.transform(arguments));
- // implicit-this "field(args)" expression has no place for
safe, spread-safe, or type arguments
- closureFieldCall.setImplicitThis(false);
-
closureFieldCall.setMethodTarget(mce.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
- closureFieldCall.setSourcePosition(mce);
- closureFieldCall.copyNodeMetaData(mce);
- return closureFieldCall;
- }
+ Expression callable = mce.getNodeMetaData("callable property");
+ if (callable != null) {
+ var callableCall = new MethodCallExpression(callable, "call",
scTransformer.transform(arguments));
+ // "callable(args)" expression has no place for safe, spread-safe
or type arguments
+ callableCall.setImplicitThis(false);
+
callableCall.setMethodTarget(mce.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
+ callableCall.setSourcePosition(mce);
+ callableCall.copyNodeMetaData(mce);
+ return callableCall;
}
return scTransformer.superTransform(mce);
@@ -90,15 +83,6 @@ class MethodCallExpressionTransformer {
((ExtensionMethodNode)
node).getExtensionMethodNode().getDeclaringClass().getName());
}
- private static boolean isCallOnClosure(final MethodCallExpression expr) {
- MethodNode target =
expr.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
- return expr.isImplicitThis()
- && !"call".equals(expr.getMethodAsString())
- && (target == StaticTypeCheckingVisitor.CLOSURE_CALL_VARGS
- || target == StaticTypeCheckingVisitor.CLOSURE_CALL_NO_ARG
- || target ==
StaticTypeCheckingVisitor.CLOSURE_CALL_ONE_ARG);
- }
-
private static MethodCallExpression transformToMopSuperCall(final
ClassNode superType, final MethodCallExpression expr) {
MethodNode mn =
expr.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
String mopName = MopWriter.getMopMethodName(mn, false);
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 b7c5675bc9..3a6d96d9f3 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -145,7 +145,6 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
-import static org.apache.groovy.ast.tools.ClassNodeUtils.getField;
import static
org.apache.groovy.ast.tools.MethodNodeUtils.withDefaultArgumentMethods;
import static org.apache.groovy.util.BeanUtils.capitalize;
import static org.apache.groovy.util.BeanUtils.decapitalize;
@@ -3649,46 +3648,13 @@ out: if ((samParameterTypes.length == 1 &&
isOrImplements(samParameterTypes[0
// visit functional arguments *after* target method selection
visitMethodCallArguments(receiver, argumentList, false, null);
- boolean isThisObjectExpression = isThisExpression(objectExpression);
- boolean isCallOnClosure = false;
- FieldNode fieldNode = null;
- switch (name) {
- case "call":
- case "doCall":
- if (!isThisObjectExpression) {
- isCallOnClosure = receiver.equals(CLOSURE_TYPE);
- }
- default:
- if (isThisObjectExpression) {
- // GROOVY-5705, GROOVY-11366: "this.x(...)" could refer to
field
- if (!typeCheckingContext.isInStaticContext) {
- fieldNode = getField(receiver, name);
- } else {
- fieldNode = getField(receiver, name, FieldNode::isStatic);
- }
- if (fieldNode != null
- && getType(fieldNode).equals(CLOSURE_TYPE)
- && !receiver.hasPossibleMethod(name, callArguments)) {
- isCallOnClosure = true;
- }
- }
- }
-
try {
+ boolean isThisObjectExpression =
isThisExpression(objectExpression);
ClassNode[] args = getArgumentTypes(argumentList);
boolean functorsVisited = false;
- if (isCallOnClosure) {
- if (fieldNode != null) {
- GenericsType[] genericsTypes =
getType(fieldNode).getGenericsTypes();
- if (genericsTypes != null) {
- Parameter[] parameters =
fieldNode.getNodeMetaData(CLOSURE_ARGUMENTS);
- if (parameters != null) {
- typeCheckClosureCall(callArguments, args,
parameters);
- }
- ClassNode closureReturnType =
genericsTypes[0].getType();
- storeType(call, closureReturnType);
- }
- } else if (objectExpression instanceof VariableExpression) {
+ if (!isThisObjectExpression && receiver.equals(CLOSURE_TYPE)
+ && (name.equals("call") || name.equals("doCall"))) {
+ if (objectExpression instanceof VariableExpression) {
Variable variable =
findTargetVariable((VariableExpression) objectExpression);
if (variable instanceof ASTNode) {
Parameter[] parameters = ((ASTNode)
variable).getNodeMetaData(CLOSURE_ARGUMENTS);
@@ -3761,6 +3727,19 @@ out: if ((samParameterTypes.length == 1 &&
isOrImplements(samParameterTypes[0
}
}
}
+ if (mn.isEmpty() && !name.equals("call")) {
+ // GROOVY-5705, GROOVY-5881, GROOVY-6324,
GROOVY-11366: closure property
+ var property = propX(objectExpression,
call.getMethod(), call.isSafe());
+ property.setImplicitThis(call.isImplicitThis());
+ if (existsProperty(property, true)
+ && getType(property).equals(CLOSURE_TYPE)) {
+ chosenReceiver = Receiver.make(getType(property));
+ call.putNodeMetaData("callable property",
property);
+ List<Expression> list =
argumentList.getExpressions();
+ int nArgs = list.stream().noneMatch(e -> e
instanceof SpreadExpression) ? list.size() : -1;
+ mn = List.of(nArgs == 0 ? CLOSURE_CALL_NO_ARG :
nArgs == 1 ? CLOSURE_CALL_ONE_ARG : CLOSURE_CALL_VARGS);
+ }
+ }
if (mn.isEmpty()) {
mn = extension.handleMissingMethod(receiver, name,
argumentList, args, call);
if (mn == null || mn.isEmpty()) {
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index ddc8066601..a7893321c9 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -229,6 +229,30 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-5881
+ void testCallClosure21() {
+ assertScript '''
+ Map<Integer, Closure<Integer>> m = [1: { int i -> i }, 2:
Closure.IDENTITY]
+ int result = 0
+ for (e in m) {
+ def c = e.value
+ def x = c(e.key)
+ assert x == e.key
+ result += e.value(e.key)
+ }
+ assert result == 3
+ '''
+ }
+
+ // GROOVY-6324
+ void testCallClosure22() {
+ assertScript '''
+ class Car { Closure<String> model }
+ def c = new Car(model: {->'Tesla'})
+ assert c.model() == 'Tesla'
+ '''
+ }
+
void testClosureReturnTypeInference1() {
assertScript '''
def c = { int a, int b -> return a + b }