diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java
index 63e1ad2bfc..61f269bf59 100644
--- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java
+++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java
@@ -5199,4 +5199,31 @@ public void testCompileStatic9786() {
runConformTest(sources, "B");
}
+
+ @Test
+ public void testCompileStatic9799() {
+ assumeTrue(isParrotParser());
+
+ //@formatter:off
+ String[] sources = {
+ "Main.groovy",
+ "class C {\n" +
+ " String x\n" +
+ "}\n" +
+ "class D {\n" +
+ " String x\n" +
+ " static D from(C c) {\n" +
+ " new D(x: c.x)\n" +
+ " }\n" +
+ "}\n" +
+ "@groovy.transform.CompileStatic\n" +
+ "void test(C c) {\n" +
+ " print Optional.of(c).map(D::from).map(D::getX).get()\n" +
+ "}\n" +
+ "test(new C(x: 'works'))\n",
+ };
+ //@formatter:on
+
+ runConformTest(sources, "works");
+ }
}
diff --git a/base/org.codehaus.groovy30/.checkstyle b/base/org.codehaus.groovy30/.checkstyle
index 28860e99b6..d9b361ba2f 100644
--- a/base/org.codehaus.groovy30/.checkstyle
+++ b/base/org.codehaus.groovy30/.checkstyle
@@ -37,8 +37,7 @@
-
-
+
diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/ast/tools/ParameterUtils.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/ast/tools/ParameterUtils.java
new file mode 100644
index 0000000000..d96bd32e08
--- /dev/null
+++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/ast/tools/ParameterUtils.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.groovy.ast.tools;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.Parameter;
+
+import java.util.function.BiPredicate;
+
+public class ParameterUtils {
+
+ public static boolean parametersEqual(Parameter[] a, Parameter[] b) {
+ return parametersEqual(a, b, false);
+ }
+
+ public static boolean parametersEqualWithWrapperType(Parameter[] a, Parameter[] b) {
+ return parametersEqual(a, b, true);
+ }
+
+ /**
+ * Checks if two parameter arrays are type-compatible.
+ *
+ * each parameter should match the following condition:
+ * {@code targetParameter.getType().getTypeClass().isAssignableFrom(sourceParameter.getType().getTypeClass())}
+ *
+ * @param source source parameters
+ * @param target target parameters
+ * @return the check result
+ * @since 3.0.0
+ */
+ public static boolean parametersCompatible(Parameter[] source, Parameter[] target) {
+ /* GRECLIPSE edit -- GROOVY-9799
+ return parametersMatch(source, target, (sourceType, targetType) ->
+ ClassHelper.getWrapper(targetType).getTypeClass().isAssignableFrom(ClassHelper.getWrapper(sourceType).getTypeClass())
+ );
+ */
+ return parametersMatch(source, target, org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport::isAssignableTo);
+ // GRECLIPSE end
+ }
+
+ private static boolean parametersEqual(Parameter[] a, Parameter[] b, boolean wrapType) {
+ return parametersMatch(a, b, (aType, bType) -> {
+ if (wrapType) {
+ aType = ClassHelper.getWrapper(aType);
+ bType = ClassHelper.getWrapper(bType);
+ }
+ return aType.equals(bType);
+ });
+ }
+
+ private static boolean parametersMatch(Parameter[] a, Parameter[] b, BiPredicate typeChecker) {
+ if (a.length == b.length) {
+ boolean answer = true;
+ for (int i = 0, n = a.length; i < n; i += 1) {
+ ClassNode aType = a[i].getType();
+ ClassNode bType = b[i].getType();
+
+ if (!typeChecker.test(aType, bType)) {
+ answer = false;
+ break;
+ }
+ }
+ return answer;
+ }
+ return false;
+ }
+}
diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
index 13d840e048..bbb5e032db 100644
--- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
+++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
@@ -35,29 +35,29 @@
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodReferenceExpression;
import org.codehaus.groovy.ast.tools.GeneralUtils;
-import org.codehaus.groovy.ast.tools.ParameterUtils;
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.classgen.asm.MethodReferenceExpressionWriter;
import org.codehaus.groovy.classgen.asm.WriterController;
import org.codehaus.groovy.runtime.ArrayTypeUtils;
import org.codehaus.groovy.syntax.RuntimeParserException;
import org.codehaus.groovy.transform.stc.ExtensionMethodNode;
-import groovyjarjarasm.asm.MethodVisitor;
import groovyjarjarasm.asm.Opcodes;
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
import java.util.stream.Collectors;
import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.ast.tools.ParameterUtils.parametersCompatible;
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.isAssignableTo;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.CLOSURE_ARGUMENTS;
/**
@@ -70,45 +70,33 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
private static final ClassNode GENERATED_TYPE = ClassHelper.make(Generated.class);
private static final ClassNode COMPILE_STATIC_TYPE = ClassHelper.make(CompileStatic.class);
- public StaticTypesMethodReferenceExpressionWriter(WriterController controller) {
+ public StaticTypesMethodReferenceExpressionWriter(final WriterController controller) {
super(controller);
}
@Override
- public void writeMethodReferenceExpression(MethodReferenceExpression methodReferenceExpression) {
+ public void writeMethodReferenceExpression(final MethodReferenceExpression methodReferenceExpression) {
ClassNode functionalInterfaceType = getFunctionalInterfaceType(methodReferenceExpression);
- if (null == functionalInterfaceType) {
- // if the parameter type failed to be inferred, generate the default bytecode, which is actually a method closure
+ if (functionalInterfaceType == null || !ClassHelper.isFunctionalInterface(functionalInterfaceType)) {
+ // generate the default bytecode, which is actually a method closure
super.writeMethodReferenceExpression(methodReferenceExpression);
return;
}
ClassNode redirect = functionalInterfaceType.redirect();
- if (!ClassHelper.isFunctionalInterface(redirect)) {
- // if the parameter type is not real FunctionalInterface, generate the default bytecode, which is actually a method closure
- super.writeMethodReferenceExpression(methodReferenceExpression);
- return;
- }
-
MethodNode abstractMethodNode = ClassHelper.findSAM(redirect);
-
String abstractMethodDesc = createMethodDescriptor(abstractMethodNode);
ClassNode classNode = controller.getClassNode();
- boolean isInterface = classNode.isInterface();
-
Expression typeOrTargetRef = methodReferenceExpression.getExpression();
- ClassNode typeOrTargetRefType = typeOrTargetRef.getType();
- // GRECLIPSE add -- GROOVY-9762
- if (!isClassExpr(typeOrTargetRef)) {
- typeOrTargetRefType = controller.getTypeChooser().resolveType(typeOrTargetRef, classNode);
- }
- // GRECLIPSE end
- String methodRefName = methodReferenceExpression.getMethodName().getText();
+ boolean isClassExpression = (typeOrTargetRef instanceof ClassExpression);
+ ClassNode typeOrTargetRefType = isClassExpression ? typeOrTargetRef.getType()
+ : controller.getTypeChooser().resolveType(typeOrTargetRef, classNode);
ClassNode[] methodReferenceParamTypes = methodReferenceExpression.getNodeMetaData(CLOSURE_ARGUMENTS);
Parameter[] parametersWithExactType = createParametersWithExactType(abstractMethodNode, methodReferenceParamTypes);
+ String methodRefName = methodReferenceExpression.getMethodName().getText();
boolean isConstructorReference = isConstructorReference(methodRefName);
MethodNode methodRefMethod;
@@ -116,8 +104,8 @@ public void writeMethodReferenceExpression(MethodReferenceExpression methodRefer
methodRefName = genSyntheticMethodNameForConstructorReference();
methodRefMethod = addSyntheticMethodForConstructorReference(methodRefName, typeOrTargetRefType, parametersWithExactType);
} else {
- // TODO move the `findMethodRefMethod` and checking to `StaticTypeCheckingVisitor`
- methodRefMethod = findMethodRefMethod(methodRefName, parametersWithExactType, typeOrTargetRef);
+ // TODO: move the findMethodRefMethod and checking to StaticTypeCheckingVisitor
+ methodRefMethod = findMethodRefMethod(methodRefName, parametersWithExactType, typeOrTargetRef, typeOrTargetRefType);
}
validate(methodReferenceExpression, typeOrTargetRef, typeOrTargetRefType, methodRefName, parametersWithExactType, methodRefMethod);
@@ -129,77 +117,65 @@ public void writeMethodReferenceExpression(MethodReferenceExpression methodRefer
methodRefMethod = addSyntheticMethodForDGSM(methodRefMethod);
}
- ClassExpression classExpression = new ClassExpression(methodRefMethod.getDeclaringClass());
+ typeOrTargetRefType = methodRefMethod.getDeclaringClass();
+ Expression classExpression = classX(typeOrTargetRefType);
classExpression.setSourcePosition(typeOrTargetRef);
typeOrTargetRef = classExpression;
- typeOrTargetRefType = typeOrTargetRef.getType();
}
methodRefMethod.putNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE, parametersWithExactType);
- MethodVisitor mv = controller.getMethodVisitor();
- boolean isClassExpr = isClassExpr(typeOrTargetRef);
- if (!isClassExpr) {
+ if (!isClassExpression) {
if (isConstructorReference) {
- // TODO move the checking code to the Parrot parser
+ // TODO: move the checking code to the parser
addFatalError("Constructor reference must be className::new", methodReferenceExpression);
- }
-
- if (methodRefMethod.isStatic()) {
- ClassExpression classExpression = new ClassExpression(typeOrTargetRefType);
+ } else if (methodRefMethod.isStatic()) {
+ ClassExpression classExpression = classX(typeOrTargetRefType);
classExpression.setSourcePosition(typeOrTargetRef);
typeOrTargetRef = classExpression;
- isClassExpr = true;
- }
-
- if (!isClassExpr) {
+ isClassExpression = true;
+ } else {
typeOrTargetRef.visit(controller.getAcg());
}
}
- mv.visitInvokeDynamicInsn(
+ controller.getMethodVisitor().visitInvokeDynamicInsn(
abstractMethodNode.getName(),
createAbstractMethodDesc(functionalInterfaceType, typeOrTargetRef),
- createBootstrapMethod(isInterface, false),
+ createBootstrapMethod(classNode.isInterface(), false),
createBootstrapMethodArguments(
abstractMethodDesc,
methodRefMethod.isStatic() || isConstructorReference ? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL,
isConstructorReference ? controller.getClassNode() : typeOrTargetRefType,
- methodRefMethod, false)
+ methodRefMethod,
+ false
+ )
);
- if (isClassExpr) {
+ if (isClassExpression) {
controller.getOperandStack().push(redirect);
} else {
controller.getOperandStack().replace(redirect, 1);
}
}
- private void validate(MethodReferenceExpression methodReferenceExpression, Expression typeOrTargetRef, ClassNode typeOrTargetRefType, String methodRefName, Parameter[] parametersWithExactType, MethodNode methodRefMethod) {
- if (null == methodRefMethod) {
+ private void validate(final MethodReferenceExpression methodReferenceExpression, final Expression typeOrTargetRef, final ClassNode typeOrTargetRefType, final String methodRefName, final Parameter[] parametersWithExactType, final MethodNode methodRefMethod) {
+ if (methodRefMethod == null) {
addFatalError("Failed to find the expected method["
+ methodRefName + "("
+ Arrays.stream(parametersWithExactType)
.map(e -> e.getType().getName())
.collect(Collectors.joining(","))
- + ")] in the type[" + typeOrTargetRefType.getName() + "]", methodReferenceExpression);
- }
-
- if (parametersWithExactType.length > 0 && isTypeReferingInstanceMethod(typeOrTargetRef, methodRefMethod)) {
- Parameter firstParameter = parametersWithExactType[0];
- Class> typeOrTargetClass = typeOrTargetRef.getType().getTypeClass();
- Class> firstParameterClass = firstParameter.getType().getTypeClass();
- if (!typeOrTargetClass.isAssignableFrom(firstParameterClass)) {
- throw new RuntimeParserException("Invalid receiver type: " + firstParameterClass + " is not compatible with " + typeOrTargetClass, typeOrTargetRef);
+ + ")] in the type[" + typeOrTargetRefType.getText() + "]", methodReferenceExpression);
+ } else if (parametersWithExactType.length > 0 && isTypeReferingInstanceMethod(typeOrTargetRef, methodRefMethod)) {
+ ClassNode firstParameterType = parametersWithExactType[0].getType();
+ if (!isAssignableTo(firstParameterType, typeOrTargetRefType)) {
+ throw new RuntimeParserException("Invalid receiver type: " + firstParameterType.getText() + " is not compatible with " + typeOrTargetRefType.getText(), typeOrTargetRef);
}
}
}
- private static boolean isExtensionMethod(MethodNode methodRefMethod) {
- return methodRefMethod instanceof ExtensionMethodNode;
- }
-
- private MethodNode addSyntheticMethodForDGSM(MethodNode mn) {
+ private MethodNode addSyntheticMethodForDGSM(final MethodNode mn) {
Parameter[] parameters = removeFirstParameter(mn.getParameters());
ArgumentListExpression args = args(parameters);
args.getExpressions().add(0, ConstantExpression.NULL);
@@ -212,7 +188,7 @@ private MethodNode addSyntheticMethodForDGSM(MethodNode mn) {
ClassNode.EMPTY_ARRAY,
block(
returnS(
- callX(new ClassExpression(mn.getDeclaringClass()), mn.getName(), args)
+ callX(classX(mn.getDeclaringClass()), mn.getName(), args)
)
)
);
@@ -223,7 +199,7 @@ private MethodNode addSyntheticMethodForDGSM(MethodNode mn) {
return syntheticMethodNode;
}
- private MethodNode addSyntheticMethodForConstructorReference(String syntheticMethodName, ClassNode returnType, Parameter[] parametersWithExactType) {
+ private MethodNode addSyntheticMethodForConstructorReference(final String syntheticMethodName, final ClassNode returnType, final Parameter[] parametersWithExactType) {
ArgumentListExpression ctorArgs = args(parametersWithExactType);
MethodNode syntheticMethodNode = controller.getClassNode().addSyntheticMethod(
@@ -257,35 +233,23 @@ private String genSyntheticMethodNameForConstructorReference() {
return controller.getContext().getNextConstructorReferenceSyntheticMethodName(controller.getMethodNode());
}
- private boolean isConstructorReference(String methodRefName) {
- return "new".equals(methodRefName);
- }
-
- private static boolean isClassExpr(Expression methodRef) {
- return methodRef instanceof ClassExpression;
- }
+ private String createAbstractMethodDesc(final ClassNode functionalInterfaceType, final Expression methodRef) {
+ List methodReferenceSharedVariableList = new ArrayList<>();
- private String createAbstractMethodDesc(ClassNode functionalInterfaceType, Expression methodRef) {
- List methodReferenceSharedVariableList = new LinkedList<>();
-
- if (!(isClassExpr(methodRef))) {
- ClassNode methodRefTargetType = methodRef.getType();
- prependParameter(methodReferenceSharedVariableList, METHODREF_EXPR_INSTANCE, methodRefTargetType);
+ if (!(methodRef instanceof ClassExpression)) {
+ prependParameter(methodReferenceSharedVariableList, METHODREF_EXPR_INSTANCE,
+ controller.getTypeChooser().resolveType(methodRef, controller.getClassNode()));
}
return BytecodeHelper.getMethodDescriptor(functionalInterfaceType.redirect(), methodReferenceSharedVariableList.toArray(Parameter.EMPTY_ARRAY));
}
- private Parameter[] createParametersWithExactType(MethodNode abstractMethodNode, ClassNode[] inferredParameterTypes) {
+ private Parameter[] createParametersWithExactType(final MethodNode abstractMethodNode, final ClassNode[] inferredParameterTypes) {
Parameter[] originalParameters = abstractMethodNode.getParameters();
-
- // We MUST clone the parameters to avoid impacting the original parameter type of SAM
+ // MUST clone the parameters to avoid impacting the original parameter type of SAM
Parameter[] parameters = GeneralUtils.cloneParams(originalParameters);
- if (parameters == null) {
- parameters = Parameter.EMPTY_ARRAY;
- }
- for (int i = 0; i < parameters.length; i++) {
+ for (int i = 0, n = parameters.length; i < n; i += 1) {
Parameter parameter = parameters[i];
ClassNode parameterType = parameter.getType();
ClassNode inferredType = inferredParameterTypes[i];
@@ -303,60 +267,61 @@ private Parameter[] createParametersWithExactType(MethodNode abstractMethodNode,
return parameters;
}
- private MethodNode findMethodRefMethod(String methodRefName, Parameter[] abstractMethodParameters, Expression typeOrTargetRef) {
- ClassNode typeOrTargetRefType = typeOrTargetRef.getType();
- // GRECLIPSE add -- GROOVY-9762
- if (!isClassExpr(typeOrTargetRef)) {
- typeOrTargetRefType = controller.getTypeChooser().resolveType(typeOrTargetRef, controller.getClassNode());
- }
- // GRECLIPSE end
- List methodNodeList = typeOrTargetRefType.getMethods(methodRefName);
- Set dgmMethodNodeSet = findDGMMethodsForClassNode(controller.getSourceUnit().getClassLoader(), typeOrTargetRefType, methodRefName);
-
- List allMethodNodeList = new LinkedList<>(methodNodeList);
- allMethodNodeList.addAll(dgmMethodNodeSet);
+ private MethodNode findMethodRefMethod(final String methodName, final Parameter[] samParameters, final Expression typeOrTargetRef, final ClassNode typeOrTargetRefType) {
+ List methods = findVisibleMethods(methodName, typeOrTargetRefType);
- ClassNode classNode = controller.getClassNode();
+ return chooseMethodRefMethodCandidate(typeOrTargetRef, methods.stream().filter(method -> {
+ Parameter[] parameters = method.getParameters();
+ if (isTypeReferingInstanceMethod(typeOrTargetRef, method)) {
+ // there is an implicit parameter for "String::length"
+ ClassNode firstParamType = method.getDeclaringClass();
- List candidates = new LinkedList<>();
- for (MethodNode mn : filterMethodsByVisibility(allMethodNodeList, classNode)) {
- Parameter[] parameters = abstractMethodParameters;
- if (isTypeReferingInstanceMethod(typeOrTargetRef, mn)) {
- if (0 == abstractMethodParameters.length) {
- continue;
- }
+ int n = parameters.length;
+ Parameter[] plusOne = new Parameter[n + 1];
+ plusOne[0] = new Parameter(firstParamType, "");
+ System.arraycopy(parameters, 0, plusOne, 1, n);
- parameters = removeFirstParameter(abstractMethodParameters);
+ parameters = plusOne;
}
+ return parametersCompatible(samParameters, parameters);
+ }).collect(Collectors.toList()));
+ }
- Parameter[] methodParameters;
- if (isExtensionMethod(mn)) {
- methodParameters = removeFirstParameter(((ExtensionMethodNode) mn).getExtensionMethodNode().getParameters());
- } else {
- methodParameters = mn.getParameters();
- }
+ private List findVisibleMethods(final String name, final ClassNode type) {
+ List methods = type.getMethods(name);
+ methods.addAll(findDGMMethodsForClassNode(controller.getSourceUnit().getClassLoader(), type, name));
+ methods = filterMethodsByVisibility(methods, controller.getClassNode());
+ return methods;
+ }
- if (ParameterUtils.parametersCompatible(parameters, methodParameters)) {
- candidates.add(mn);
- }
- }
+ private void addFatalError(final String msg, final ASTNode node) {
+ controller.getSourceUnit().addFatalError(msg, node);
+ }
- return chooseMethodRefMethodCandidate(typeOrTargetRef, candidates);
+ //--------------------------------------------------------------------------
+
+ private static boolean isConstructorReference(final String methodRefName) {
+ return "new".equals(methodRefName);
}
- private static Parameter[] removeFirstParameter(Parameter[] parameters) {
- return Arrays.stream(parameters).skip(1).toArray(Parameter[]::new);
+ private static boolean isExtensionMethod(final MethodNode methodRefMethod) {
+ return (methodRefMethod instanceof ExtensionMethodNode);
}
- private static boolean isTypeReferingInstanceMethod(Expression typeOrTargetRef, MethodNode mn) { // class::instanceMethod
- return (!mn.isStatic() || (isExtensionMethod(mn) && !((ExtensionMethodNode) mn).isStaticExtension()))
- && isClassExpr(typeOrTargetRef);
+ private static boolean isTypeReferingInstanceMethod(final Expression typeOrTargetRef, final MethodNode mn) {
+ // class::instanceMethod
+ return (typeOrTargetRef instanceof ClassExpression) && ((mn != null && !mn.isStatic())
+ || (isExtensionMethod(mn) && !((ExtensionMethodNode) mn).isStaticExtension()));
+ }
+
+ private static Parameter[] removeFirstParameter(final Parameter[] parameters) {
+ return Arrays.copyOfRange(parameters, 1, parameters.length);
}
/**
* Choose the best method node for method reference.
*/
- private MethodNode chooseMethodRefMethodCandidate(Expression methodRef, List candidates) {
+ private static MethodNode chooseMethodRefMethodCandidate(final Expression methodRef, final List candidates) {
if (1 == candidates.size()) return candidates.get(0);
return candidates.stream()
@@ -366,22 +331,19 @@ private MethodNode chooseMethodRefMethodCandidate(Expression methodRef, Listpublic
+ public static boolean isAssignableTo(ClassNode type, ClassNode toBeAssignedTo) {
if (type == toBeAssignedTo || type == UNKNOWN_PARAMETER_TYPE) return true;
if (isPrimitiveType(type)) type = getWrapper(type);
if (isPrimitiveType(toBeAssignedTo)) toBeAssignedTo = getWrapper(toBeAssignedTo);