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/12/01 22:51:07 UTC
[groovy] branch master updated: GROOVY-10742, GROOVY-10858: STC: return type of method pointer/reference
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 798903b2ef GROOVY-10742, GROOVY-10858: STC: return type of method pointer/reference
798903b2ef is described below
commit 798903b2ef9089ebf8b39ce96eda8b654ae8cdb8
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Dec 1 16:50:36 2022 -0600
GROOVY-10742, GROOVY-10858: STC: return type of method pointer/reference
---
.../transform/stc/StaticTypeCheckingSupport.java | 2 +-
.../transform/stc/StaticTypeCheckingVisitor.java | 49 ++++++++++------------
.../transform/stc/MethodReferenceTest.groovy | 18 ++++++--
3 files changed, 38 insertions(+), 31 deletions(-)
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 57eef99ea9..48cf4afe26 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1385,7 +1385,7 @@ public abstract class StaticTypeCheckingSupport {
GenericsType gt = GenericsUtils.buildWildcardType(parameterType);
if (!gt.isCompatibleWith(argumentType)) {
boolean samCoercion = isSAMType(parameterType) && argumentType.equals(CLOSURE_TYPE);
- if (!samCoercion) return false;
+ if (!samCoercion) return false; // else assume parameters and return checked earlier
}
} else if (parameterType.isArray() && argumentType.isArray()) {
// verify component type
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 d0cd9d77df..3ef7d60c4b 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2408,9 +2408,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
String nameText = nameExpr.getText();
if ("new".equals(nameText)) {
- ClassNode receiverType = getType(expression.getExpression());
- if (isClassClassNodeWrappingConcreteType(receiverType)) {
- storeType(expression, wrapClosureType(receiverType));
+ ClassNode type = getType(expression.getExpression());
+ if (isClassClassNodeWrappingConcreteType(type)){
+ type = type.getGenericsTypes()[0].getType();
+ storeType(expression,wrapClosureType(type));
}
return;
}
@@ -2454,7 +2455,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
storeType(expression, closureType);
});
expression.putNodeMetaData(MethodNode.class, candidates);
- } else if (!(expression instanceof MethodReferenceExpression)) {
+ } else if (!(expression instanceof MethodReferenceExpression)
+ || this.getClass() == StaticTypeCheckingVisitor.class) {
ClassNode type = wrapTypeIfNecessary(getType(expression.getExpression()));
if (isClassClassNodeWrappingConcreteType(type)) type = type.getGenericsTypes()[0].getType();
addStaticTypeError("Cannot find matching method " + prettyPrintTypeName(type) + "#" + nameText + ". Please check if the declared type is correct and if the method exists.", nameExpr);
@@ -3674,13 +3676,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
int nParameters = parameters.length;
if (nParameters > 0) {
ClassNode firstParamType = dynamicType();
- // GROOVY-10734: Type::instanceMethod has implied first param
- List<MethodNode> candidates = methodReference.getNodeMetaData(MethodNode.class);
- if (candidates != null && !candidates.isEmpty()) {
- ClassNode objExpType = getType(methodReference.getExpression());
- if (isClassClassNodeWrappingConcreteType(objExpType)
- && candidates.stream().allMatch(mn -> !mn.isStatic())) {
- firstParamType = objExpType.getGenericsTypes()[0].getType();
+ ClassNode sourceExprType = getType(methodReference.getExpression());
+ if (isClassClassNodeWrappingConcreteType(sourceExprType) && !"new".equals(methodReference.getMethodName().getText())) {
+ // GROOVY-10734: Type::instanceMethod has implied first param
+ List<MethodNode> candidates = methodReference.getNodeMetaData(MethodNode.class);
+ if (candidates != null && !candidates.isEmpty() && candidates.stream().allMatch(mn ->
+ !(mn instanceof ExtensionMethodNode ? ((ExtensionMethodNode) mn).isStaticExtension() : mn.isStatic()))) {
+ firstParamType = sourceExprType.getGenericsTypes()[0].getType();
}
}
parameters = new Parameter[nParameters];
@@ -4560,28 +4562,23 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
private ClassNode inferSAMTypeGenericsInAssignment(final ClassNode samType, final MethodNode abstractMethod, final ClassNode closureType, final ClosureExpression closureExpression) {
- // if the sam type or closure type do not provide generics information,
- // we cannot infer anything, thus we simply return the provided samUsage
- GenericsType[] samTypeGenerics = samType.getGenericsTypes();
- GenericsType[] closureGenerics = closureType.getGenericsTypes();
- if (samTypeGenerics == null || closureGenerics == null) return samType;
-
- // extract the generics from the return type
Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
- extractGenericsConnections(connections, wrapTypeIfNecessary(getInferredReturnType(closureExpression)), abstractMethod.getReturnType());
- // next we get the block parameter types and set the generics
- // information just like before
- // TODO: add vargs handling
+ // extract generics from the closure return type
+ ClassNode closureReturnType = isClosureWithType(closureType)
+ ? getCombinedBoundType(closureType.getGenericsTypes()[0])
+ : wrapTypeIfNecessary(getInferredReturnType(closureExpression));
+ extractGenericsConnections(connections, closureReturnType, abstractMethod.getReturnType());
+
+ // extract generics from the closure parameters
if (closureExpression.isParameterSpecified()) {
Parameter[] closureParams = closureExpression.getParameters();
- Parameter[] methodParams = abstractMethod.getParameters();
+ Parameter[] methodParams = abstractMethod.getParameters();
for (int i = 0, n = Math.min(closureParams.length, methodParams.length); i < n; i += 1) {
- ClassNode closureParamType = closureParams[i].getType();
- ClassNode methodParamType = methodParams[i].getType();
- extractGenericsConnections(connections, closureParamType, methodParamType);
+ extractGenericsConnections(connections, closureParams[i].getType(), methodParams[i].getType());
}
}
+
return applyGenericsContext(connections, samType.redirect());
}
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 0a9c5d3ae7..e155747d2a 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -18,6 +18,7 @@
*/
package groovy.transform.stc
+import groovy.test.NotYetImplemented
import org.junit.Test
import static groovy.test.GroovyAssert.assertScript
@@ -603,7 +604,7 @@ final class MethodReferenceTest {
assertScript shell, '''
@CompileStatic
void test() {
- Function<String, Integer> f = Integer::new
+ Function<String, Integer> f = Integer::new // deprcated; parseInt/valueOf
def result = ["1", "2", "3"].stream().map(f).collect(Collectors.toList())
assert result == [1, 2, 3]
}
@@ -725,6 +726,7 @@ final class MethodReferenceTest {
'''
}
+ @NotYetImplemented
@Test // class::staticMethod
void testFunctionCS4() {
assertScript shell, '''
@@ -810,7 +812,7 @@ final class MethodReferenceTest {
'''
}
- @groovy.test.NotYetImplemented
+ @NotYetImplemented
@Test // class::staticMethod
void testFunctionCS8() {
assertScript shell, '''
@@ -983,7 +985,7 @@ final class MethodReferenceTest {
'''
}
- @Test // GROOVY-10742
+ @Test // GROOVY-10742, GROOVY-10858
void testIncompatibleReturnType() {
def err = shouldFail shell, '''
void foo(bar) {
@@ -993,7 +995,15 @@ final class MethodReferenceTest {
Function<Object,String> f = this::foo
}
'''
- assert err =~ /Invalid return type: void is not convertible to java.lang.String/
+ assert err =~ /Cannot assign java.util.function.Function<java.lang.Object, java.lang.Void> to: java.util.function.Function<java.lang.Object, java.lang.String>/
+
+ err = shouldFail shell, '''
+ @CompileStatic
+ void test() {
+ Function<Object,Number> f = Object::toString
+ }
+ '''
+ assert err =~ /Cannot assign java.util.function.Function<java.lang.Object, java.lang.String> to: java.util.function.Function<java.lang.Object, java.lang.Number>/
}
@Test // GROOVY-10269