@@ -1679,7 +1679,7 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
16791679 ClassNode receiverType = receiver .getType ();
16801680
16811681 if (receiverType .isArray () && "length" .equals (propertyName )) {
1682- // GRECLIPSE edit -- GROOVY-5450
1682+ // GRECLIPSE add -- GROOVY-5450
16831683 pexp .putNodeMetaData (READONLY_PROPERTY , Boolean .TRUE );
16841684 // GRECLIPSE end
16851685 storeType (pexp , int_TYPE );
@@ -3222,35 +3222,34 @@ private void processNamedParam(final AnnotationNode namedParam, final Map<Object
32223222 }
32233223
32243224 /**
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.
32303232 *
32313233 * @param receiver
32323234 * @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}
32363238 */
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 );
32393241 if (annotations != null && !annotations .isEmpty ()) {
32403242 for (AnnotationNode annotation : annotations ) {
32413243 Expression hintClass = annotation .getMember ("value" );
3242- Expression options = annotation .getMember ("options" );
3243- Expression resolverClass = annotation .getMember ("conflictResolutionStrategy" );
32443244 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 );
32463248 }
32473249 }
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 ());
32543253
32553254 if (typeParameters != null ) {
32563255 boolean typeParametersResolved = false ;
@@ -3273,13 +3272,21 @@ protected void inferClosureParameterTypes(final ClassNode receiver, final Expres
32733272 }
32743273 if (!typeParametersResolved ) {
32753274 // check for implicit type arguments
3276- int i = -1 ; Parameter [] p = selectedMethod .getParameters ();
3275+ int i = -1 ; Parameter [] p = method .getParameters ();
32773276 for (Expression argument : (ArgumentListExpression ) arguments ) { i += 1 ;
3278- if (argument instanceof ClosureExpression || isNullConstant (argument )) continue ;
3277+ if (isNullConstant (argument )) continue ;
32793278
32803279 ClassNode pType = p [Math .min (i , p .length - 1 )].getType ();
32813280 Map <GenericsTypeName , GenericsType > gc = new HashMap <>();
32823281 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+ }
32833290
32843291 gc .forEach ((key , gt ) -> {
32853292 for (GenericsType tp : typeParameters ) {
@@ -3297,7 +3304,7 @@ protected void inferClosureParameterTypes(final ClassNode receiver, final Expres
32973304 }
32983305 }
32993306
3300- ClassNode [] samParamTypes = GenericsUtils .parameterizeSAM (applyGenericsContext (context , param .getType ())).getV1 ();
3307+ ClassNode [] samParamTypes = GenericsUtils .parameterizeSAM (applyGenericsContext (context , target .getType ())).getV1 ();
33013308
33023309 ClassNode [] paramTypes = expression .getNodeMetaData (CLOSURE_ARGUMENTS );
33033310 if (paramTypes == null ) {
@@ -3308,142 +3315,14 @@ protected void inferClosureParameterTypes(final ClassNode receiver, final Expres
33083315 } else if ((n = p .length ) == 0 ) {
33093316 // implicit parameter(s)
33103317 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 );
33243320 }
33253321 expression .putNodeMetaData (CLOSURE_ARGUMENTS , paramTypes );
33263322 }
3327- // GRECLIPSE end
33283323 }
33293324 }
33303325
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-
34473326 private List <ClassNode []> getSignaturesFromHint (final ClosureExpression expression , final MethodNode selectedMethod , final Expression hintClass , final Expression options ) {
34483327 // initialize hints
34493328 List <ClassNode []> closureSignatures ;
@@ -3507,25 +3386,24 @@ private void doInferClosureParameterTypes(final ClassNode receiver, final Expres
35073386 }
35083387 }
35093388 if (candidates .size () > 1 ) {
3510- Iterator <ClassNode []> candIt = candidates .iterator ();
3511- while (candIt .hasNext ()) {
3389+ for (Iterator <ClassNode []> candIt = candidates .iterator (); candIt .hasNext (); ) {
35123390 ClassNode [] inferred = candIt .next ();
35133391 for (int i = 0 , n = closureParams .length ; i < n ; i += 1 ) {
35143392 Parameter closureParam = closureParams [i ];
3515- ClassNode originType = closureParam .getOriginType ();
3393+ ClassNode declaredType = closureParam .getOriginType ();
35163394 ClassNode inferredType ;
3517- if (i < inferred .length - 1 || inferred .length == closureParams . length ) {
3395+ if (i < inferred .length - 1 || inferred .length == n ) {
35183396 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 ();
35233401 } else {
35243402 candIt .remove ();
35253403 continue ;
35263404 }
35273405 }
3528- if (!typeCheckMethodArgumentWithGenerics (originType , inferredType , i == (n - 1 ))) {
3406+ if (!typeCheckMethodArgumentWithGenerics (declaredType , inferredType , i == (n - 1 ))) {
35293407 candIt .remove ();
35303408 }
35313409 }
@@ -3544,24 +3422,21 @@ private void doInferClosureParameterTypes(final ClassNode receiver, final Expres
35443422 } else {
35453423 for (int i = 0 , n = closureParams .length ; i < n ; i += 1 ) {
35463424 Parameter closureParam = closureParams [i ];
3547- ClassNode originType = closureParam .getOriginType ();
3425+ ClassNode declaredType = closureParam .getOriginType ();
35483426 ClassNode inferredType = OBJECT_TYPE ;
3549- if (i < inferred .length - 1 || inferred .length == closureParams . length ) {
3427+ if (i < inferred .length - 1 || inferred .length == n ) {
35503428 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 ();
35553433 } 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 );
35573435 }
35583436 }
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 ());
35633439 }
3564-
35653440 typeCheckingContext .controlStructureVariables .put (closureParam , inferredType );
35663441 }
35673442 }
0 commit comments