You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/01/31 19:44:55 UTC
[groovy] 03/06: GROOVY-9751: STC: add inferencing of method pointer expression
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 6d37257117c6535ec28a22648488a25d4f4ccb7a
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Sep 21 14:03:24 2020 -0500
GROOVY-9751: STC: add inferencing of method pointer expression
Conflicts:
src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
---
.../transform/stc/StaticTypeCheckingVisitor.java | 69 ++++++++++++++++++++--
.../groovy/transform/stc/GenericsSTCTest.groovy | 34 +++++++++++
2 files changed, 98 insertions(+), 5 deletions(-)
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 a9bbc32..f505312 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -21,7 +21,6 @@ package org.codehaus.groovy.transform.stc;
import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
import groovy.lang.IntRange;
-import groovy.lang.Range;
import groovy.transform.NamedParam;
import groovy.transform.NamedParams;
import groovy.transform.TypeChecked;
@@ -66,6 +65,7 @@ import org.codehaus.groovy.ast.expr.MapEntryExpression;
import org.codehaus.groovy.ast.expr.MapExpression;
import org.codehaus.groovy.ast.expr.MethodCall;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.MethodPointerExpression;
import org.codehaus.groovy.ast.expr.NotExpression;
import org.codehaus.groovy.ast.expr.PostfixExpression;
import org.codehaus.groovy.ast.expr.PrefixExpression;
@@ -90,7 +90,6 @@ import org.codehaus.groovy.ast.stmt.SwitchStatement;
import org.codehaus.groovy.ast.stmt.TryCatchStatement;
import org.codehaus.groovy.ast.stmt.WhileStatement;
import org.codehaus.groovy.ast.tools.GenericsUtils;
-import org.codehaus.groovy.ast.tools.WideningCategories;
import org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode;
import org.codehaus.groovy.classgen.ReturnAdder;
import org.codehaus.groovy.classgen.asm.InvocationWriter;
@@ -231,6 +230,7 @@ import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.evalua
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.extractGenericsConnections;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.extractGenericsParameterMapOfThis;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments;
+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.fullyResolveType;
@@ -775,8 +775,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (Integer_TYPE.equals(fromType) && Integer_TYPE.equals(toType)) {
storeType(expression, ClassHelper.make(IntRange.class));
} else {
- ClassNode rangeType = ClassHelper.make(Range.class).getPlainNodeReference();
- rangeType.setGenericsTypes(new GenericsType[]{new GenericsType(WideningCategories.lowestUpperBound(fromType, toType))});
+ ClassNode rangeType = RANGE_TYPE.getPlainNodeReference();
+ rangeType.setGenericsTypes(new GenericsType[]{new GenericsType(lowestUpperBound(fromType, toType))});
storeType(expression, rangeType);
}
}
@@ -2385,6 +2385,65 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
+ @Override
+ public void visitMethodPointerExpression(final MethodPointerExpression expression) {
+ super.visitMethodPointerExpression(expression);
+ Expression nameExpr = expression.getMethodName();
+ if (nameExpr instanceof ConstantExpression
+ && getType(nameExpr).equals(STRING_TYPE)) {
+ String nameText = nameExpr.getText();
+
+ if ("new".equals(nameText)) {
+ ClassNode receiverType = getType(expression.getExpression());
+ if (isClassClassNodeWrappingConcreteType(receiverType)) {
+ storeType(expression, wrapClosureType(receiverType));
+ }
+ return;
+ }
+
+ List<Receiver<String>> receivers = new ArrayList<>();
+ addReceivers(receivers, makeOwnerList(expression.getExpression()), false);
+
+ List<MethodNode> candidates = EMPTY_METHODNODE_LIST;
+ for (Receiver<String> currentReceiver : receivers) {
+ ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
+
+ candidates = findMethodsWithGenerated(receiverType, nameText);
+ collectAllInterfaceMethodsByName(receiverType, nameText, candidates);
+ if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
+ candidates.addAll(findDGMMethodsForClassNode(getTransformLoader(), receiverType, nameText));
+ candidates = filterMethodsByVisibility(candidates);
+ if (!candidates.isEmpty()) {
+ break;
+ }
+ }
+
+ if (candidates.isEmpty()) {
+ candidates = extension.handleMissingMethod(
+ getType(expression.getExpression()), nameText, null, null, null);
+ } else if (candidates.size() > 1) {
+ candidates = extension.handleAmbiguousMethods(candidates, expression);
+ }
+
+ int n = candidates.size();
+ if (n > 0) {
+ ClassNode returnType;
+ if (n == 1) {
+ returnType = candidates.get(0).getReturnType();
+ } else {
+ List<ClassNode> returnTypes = new ArrayList<>(n);
+ for (MethodNode candidate : candidates) {
+ returnTypes.add(candidate.getReturnType());
+ }
+ returnType = lowestUpperBound(returnTypes);
+ }
+ if (!returnType.equals(OBJECT_TYPE)) {
+ storeType(expression, wrapClosureType(returnType));
+ }
+ }
+ }
+ }
+
private static ClassNode wrapClosureType(final ClassNode returnType) {
ClassNode inferredType = CLOSURE_TYPE.getPlainNodeReference();
inferredType.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(returnType))});
@@ -3053,7 +3112,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
} else {
ClassNode[] upperBounds = genericsType.getUpperBounds();
if (upperBounds != null) {
- value = WideningCategories.lowestUpperBound(Arrays.asList(upperBounds));
+ value = lowestUpperBound(Arrays.asList(upperBounds));
}
}
return value;
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 8bde750..7f2d341 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -583,6 +583,40 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-9751
+ void testShouldUseMethodGenericType5() {
+ assertScript '''
+ interface Service {
+ Number transform(String s)
+ }
+ void test(Service service) {
+ Set<Number> numbers = []
+ List<String> strings = ['1','2','3']
+ numbers.addAll(strings.collect(service.&transform))
+ }
+ test({ String s -> Integer.valueOf(s) } as Service)
+ '''
+ }
+
+ void testShouldUseMethodGenericType6() {
+ assertScript '''
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.rightExpression.getNodeMetaData(INFERRED_TYPE)
+ assert type.equals(CLOSURE_TYPE)
+ assert type.getGenericsTypes()[0].getType().equals(STRING_TYPE)
+ })
+ def ptr = "".&toString
+ '''
+ assertScript '''
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.rightExpression.getNodeMetaData(INFERRED_TYPE)
+ assert type.equals(CLOSURE_TYPE)
+ assert type.getGenericsTypes()[0].getType().equals(Double_TYPE)
+ })
+ def ptr = Math.&abs
+ '''
+ }
+
// GROOVY-5516
void testAddAllWithCollectionShouldBeAllowed() {
assertScript '''import org.codehaus.groovy.transform.stc.ExtensionMethodNode