132132import java .util .Set ;
133133import java .util .concurrent .atomic .AtomicLong ;
134134import java .util .concurrent .atomic .AtomicReference ;
135+ import java .util .function .Supplier ;
135136
136137import static org .apache .groovy .ast .tools .ClassNodeUtils .samePackageName ;
137138import static org .codehaus .groovy .ast .ClassHelper .BigDecimal_TYPE ;
@@ -2634,6 +2635,7 @@ && getType(nameExpr).equals(STRING_TYPE)) {
26342635 .reduce (WideningCategories ::lowestUpperBound )
26352636 .filter (returnType -> !returnType .equals (OBJECT_TYPE ))
26362637 .ifPresent (returnType -> storeType (expression , wrapClosureType (returnType )));
2638+ expression .setNodeMetaData (MethodNode .class , candidates ); // GROOVY-9803
26372639 }
26382640 }
26392641 }
@@ -5519,12 +5521,23 @@ protected ClassNode inferReturnTypeGenerics(
55195521 actualType = actualType .getComponentType ();
55205522 }
55215523 if (isUsingGenericsOrIsArrayUsingGenerics (type )) {
5524+ /* GRECLIPSE edit -- GROOVY-9803
55225525 if (implementsInterfaceOrIsSubclassOf(actualType, CLOSURE_TYPE) &&
55235526 isSAMType(type)) {
55245527 // implicit closure coercion in action!
55255528 Map<GenericsTypeName, GenericsType> pholders = applyGenericsContextToParameterClass(resolvedPlaceholders, type);
55265529 actualType = convertClosureTypeToSAMType(expressions.get(i), actualType, type, pholders);
55275530 }
5531+ */
5532+ if (actualType .isDerivedFrom (CLOSURE_TYPE )) {
5533+ MethodNode sam = findSAM (type );
5534+ if (sam != null ) { // implicit closure coercion in action!
5535+ actualType = !type .isUsingGenerics () ? type
5536+ : convertClosureTypeToSAMType (expressions .get (i ), actualType , sam , type ,
5537+ applyGenericsContextToParameterClass (resolvedPlaceholders , type ));
5538+ }
5539+ }
5540+ // GRECLIPSE end
55285541 if (isVargs && lastArg && actualType .isArray ()) {
55295542 actualType = actualType .getComponentType ();
55305543 }
@@ -5600,6 +5613,48 @@ private static void extractGenericsConnectionsForSuperClassAndInterfaces(final M
56005613 }
56015614 }
56025615
5616+ // GRECLIPSE add -- GROOVY-9803
5617+ private static MethodNode chooseMethod (final MethodPointerExpression source , final Supplier <ClassNode []> samSignature ) {
5618+ List <MethodNode > options = source .getNodeMetaData (MethodNode .class );
5619+ if (options == null || options .isEmpty ()) {
5620+ return null ;
5621+ }
5622+
5623+ ClassNode [] paramTypes = samSignature .get ();
5624+ return options .stream ().filter ((MethodNode option ) -> {
5625+ ClassNode [] types = collateMethodReferenceParameterTypes (source , option );
5626+ if (types .length == paramTypes .length ) {
5627+ for (int i = 0 , n = types .length ; i < n ; i += 1 ) {
5628+ if (!types [i ].isGenericsPlaceHolder () && !isAssignableTo (types [i ], paramTypes [i ])) {
5629+ return false ;
5630+ }
5631+ }
5632+ return true ;
5633+ }
5634+ return false ;
5635+ }).findFirst ().orElse (null ); // TODO: order matches by param distance
5636+ }
5637+
5638+ private static ClassNode [] collateMethodReferenceParameterTypes (final MethodPointerExpression source , final MethodNode target ) {
5639+ Parameter [] params ;
5640+
5641+ if (target instanceof ExtensionMethodNode && !((ExtensionMethodNode ) target ).isStaticExtension ()) {
5642+ params = ((ExtensionMethodNode ) target ).getExtensionMethodNode ().getParameters ();
5643+ } else if (!target .isStatic () && source .getExpression () instanceof ClassExpression ) {
5644+ ClassNode thisType = ((ClassExpression ) source .getExpression ()).getType ();
5645+ // there is an implicit parameter for "String::length"
5646+ int n = target .getParameters ().length ;
5647+ params = new Parameter [n + 1 ];
5648+ params [0 ] = new Parameter (thisType , "" );
5649+ System .arraycopy (target .getParameters (), 0 , params , 1 , n );
5650+ } else {
5651+ params = target .getParameters ();
5652+ }
5653+
5654+ return extractTypesFromParameters (params );
5655+ }
5656+ // GRECLIPSE end
5657+
56035658 /**
56045659 * This method will convert a closure type to the appropriate SAM type, which will be used
56055660 * to infer return type generics.
@@ -5608,6 +5663,7 @@ private static void extractGenericsConnectionsForSuperClassAndInterfaces(final M
56085663 * @param samType the type into which the closure is coerced into
56095664 * @return same SAM type, but completed with information from the closure node
56105665 */
5666+ /* GRECLIPSE edit -- GROOVY-9803
56115667 private static ClassNode convertClosureTypeToSAMType(final Expression expression, final ClassNode closureType, final ClassNode samType, final Map<GenericsTypeName, GenericsType> placeholders) {
56125668 if (!samType.isUsingGenerics()) return samType;
56135669
@@ -5626,16 +5682,44 @@ private static ClassNode convertClosureTypeToSAMType(final Expression expression
56265682 } else if (samReturnType.isGenericsPlaceHolder()) {
56275683 placeholders.put(new GenericsTypeName(samReturnType.getGenericsTypes()[0].getName()), closureType.getGenericsTypes()[0]);
56285684 }
5685+ */
5686+ private static ClassNode convertClosureTypeToSAMType (final Expression expression , final ClassNode closureType , final MethodNode sam , final ClassNode samType , final Map <GenericsTypeName , GenericsType > placeholders ) {
5687+ // use the generics information from Closure to further specify the type
5688+ if (closureType .isUsingGenerics ()) {
5689+ ClassNode closureReturnType = closureType .getGenericsTypes ()[0 ].getType ();
5690+
5691+ Parameter [] parameters = sam .getParameters ();
5692+ if (parameters .length > 0 && expression instanceof MethodPointerExpression
5693+ && isUsingUncheckedGenerics (closureReturnType )) { // needs resolve
5694+ MethodPointerExpression mp = (MethodPointerExpression ) expression ;
5695+ MethodNode mn = chooseMethod (mp , () ->
5696+ applyGenericsContext (placeholders , extractTypesFromParameters (parameters ))
5697+ );
5698+ if (mn != null ) {
5699+ ClassNode [] pTypes = collateMethodReferenceParameterTypes (mp , mn );
5700+ Map <GenericsTypeName , GenericsType > connections = new HashMap <>();
5701+ for (int i = 0 , n = parameters .length ; i < n ; i += 1 ) {
5702+ // SAM parameters should align one-for-one with the referenced method's parameters
5703+ extractGenericsConnections (connections , parameters [i ].getOriginType (), pTypes [i ]);
5704+ }
5705+ // convert the method reference's generics into the SAM's generics domain
5706+ closureReturnType = applyGenericsContext (connections , closureReturnType );
5707+ // apply known generics connections to the placeholders of the return type
5708+ closureReturnType = applyGenericsContext (placeholders , closureReturnType );
5709+ }
5710+ }
56295711
5630- // now repeat the same for each parameter given in the ClosureExpression
5631- if (expression instanceof ClosureExpression && sam .getParameters ().length > 0 ) {
5712+ // the SAM's return type exactly corresponds to the inferred closure return type
5713+ extractGenericsConnections (placeholders , closureReturnType , sam .getReturnType ());
5714+ // GRECLIPSE end
5715+ // repeat the same for each parameter given in the ClosureExpression
5716+ if (parameters .length > 0 && expression instanceof ClosureExpression ) {
56325717 List <ClassNode []> genericsToConnect = new LinkedList <ClassNode []>();
56335718 Parameter [] closureParams = ((ClosureExpression ) expression ).getParameters ();
56345719 ClassNode [] closureParamTypes = extractTypesFromParameters (closureParams );
56355720 if (expression .getNodeMetaData (StaticTypesMarker .CLOSURE_ARGUMENTS ) != null ) {
56365721 closureParamTypes = expression .getNodeMetaData (StaticTypesMarker .CLOSURE_ARGUMENTS );
56375722 }
5638- final Parameter [] parameters = sam .getParameters ();
56395723 for (int i = 0 ; i < parameters .length ; i ++) {
56405724 final Parameter parameter = parameters [i ];
56415725 if (parameter .getOriginType ().isUsingGenerics () && closureParamTypes .length > i ) {
0 commit comments