@@ -1679,7 +1679,7 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
1679
1679
ClassNode receiverType = receiver .getType ();
1680
1680
1681
1681
if (receiverType .isArray () && "length" .equals (propertyName )) {
1682
- // GRECLIPSE edit -- GROOVY-5450
1682
+ // GRECLIPSE add -- GROOVY-5450
1683
1683
pexp .putNodeMetaData (READONLY_PROPERTY , Boolean .TRUE );
1684
1684
// GRECLIPSE end
1685
1685
storeType (pexp , int_TYPE );
@@ -3222,35 +3222,34 @@ private void processNamedParam(final AnnotationNode namedParam, final Map<Object
3222
3222
}
3223
3223
3224
3224
/**
3225
- * This method is responsible for performing type inference on closure argument types whenever code like this is
3226
- * found: <code>foo.collect { it.toUpperCase() }</code>.
3227
- * In this case, the type checker tries to find if the <code>collect</code> method has its {@link Closure} argument
3228
- * annotated with {@link groovy.transform.stc.ClosureParams}. If yes, then additional type inference can be performed
3229
- * and the type of <code>it</code> may be inferred.
3225
+ * Performs type inference on closure argument types whenever code like this
3226
+ * is found: <code>foo.collect { it.toUpperCase() }</code>.
3227
+ * <p>
3228
+ * In this case the type checker tries to find if the {@code collect} method
3229
+ * has its {@link Closure} argument annotated with {@link ClosureParams}. If
3230
+ * so, then additional type inference can be performed and the type of
3231
+ * {@code it} may be inferred.
3230
3232
*
3231
3233
* @param receiver
3232
3234
* @param arguments
3233
- * @param expression a closure expression for which the argument types should be inferred
3234
- * @param param the parameter where to look for a {@link groovy.transform.stc. ClosureParams} annotation.
3235
- * @param selectedMethod the method accepting a closure
3235
+ * @param expression closure or lambda expression for which the argument types should be inferred
3236
+ * @param target parameter which may provide {@link ClosureParams} annotation or SAM type
3237
+ * @param method method that declares {@code target}
3236
3238
*/
3237
- protected void inferClosureParameterTypes (final ClassNode receiver , final Expression arguments , final ClosureExpression expression , final Parameter param , final MethodNode selectedMethod ) {
3238
- List <AnnotationNode > annotations = param .getAnnotations (CLOSUREPARAMS_CLASSNODE );
3239
+ protected void inferClosureParameterTypes (final ClassNode receiver , final Expression arguments , final ClosureExpression expression , final Parameter target , final MethodNode method ) {
3240
+ List <AnnotationNode > annotations = target .getAnnotations (CLOSUREPARAMS_CLASSNODE );
3239
3241
if (annotations != null && !annotations .isEmpty ()) {
3240
3242
for (AnnotationNode annotation : annotations ) {
3241
3243
Expression hintClass = annotation .getMember ("value" );
3242
- Expression options = annotation .getMember ("options" );
3243
- Expression resolverClass = annotation .getMember ("conflictResolutionStrategy" );
3244
3244
if (hintClass instanceof ClassExpression ) {
3245
- doInferClosureParameterTypes (receiver , arguments , expression , selectedMethod , hintClass , resolverClass , options );
3245
+ Expression options = annotation .getMember ("options" );
3246
+ Expression resolverClass = annotation .getMember ("conflictResolutionStrategy" );
3247
+ doInferClosureParameterTypes (receiver , arguments , expression , method , hintClass , resolverClass , options );
3246
3248
}
3247
3249
}
3248
- } else if (isSAMType (param .getOriginType ())) {
3249
- /* GRECLIPSE edit -- GROOVY-8917, GROOVY-10047, GROOVY-10049
3250
- inferSAMType(param, receiver, selectedMethod, InvocationWriter.makeArgumentList(arguments), expression);
3251
- */
3252
- Map <GenericsTypeName , GenericsType > context = selectedMethod .isStatic () ? new HashMap <>() : extractPlaceHolders (null , receiver , getDeclaringClass (selectedMethod , arguments ));
3253
- GenericsType [] typeParameters = selectedMethod instanceof ConstructorNode ? selectedMethod .getDeclaringClass ().getGenericsTypes () : applyGenericsContext (context , selectedMethod .getGenericsTypes ());
3250
+ } else if (isSAMType (target .getOriginType ())) { // SAM-type coercion
3251
+ Map <GenericsTypeName , GenericsType > context = method .isStatic () ? new HashMap <>() : extractPlaceHolders (null , receiver , getDeclaringClass (method , arguments ));
3252
+ GenericsType [] typeParameters = method instanceof ConstructorNode ? method .getDeclaringClass ().getGenericsTypes () : applyGenericsContext (context , method .getGenericsTypes ());
3254
3253
3255
3254
if (typeParameters != null ) {
3256
3255
boolean typeParametersResolved = false ;
@@ -3273,13 +3272,21 @@ protected void inferClosureParameterTypes(final ClassNode receiver, final Expres
3273
3272
}
3274
3273
if (!typeParametersResolved ) {
3275
3274
// check for implicit type arguments
3276
- int i = -1 ; Parameter [] p = selectedMethod .getParameters ();
3275
+ int i = -1 ; Parameter [] p = method .getParameters ();
3277
3276
for (Expression argument : (ArgumentListExpression ) arguments ) { i += 1 ;
3278
- if (argument instanceof ClosureExpression || isNullConstant (argument )) continue ;
3277
+ if (isNullConstant (argument )) continue ;
3279
3278
3280
3279
ClassNode pType = p [Math .min (i , p .length - 1 )].getType ();
3281
3280
Map <GenericsTypeName , GenericsType > gc = new HashMap <>();
3282
3281
extractGenericsConnections (gc , wrapTypeIfNecessary (getType (argument )), pType );
3282
+ // GROOVY-10436: extract generics connections from closure parameter declaration(s)
3283
+ if (argument == expression || (argument instanceof ClosureExpression && isSAMType (pType ))) {
3284
+ Parameter [] q = getParametersSafe ((ClosureExpression ) argument );
3285
+ ClassNode [] r = extractTypesFromParameters (q ); // maybe typed
3286
+ ClassNode [] s = GenericsUtils .parameterizeSAM (pType ).getV1 ();
3287
+ for (int j = 0 ; j < r .length && j < s .length ; j += 1 )
3288
+ if (!q [j ].isDynamicTyped ()) extractGenericsConnections (gc , r [j ], s [j ]);
3289
+ }
3283
3290
3284
3291
gc .forEach ((key , gt ) -> {
3285
3292
for (GenericsType tp : typeParameters ) {
@@ -3297,7 +3304,7 @@ protected void inferClosureParameterTypes(final ClassNode receiver, final Expres
3297
3304
}
3298
3305
}
3299
3306
3300
- ClassNode [] samParamTypes = GenericsUtils .parameterizeSAM (applyGenericsContext (context , param .getType ())).getV1 ();
3307
+ ClassNode [] samParamTypes = GenericsUtils .parameterizeSAM (applyGenericsContext (context , target .getType ())).getV1 ();
3301
3308
3302
3309
ClassNode [] paramTypes = expression .getNodeMetaData (CLOSURE_ARGUMENTS );
3303
3310
if (paramTypes == null ) {
@@ -3308,142 +3315,14 @@ protected void inferClosureParameterTypes(final ClassNode receiver, final Expres
3308
3315
} else if ((n = p .length ) == 0 ) {
3309
3316
// implicit parameter(s)
3310
3317
paramTypes = samParamTypes ;
3311
- } else {
3312
- paramTypes = new ClassNode [n ];
3313
- for (int i = 0 ; i < n && i < samParamTypes .length ; i += 1 ) {
3314
- if (p [i ].isDynamicTyped ()) {
3315
- paramTypes [i ] = samParamTypes [i ];
3316
- } else {
3317
- ClassNode declared = p [i ].getOriginType (), inferred = samParamTypes [i ];
3318
- if (isPrimitiveType (inferred ) && getWrapper (inferred ).equals (declared ))
3319
- paramTypes [i ] = inferred ; // GROOVY-9790
3320
- else
3321
- paramTypes [i ] = declared ;
3322
- }
3323
- }
3318
+ } else { // TODO: error for length mismatch
3319
+ paramTypes = Arrays .copyOf (samParamTypes , n );
3324
3320
}
3325
3321
expression .putNodeMetaData (CLOSURE_ARGUMENTS , paramTypes );
3326
3322
}
3327
- // GRECLIPSE end
3328
3323
}
3329
3324
}
3330
3325
3331
- /**
3332
- * In a method call with SAM coercion the inference is to be understood as a
3333
- * two phase process. We have the normal method call to the target method
3334
- * with the closure argument and we have the SAM method that will be called
3335
- * inside the normal target method. To infer correctly we have to "simulate"
3336
- * this process. We know the call to the closure will be done through the SAM
3337
- * type, so the SAM type generics deliver information about the Closure. At
3338
- * the same time the SAM class is used in the target method parameter,
3339
- * providing a connection from the SAM type and the target method's class.
3340
- */
3341
- /* GRECLIPSE edit
3342
- private void inferSAMType(final Parameter param, final ClassNode receiver, final MethodNode methodWithSAMParameter, final ArgumentListExpression originalMethodCallArguments, final ClosureExpression openBlock) {
3343
- // first we try to get as much information about the declaration class through the receiver
3344
- Map<GenericsTypeName, GenericsType> targetMethodConnections = new HashMap<>();
3345
- for (ClassNode face : receiver.getAllInterfaces()) {
3346
- extractGenericsConnections(targetMethodConnections, getCorrectedClassNode(receiver, face, true), face.redirect());
3347
- }
3348
- if (!receiver.isInterface()) {
3349
- extractGenericsConnections(targetMethodConnections, receiver, receiver.redirect());
3350
- }
3351
-
3352
- // then we use the method with the SAM-type parameter to get more information about the declaration
3353
- Parameter[] parametersOfMethodContainingSAM = methodWithSAMParameter.getParameters();
3354
- for (int i = 0, n = parametersOfMethodContainingSAM.length; i < n; i += 1) {
3355
- ClassNode parameterType = parametersOfMethodContainingSAM[i].getType();
3356
- // potentially skip empty varargs
3357
- if (i == (n - 1) && i == originalMethodCallArguments.getExpressions().size() && parameterType.isArray()) {
3358
- continue;
3359
- }
3360
- Expression callArg = originalMethodCallArguments.getExpression(i);
3361
- // we look at the closure later in detail, so skip it here
3362
- if (callArg == openBlock) {
3363
- continue;
3364
- }
3365
- extractGenericsConnections(targetMethodConnections, getType(callArg), parameterType);
3366
- }
3367
-
3368
- // To make a connection to the SAM class we use that new information
3369
- // to replace the generics in the SAM type parameter of the target
3370
- // method and than that to make the connections to the SAM type generics
3371
- ClassNode paramTypeWithReceiverInformation = applyGenericsContext(targetMethodConnections, param.getOriginType());
3372
- Map<GenericsTypeName, GenericsType> samTypeConnections = new HashMap<>();
3373
- ClassNode samTypeRedirect = paramTypeWithReceiverInformation.redirect();
3374
- extractGenericsConnections(samTypeConnections, paramTypeWithReceiverInformation, samTypeRedirect);
3375
-
3376
- // should the open block provide final information we apply that
3377
- // to the corresponding parameters of the SAM type method
3378
- MethodNode abstractMethod = findSAM(samTypeRedirect);
3379
- ClassNode[] abstractMethodParamTypes = extractTypesFromParameters(abstractMethod.getParameters());
3380
- ClassNode[] blockParamTypes = openBlock.getNodeMetaData(CLOSURE_ARGUMENTS);
3381
- if (blockParamTypes == null) {
3382
- Parameter[] p = openBlock.getParameters();
3383
- if (p == null) {
3384
- // zero parameter closure e.g. { -> println 'no args' }
3385
- blockParamTypes = ClassNode.EMPTY_ARRAY;
3386
- } else if (p.length == 0 && abstractMethodParamTypes.length != 0) {
3387
- // implicit it
3388
- blockParamTypes = abstractMethodParamTypes;
3389
- } else {
3390
- blockParamTypes = new ClassNode[p.length];
3391
- for (int i = 0, n = p.length; i < n; i += 1) {
3392
- if (p[i] != null && !p[i].isDynamicTyped()) {
3393
- blockParamTypes[i] = p[i].getType();
3394
- } else {
3395
- blockParamTypes[i] = typeOrNull(abstractMethodParamTypes, i);
3396
- }
3397
- }
3398
- }
3399
- }
3400
- for (int i = 0, n = blockParamTypes.length; i < n; i += 1) {
3401
- extractGenericsConnections(samTypeConnections, blockParamTypes[i], typeOrNull(abstractMethodParamTypes, i));
3402
- }
3403
-
3404
- // finally apply the generics information to the parameters and
3405
- // store the type of parameter and block type as meta information
3406
- for (int i = 0, n = blockParamTypes.length; i < n; i += 1) {
3407
- blockParamTypes[i] = applyGenericsContext(samTypeConnections, typeOrNull(abstractMethodParamTypes, i));
3408
- }
3409
-
3410
- tryToInferUnresolvedBlockParameterType(paramTypeWithReceiverInformation, abstractMethod, blockParamTypes);
3411
-
3412
- openBlock.putNodeMetaData(CLOSURE_ARGUMENTS, blockParamTypes);
3413
- }
3414
-
3415
- private void tryToInferUnresolvedBlockParameterType(final ClassNode paramTypeWithReceiverInformation, final MethodNode methodForSAM, final ClassNode[] blockParameterTypes) {
3416
- List<Integer> indexList = new LinkedList<>();
3417
- for (int i = 0, n = blockParameterTypes.length; i < n; i += 1) {
3418
- ClassNode blockParameterType = blockParameterTypes[i];
3419
- if (blockParameterType != null && blockParameterType.isGenericsPlaceHolder()) {
3420
- indexList.add(i);
3421
- }
3422
- }
3423
-
3424
- if (!indexList.isEmpty()) {
3425
- // If the parameter type failed to resolve, try to find the parameter type through the class hierarchy
3426
- Map<GenericsType, GenericsType> genericsTypeMap = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(methodForSAM.getDeclaringClass(), paramTypeWithReceiverInformation);
3427
-
3428
- for (Integer index : indexList) {
3429
- for (Map.Entry<GenericsType, GenericsType> entry : genericsTypeMap.entrySet()) {
3430
- if (entry.getKey().getName().equals(blockParameterTypes[index].getUnresolvedName())) {
3431
- ClassNode type = entry.getValue().getType();
3432
- if (type != null && !type.isGenericsPlaceHolder()) {
3433
- blockParameterTypes[index] = type;
3434
- }
3435
- break;
3436
- }
3437
- }
3438
- }
3439
- }
3440
- }
3441
-
3442
- private static ClassNode typeOrNull(final ClassNode[] parameterTypesForSAM, final int i) {
3443
- return i < parameterTypesForSAM.length ? parameterTypesForSAM[i] : null;
3444
- }
3445
- */
3446
-
3447
3326
private List <ClassNode []> getSignaturesFromHint (final ClosureExpression expression , final MethodNode selectedMethod , final Expression hintClass , final Expression options ) {
3448
3327
// initialize hints
3449
3328
List <ClassNode []> closureSignatures ;
@@ -3507,25 +3386,24 @@ private void doInferClosureParameterTypes(final ClassNode receiver, final Expres
3507
3386
}
3508
3387
}
3509
3388
if (candidates .size () > 1 ) {
3510
- Iterator <ClassNode []> candIt = candidates .iterator ();
3511
- while (candIt .hasNext ()) {
3389
+ for (Iterator <ClassNode []> candIt = candidates .iterator (); candIt .hasNext (); ) {
3512
3390
ClassNode [] inferred = candIt .next ();
3513
3391
for (int i = 0 , n = closureParams .length ; i < n ; i += 1 ) {
3514
3392
Parameter closureParam = closureParams [i ];
3515
- ClassNode originType = closureParam .getOriginType ();
3393
+ ClassNode declaredType = closureParam .getOriginType ();
3516
3394
ClassNode inferredType ;
3517
- if (i < inferred .length - 1 || inferred .length == closureParams . length ) {
3395
+ if (i < inferred .length - 1 || inferred .length == n ) {
3518
3396
inferredType = inferred [i ];
3519
- } else { // vargs?
3520
- ClassNode lastArgInferred = inferred [inferred .length - 1 ];
3521
- if (lastArgInferred .isArray ()) {
3522
- inferredType = lastArgInferred .getComponentType ();
3397
+ } else {
3398
+ ClassNode lastInferred = inferred [inferred .length - 1 ];
3399
+ if (lastInferred .isArray ()) {
3400
+ inferredType = lastInferred .getComponentType ();
3523
3401
} else {
3524
3402
candIt .remove ();
3525
3403
continue ;
3526
3404
}
3527
3405
}
3528
- if (!typeCheckMethodArgumentWithGenerics (originType , inferredType , i == (n - 1 ))) {
3406
+ if (!typeCheckMethodArgumentWithGenerics (declaredType , inferredType , i == (n - 1 ))) {
3529
3407
candIt .remove ();
3530
3408
}
3531
3409
}
@@ -3544,24 +3422,21 @@ private void doInferClosureParameterTypes(final ClassNode receiver, final Expres
3544
3422
} else {
3545
3423
for (int i = 0 , n = closureParams .length ; i < n ; i += 1 ) {
3546
3424
Parameter closureParam = closureParams [i ];
3547
- ClassNode originType = closureParam .getOriginType ();
3425
+ ClassNode declaredType = closureParam .getOriginType ();
3548
3426
ClassNode inferredType = OBJECT_TYPE ;
3549
- if (i < inferred .length - 1 || inferred .length == closureParams . length ) {
3427
+ if (i < inferred .length - 1 || inferred .length == n ) {
3550
3428
inferredType = inferred [i ];
3551
- } else { // vargs?
3552
- ClassNode lastArgInferred = inferred [inferred .length - 1 ];
3553
- if (lastArgInferred .isArray ()) {
3554
- inferredType = lastArgInferred .getComponentType ();
3429
+ } else {
3430
+ ClassNode lastInferred = inferred [inferred .length - 1 ];
3431
+ if (lastInferred .isArray ()) {
3432
+ inferredType = lastInferred .getComponentType ();
3555
3433
} else {
3556
- addError ("Incorrect number of parameters. Expected " + inferred .length + " but found " + closureParams . length , expression );
3434
+ addError ("Incorrect number of parameters. Expected " + inferred .length + " but found " + n , expression );
3557
3435
}
3558
3436
}
3559
- boolean lastArg = i == (n - 1 );
3560
-
3561
- if (!typeCheckMethodArgumentWithGenerics (originType , inferredType , lastArg )) {
3562
- addError ("Expected parameter of type " + inferredType .toString (false ) + " but got " + originType .toString (false ), closureParam .getType ());
3437
+ if (!typeCheckMethodArgumentWithGenerics (declaredType , inferredType , i == n -1 )) {
3438
+ addError ("Expected parameter of type " + prettyPrintType (inferredType ) + " but got " + prettyPrintType (declaredType ), closureParam .getType ());
3563
3439
}
3564
-
3565
3440
typeCheckingContext .controlStructureVariables .put (closureParam , inferredType );
3566
3441
}
3567
3442
}
0 commit comments