@@ -2867,8 +2867,12 @@ public void visitStaticMethodCallExpression(final StaticMethodCallExpression cal
28672867 for (Receiver <String > currentReceiver : receivers ) {
28682868 mn = findMethod (currentReceiver .getType (), name , args );
28692869 if (!mn .isEmpty ()) {
2870- if (mn .size () == 1 )
2870+ if (mn .size () == 1 ) {
2871+ // GRECLIPSE add -- GROOVY-8961, GROOVY-9734, GROOVY-9915
2872+ resolvePlaceholdersFromImplicitTypeHints (args , argumentList , mn .get (0 ));
2873+ // GRECLIPSE end
28712874 typeCheckMethodsWithGenericsOrFail (currentReceiver .getType (), args , mn .get (0 ), call );
2875+ }
28722876 chosenReceiver = currentReceiver ;
28732877 break ;
28742878 }
@@ -3768,36 +3772,7 @@ public void visitMethodCallExpression(MethodCallExpression call) {
37683772 }
37693773 }
37703774 // GRECLIPSE add -- GROOVY-8961, GROOVY-9734
3771- for (int i = 0 , n = args .length ; i < n ; i += 1 ) {
3772- Expression a = argumentList .getExpression (i ); ClassNode at = args [i ];
3773- if (a instanceof MethodCallExpression && !((MethodCallExpression ) a ).isUsingGenerics () && isUsingUncheckedGenerics (at )) {
3774- // try to resolve unresolved placeholders in argument type using parameter type
3775-
3776- MethodNode aNode = a .getNodeMetaData (StaticTypesMarker .DIRECT_METHOD_CALL_TARGET );
3777- if (aNode == null || aNode .getGenericsTypes () == null ) continue ;
3778-
3779- int np = directMethodCallCandidate .getParameters ().length ;
3780- Parameter p = directMethodCallCandidate .getParameters ()[Math .min (i , np )];
3781- ClassNode pt = getType (p ); if (i >= (np - 1 ) && pt .isArray () && !at .isArray ()) pt = pt .getComponentType ();
3782-
3783- Map <GenericsTypeName , GenericsType > source = extractPlaceholders (at );
3784- Map <GenericsTypeName , GenericsType > target = extractPlaceholders (pt );
3785- Map <GenericsTypeName , GenericsType > linked = new HashMap <>();
3786-
3787- // connect E:T from source to E:Type from target
3788- for (GenericsType placeholder : aNode .getGenericsTypes ()) {
3789- for (Map .Entry <GenericsTypeName , GenericsType > e : source .entrySet ()) {
3790- if (e .getValue () == placeholder ) {
3791- Optional .ofNullable (target .get (e .getKey ()))
3792- .filter (gt -> isAssignableTo (gt .getType (), placeholder .getType ()))
3793- .ifPresent (gt -> linked .put (new GenericsTypeName (placeholder .getName ()), gt ));
3794- break ;
3795- }
3796- }
3797- }
3798- args [i ] = applyGenericsContext (linked , at );
3799- }
3800- }
3775+ resolvePlaceholdersFromImplicitTypeHints (args , argumentList , directMethodCallCandidate );
38013776 // GRECLIPSE end
38023777 if (typeCheckMethodsWithGenericsOrFail (chosenReceiver .getType (), args , directMethodCallCandidate , call )) {
38033778 returnType = adjustWithTraits (directMethodCallCandidate , chosenReceiver .getType (), args , returnType );
@@ -5575,6 +5550,53 @@ private static void resolvePlaceholdersFromExplicitTypeHints(final MethodNode me
55755550 }
55765551 }
55775552
5553+ /**
5554+ * Given method call like "m(Collections.emptyList())", the type of the call
5555+ * argument is {@code List<T>} without explicit type arguments. Knowning the
5556+ * method target of "m", {@code T} could be resolved.
5557+ */
5558+ private static void resolvePlaceholdersFromImplicitTypeHints (final ClassNode [] actuals , final ArgumentListExpression argumentList , final MethodNode inferredMethod ) {
5559+ for (int i = 0 , n = actuals .length ; i < n ; i += 1 ) {
5560+ // check for method call with known target
5561+ Expression a = argumentList .getExpression (i );
5562+ if (!(a instanceof MethodCallExpression )) continue ;
5563+ if (((MethodCallExpression ) a ).isUsingGenerics ()) continue ;
5564+ MethodNode aNode = a .getNodeMetaData (StaticTypesMarker .DIRECT_METHOD_CALL_TARGET );
5565+ if (aNode == null || aNode .getGenericsTypes () == null ) continue ;
5566+
5567+ // and unknown generics
5568+ ClassNode at = actuals [i ];
5569+ if (!GenericsUtils .hasUnresolvedGenerics (at )) continue ;
5570+
5571+ int np = inferredMethod .getParameters ().length ;
5572+ Parameter p = inferredMethod .getParameters ()[Math .min (i , np - 1 )];
5573+
5574+ ClassNode pt = p .getOriginType ();
5575+ if (i >= (np - 1 ) && pt .isArray () && !at .isArray ()) pt = pt .getComponentType ();
5576+
5577+ // try to resolve placeholder(s) in argument type using parameter type
5578+
5579+ Map <GenericsTypeName , GenericsType > linked = new HashMap <>();
5580+ Map <GenericsTypeName , GenericsType > source = GenericsUtils .extractPlaceholders (at );
5581+ Map <GenericsTypeName , GenericsType > target = GenericsUtils .extractPlaceholders (pt );
5582+
5583+ // connect E:T from source to E:Type from target
5584+ for (GenericsType placeholder : aNode .getGenericsTypes ()) {
5585+ for (Map .Entry <GenericsTypeName , GenericsType > e : source .entrySet ()) {
5586+ if (e .getValue () == placeholder ) {
5587+ Optional .ofNullable (target .get (e .getKey ()))
5588+ // skip "f(g())" for "f(T<String>)" and "<U extends Number> U g()"
5589+ .filter (gt -> isAssignableTo (gt .getType (), placeholder .getType ()))
5590+ .ifPresent (gt -> linked .put (new GenericsTypeName (placeholder .getName ()), gt ));
5591+ break ;
5592+ }
5593+ }
5594+ }
5595+
5596+ actuals [i ] = applyGenericsContext (linked , at );
5597+ }
5598+ }
5599+
55785600 private static void extractGenericsConnectionsForSuperClassAndInterfaces (final Map <GenericsTypeName , GenericsType > resolvedPlaceholders , final Map <GenericsTypeName , GenericsType > connections ) {
55795601 for (GenericsType value : new HashSet <GenericsType >(connections .values ())) {
55805602 if (!value .isPlaceholder () && !value .isWildcard ()) {
0 commit comments