You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/12/20 16:28:18 UTC

[groovy] branch master updated: STC: uniform handling of `onMethodSelection` extension

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new e9c4abc6f1 STC: uniform handling of `onMethodSelection` extension
e9c4abc6f1 is described below

commit e9c4abc6f115a21006192969f6a0a73476e07367
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Dec 20 10:26:37 2022 -0600

    STC: uniform handling of `onMethodSelection` extension
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 129 +++++++++++----------
 1 file changed, 69 insertions(+), 60 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 88f765dda5..e6fb06d397 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2739,45 +2739,50 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         try {
             ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(call.getArguments());
 
-            boolean closuresVisited = false; // visit *after* method has been chosen
-            visitMethodCallArguments(type, argumentList, closuresVisited, null);
-            ClassNode[] args = getArgumentTypes(argumentList);
+            boolean functorsVisited = false; // visit *after* method selection
+            visitMethodCallArguments(type, argumentList, functorsVisited, null);
 
-            List<MethodNode> mn = findMethod(type, name, args);
-            if (mn.isEmpty()) {
-                mn = extension.handleMissingMethod(type, name, argumentList, args, call);
-            }
-            if (mn.isEmpty()) {
-                addNoMatchingMethodError(type, name, args, call);
-            } else {
-                mn = disambiguateMethods(mn, type, args, call);
-                if (mn.size() != 1) {
-                    addAmbiguousErrorMessage(mn, name, args, call);
+            {
+                ClassNode[] args = getArgumentTypes(argumentList);
+                List<MethodNode> opts = findMethod(type, name, args);
+                if (opts.isEmpty()) {
+                    opts = extension.handleMissingMethod(type, name, argumentList, args, call);
+                }
+                if (opts.isEmpty()) {
+                    addNoMatchingMethodError(type, name, args, call);
                 } else {
-                    MethodNode targetMethod = mn.get(0);
-                    // GROOVY-8909, GROOVY-8961, GROOVY-9734, GROOVY-9844, GROOVY-9915, et al.
-                    resolvePlaceholdersFromImplicitTypeHints(args, argumentList, targetMethod.getParameters());
-                    typeCheckMethodsWithGenericsOrFail(type, args, targetMethod, call);
-
-                    ClassNode returnType = getType(targetMethod);
-                    if (returnType.isUsingGenerics() && !returnType.isEnum()) {
-                        closuresVisited = true; // visit closure/lambda arguments with selected method
-                        visitMethodCallArguments(type, argumentList, true, targetMethod);
-
-                        ClassNode rt = inferReturnTypeGenerics(type, targetMethod, argumentList);
-                        if (rt != null && implementsInterfaceOrIsSubclassOf(rt, returnType))
-                            returnType = rt;
+                    opts = disambiguateMethods(opts, type, args, call);
+                    if (opts.size() != 1) {
+                        addAmbiguousErrorMessage(opts, name, args, call);
+                    } else {
+                        MethodNode targetMethod = opts.get(0);
+                        storeTargetMethod(call, targetMethod); // trigger onMethodSelection
+                        if (call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET) == targetMethod) {
+                            // GROOVY-8909, GROOVY-8961, GROOVY-9734, GROOVY-9844, GROOVY-9915, et al.
+                            resolvePlaceholdersFromImplicitTypeHints(args, argumentList, targetMethod.getParameters());
+                            typeCheckMethodsWithGenericsOrFail(type, args, targetMethod, call);
+
+                            ClassNode returnType = getType(targetMethod);
+                            if (isUsingGenericsOrIsArrayUsingGenerics(returnType)) {
+                                functorsVisited = true; // visit functional argument(s) with selected method
+                                visitMethodCallArguments(type, argumentList, functorsVisited, targetMethod);
+                                ClassNode rt = inferReturnTypeGenerics(type, targetMethod, argumentList);
+                                if (rt != null && implementsInterfaceOrIsSubclassOf(rt, returnType)) {
+                                    returnType = rt;
+                                }
+                            }
+                            storeType(call, returnType);
+                        }
                     }
-                    storeType(call, returnType);
-                    storeTargetMethod(call, targetMethod);
                 }
             }
 
-            if (!closuresVisited) {
-                final MethodNode targetMethod = !mn.isEmpty() ? mn.get(0) : null;
-                visitMethodCallArguments(type, argumentList, true, targetMethod);
+            // type-checking extension may have changed or cleared target method
+            MethodNode target = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
+            if (!functorsVisited) {
+                visitMethodCallArguments(type, argumentList, true, target);
             }
-            if (mn.size() == 1) { Parameter[] params = mn.get(0).getParameters();
+            if (target != null) { Parameter[] params = target.getParameters();
                 checkClosureMetadata(argumentList.getExpressions(), params);
                 checkForbiddenSpreadArgument(argumentList, params);
             } else {
@@ -2819,7 +2824,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         startMethodInference(directMethodCallCandidate, collector);
     }
 
-    protected void visitMethodCallArguments(final ClassNode receiver, final ArgumentListExpression arguments, final boolean visitClosures, final MethodNode selectedMethod) {
+    protected void visitMethodCallArguments(final ClassNode receiver, final ArgumentListExpression arguments, final boolean visitFunctors, final MethodNode selectedMethod) {
         Parameter[] parameters;
         List<Expression> expressions = new ArrayList<>();
         if (selectedMethod instanceof ExtensionMethodNode) {
@@ -2834,8 +2839,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         int nthParameter = parameters.length - 1;
         for (int i = 0; i < nExpressions; i += 1) {
             Expression expression = expressions.get(i);
-            if (visitClosures == (expression instanceof ClosureExpression || expression instanceof MethodPointerExpression)) {
-                if (visitClosures && nthParameter != -1) { // GROOVY-10636: vargs call
+            if (visitFunctors == (expression instanceof ClosureExpression || expression instanceof MethodPointerExpression)) {
+                if (visitFunctors && nthParameter != -1) { // GROOVY-10636: vargs call
                     Parameter target = parameters[Math.min(i, nthParameter)];
                     ClassNode targetType = target.getType();
                     if (targetType.isArray() && i >= nthParameter)
@@ -3443,7 +3448,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         Expression callArguments = call.getArguments();
         ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(callArguments);
 
-        // visit functional arguments *after* method has been chosen
+        // visit functional arguments *after* target method selection
         visitMethodCallArguments(receiver, argumentList, false, null);
 
         boolean isThisObjectExpression = isThisExpression(objectExpression);
@@ -3469,7 +3474,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
         try {
             ClassNode[] args = getArgumentTypes(argumentList);
-            boolean callArgsVisited = false;
+            boolean functorsVisited = false;
             if (isCallOnClosure) {
                 if (fieldNode != null) {
                     GenericsType[] genericsTypes = getType(fieldNode).getGenericsTypes();
@@ -3575,37 +3580,42 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                         }
                     }
 
-                    if (mn.size() == 1) {
-                        MethodNode targetMethodCandidate = mn.get(0);
-                        ClassNode declaringClass = targetMethodCandidate.getDeclaringClass();
+out:                if (mn.size() != 1) {
+                        addAmbiguousErrorMessage(mn, name, args, call);
+                    } else {
+                        MethodNode targetMethod = mn.get(0);
+                        // note second pass to differentiate from extension that sets type
+                        boolean mergeType = (call.getNodeMetaData(INFERRED_TYPE) != null);
+                        storeTargetMethod(call, targetMethod); // trigger onMethodSelection
+                        if (call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET) != targetMethod) {
+                            break out; // type-checking script intervention
+                        }
+                        ClassNode declaringClass = targetMethod.getDeclaringClass();
                         if (chosenReceiver == null) {
                             chosenReceiver = Receiver.make(declaringClass.getPlainNodeReference());
                         }
-                        if (!targetMethodCandidate.isStatic() && !isClassType(declaringClass) && isClassType(receiver)
+                        if (!targetMethod.isStatic() && !isClassType(declaringClass) && isClassType(receiver)
                                 && chosenReceiver.getData() == null && call.getNodeMetaData(DYNAMIC_RESOLUTION) == null) {
-                            addStaticTypeError("Non-static method " + prettyPrintTypeName(declaringClass) + "#" + targetMethodCandidate.getName() + " cannot be called from static context", call);
-                        } else if (targetMethodCandidate.isAbstract() && isSuperExpression(objectExpression)) { // GROOVY-10341
-                            String target = toMethodParametersString(targetMethodCandidate.getName(), extractTypesFromParameters(targetMethodCandidate.getParameters()));
-                            if (Traits.hasDefaultImplementation(targetMethodCandidate)) { // GROOVY-10494
+                            addStaticTypeError("Non-static method " + prettyPrintTypeName(declaringClass) + "#" + targetMethod.getName() + " cannot be called from static context", call);
+                        } else if (targetMethod.isAbstract() && isSuperExpression(objectExpression)) { // GROOVY-10341
+                            String target = toMethodParametersString(targetMethod.getName(), extractTypesFromParameters(targetMethod.getParameters()));
+                            if (Traits.hasDefaultImplementation(targetMethod)) { // GROOVY-10494
                                 addStaticTypeError("Default method " + target + " requires qualified super", call);
                             } else {
                                 addStaticTypeError("Abstract method " + target + " cannot be called directly", call);
                             }
                         }
-                        // note second pass here to differentiate from extension that sets type
-                        boolean mergeType = (call.getNodeMetaData(INFERRED_TYPE) != null);
-                        storeTargetMethod(call, targetMethodCandidate);
 
-                        visitMethodCallArguments(chosenReceiver.getType(), argumentList, true, targetMethodCandidate); callArgsVisited = true;
+                        visitMethodCallArguments(chosenReceiver.getType(), argumentList, true, targetMethod); functorsVisited = true;
 
-                        ClassNode returnType = getType(targetMethodCandidate);
+                        ClassNode returnType = getType(targetMethod);
                         if (isUsingGenericsOrIsArrayUsingGenerics(returnType)) {
-                            ClassNode irtg = inferReturnTypeGenerics(chosenReceiver.getType(), targetMethodCandidate, callArguments, call.getGenericsTypes());
+                            ClassNode irtg = inferReturnTypeGenerics(chosenReceiver.getType(), targetMethod, callArguments, call.getGenericsTypes());
                             if (irtg != null && implementsInterfaceOrIsSubclassOf(irtg, returnType))
                                 returnType = irtg;
                         }
                         // GROOVY-6091: use of "delegate" or "getDelegate()" does not make use of @DelegatesTo metadata
-                        if (targetMethodCandidate == GET_DELEGATE && typeCheckingContext.getEnclosingClosure() != null) {
+                        if (targetMethod == GET_DELEGATE && typeCheckingContext.getEnclosingClosure() != null) {
                             DelegationMetadata md = getDelegationMetadata(typeCheckingContext.getEnclosingClosure().getClosureExpression());
                             if (md != null) {
                                 returnType = md.getType();
@@ -3614,14 +3624,14 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                             }
                         }
                         // GROOVY-7106, GROOVY-7274, GROOVY-8909, GROOVY-8961, GROOVY-9734, GROOVY-9844, GROOVY-9915, et al.
-                        Parameter[] parameters = targetMethodCandidate.getParameters();
-                        if (chosenReceiver.getType().getGenericsTypes() != null && !targetMethodCandidate.isStatic() && !(targetMethodCandidate instanceof ExtensionMethodNode)) {
-                            Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(chosenReceiver.getType(), targetMethodCandidate, argumentList);
+                        Parameter[] parameters = targetMethod.getParameters();
+                        if (chosenReceiver.getType().getGenericsTypes() != null && !targetMethod.isStatic() && !(targetMethod instanceof ExtensionMethodNode)) {
+                            Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(chosenReceiver.getType(), targetMethod, argumentList);
                             parameters = Arrays.stream(parameters).map(p -> new Parameter(applyGenericsContext(context, p.getType()), p.getName())).toArray(Parameter[]::new);
                         }
                         resolvePlaceholdersFromImplicitTypeHints(args, argumentList, parameters);
 
-                        if (typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, targetMethodCandidate, call)) {
+                        if (typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, targetMethod, call)) {
                             String data = chosenReceiver.getData();
                             if (data != null) {
                                 // the method which has been chosen is supposed to be a call on delegate or owner
@@ -3630,7 +3640,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                             }
                             receiver = chosenReceiver.getType();
                             if (mergeType || call.getNodeMetaData(INFERRED_TYPE) == null)
-                                storeType(call, adjustWithTraits(targetMethodCandidate, receiver, args, returnType));
+                                storeType(call, adjustWithTraits(targetMethod, receiver, args, returnType));
 
                             if (objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isClosureSharedVariable()) {
                                 // if the object expression is a closure shared variable, we will have to perform a second pass
@@ -3639,8 +3649,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                         } else {
                             call.removeNodeMetaData(DIRECT_METHOD_CALL_TARGET);
                         }
-                    } else {
-                        addAmbiguousErrorMessage(mn, name, args, call);
                     }
                 }
             }
@@ -3652,8 +3660,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
             }
 
+            // type-checking extension may have changed or cleared target method
             MethodNode target = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
-            if (!callArgsVisited) {
+            if (!functorsVisited) {
                 visitMethodCallArguments(receiver, argumentList, true, target);
             }
             if (target != null) { Parameter[] params = target.getParameters();