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

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


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new 17b209cccd GROOVY-10322: STC: remove hidden generics when checking 
method arguments
17b209cccd is described below

commit 17b209cccde20ffc9570d2a5aff93318c4804934
Author: Eric Milles <[email protected]>
AuthorDate: Wed Mar 8 10:20:09 2023 -0600

    GROOVY-10322: STC: remove hidden generics when checking method arguments
    
    2_5_X backport
---
 .../transform/stc/StaticTypeCheckingSupport.java   | 120 +++++++-------
 .../transform/stc/StaticTypeCheckingVisitor.java   | 178 ++++++++++-----------
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  33 ++--
 3 files changed, 165 insertions(+), 166 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index fa911e447a..03433f68c7 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -60,6 +60,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -1105,14 +1106,28 @@ public abstract class StaticTypeCheckingSupport {
                 Person p = foo(b)
             */
 
-            ClassNode declaringClassForDistance = 
candidate.getDeclaringClass();
-            ClassNode actualReceiverForDistance = receiver != null ? receiver 
: declaringClassForDistance;
-            Map<GenericsType, GenericsType> declaringAndActualGenericsTypeMap 
= 
GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(declaringClassForDistance,
 actualReceiverForDistance);
+            ClassNode declaringClass = candidate.getDeclaringClass();
+            ClassNode actualReceiver = receiver != null ? receiver : 
declaringClass;
 
-            Parameter[] params = makeRawTypes(safeNode.getParameters(), 
declaringAndActualGenericsTypeMap);
+            Map<GenericsType, GenericsType> spec;
+            if (candidate.isStatic()) {
+                spec = Collections.emptyMap(); // none visible
+            } else {
+                spec = 
GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(declaringClass, 
actualReceiver);
+                GenericsType[] methodGenerics = candidate.getGenericsTypes();
+                if (methodGenerics != null) { // GROOVY-10322: remove hidden 
type parameters
+                    for (int i = 0, n = methodGenerics.length; i < n && 
!spec.isEmpty(); i += 1) {
+                        for (Iterator<GenericsType> it = 
spec.keySet().iterator(); it.hasNext(); ) {
+                            if 
(it.next().getName().equals(methodGenerics[i].getName())) it.remove();
+                        }
+                    }
+                }
+            }
+
+            Parameter[] params = makeRawTypes(safeNode.getParameters(), spec);
             int dist = measureParametersAndArgumentsDistance(params, safeArgs);
             if (dist >= 0) {
-                dist += getClassDistance(declaringClassForDistance, 
actualReceiverForDistance);
+                dist += getClassDistance(declaringClass, actualReceiver);
                 dist += getExtensionDistance(isExtensionMethod);
                 if (dist < bestDist) {
                     bestDist = dist;
@@ -1447,15 +1462,6 @@ public abstract class StaticTypeCheckingSupport {
         return true;
     }
 
-    static void addMethodLevelDeclaredGenerics(MethodNode method, 
Map<GenericsTypeName, GenericsType> resolvedPlaceholders) {
-        GenericsType[] generics = method.getGenericsTypes();
-        if (!method.isStatic() && !resolvedPlaceholders.isEmpty()) {
-            // GROOVY-8034: non-static method may use class generics
-            generics = applyGenericsContext(resolvedPlaceholders, generics);
-        }
-        
GenericsUtils.extractPlaceholders(GenericsUtils.makeClassSafe0(OBJECT_TYPE, 
generics), resolvedPlaceholders);
-    }
-
     protected static boolean typeCheckMethodsWithGenerics(ClassNode receiver, 
ClassNode[] arguments, MethodNode candidateMethod) {
         if (candidateMethod instanceof ExtensionMethodNode) {
             ClassNode[] realTypes = new ClassNode[arguments.length + 1];
@@ -1474,62 +1480,52 @@ public abstract class StaticTypeCheckingSupport {
         return typeCheckMethodsWithGenerics(receiver, arguments, 
candidateMethod, false);
     }
 
-    private static boolean typeCheckMethodsWithGenerics(final ClassNode 
receiver, final ClassNode[] arguments, final MethodNode candidateMethod, final 
boolean isExtensionMethod) {
-        Parameter[] parameters = candidateMethod.getParameters();
-        if (parameters.length == 0 || parameters.length > arguments.length) {
-            // this is a limitation that must be removed in a future version
-            // we cannot check generic type arguments if there are default 
parameters!
+    private static boolean typeCheckMethodsWithGenerics(final ClassNode 
receiver, final ClassNode[] argumentTypes, final MethodNode candidateMethod, 
final boolean isExtensionMethod) {
+        final Parameter[] parameters = candidateMethod.getParameters();
+        if (parameters.length == 0 || parameters.length > 
argumentTypes.length){
+            // this is a limitation that must be removed in a future version; 
we
+            // cannot check generic type arguments if there is default 
argument!
             return true;
         }
 
-        // correct receiver for inner class
+        boolean failure = false;
+        Set<GenericsTypeName> fixedPlaceHolders = Collections.emptySet();
+        Map<GenericsTypeName, GenericsType> candidateGenerics = new 
HashMap<>();
         // we assume the receiver is an instance of the declaring class of the
-        // candidate method, but findMethod returns also outer class methods
-        // for that receiver. For now we skip receiver based checks in that 
case
-        // TODO: correct generics for when receiver is to be skipped
-        boolean skipBecauseOfInnerClassNotReceiver = 
!implementsInterfaceOrIsSubclassOf(receiver, 
candidateMethod.getDeclaringClass());
-
-        // we have here different generics contexts we have to deal with.
-        // There is firstly the context given through the class, and the 
method.
-        // The method context may hide generics given through the class, but 
use
-        // the non-hidden ones.
-        Map<GenericsTypeName, GenericsType> classGTs;
-        Map<GenericsTypeName, GenericsType> resolvedMethodGenerics = new 
HashMap<>();
-        if (skipBecauseOfInnerClassNotReceiver) {
-            classGTs = Collections.emptyMap();
-        } else {
-            classGTs = GenericsUtils.extractPlaceholders(receiver);
-            addMethodLevelDeclaredGenerics(candidateMethod, 
resolvedMethodGenerics);
-            // remove hidden generics
-            for (GenericsTypeName key : resolvedMethodGenerics.keySet()) {
-                classGTs.remove(key);
+        // candidate method, but findMethod() also returns outer class methods
+        // for the receiver; for now we skip receiver-based checks in that case
+        if (implementsInterfaceOrIsSubclassOf(receiver, 
candidateMethod.getDeclaringClass())) {
+            if ("<init>".equals(candidateMethod.getName())) {
+                candidateGenerics = 
GenericsUtils.extractPlaceholders(receiver);
+                fixedPlaceHolders = new HashSet<>( candidateGenerics.keySet() 
);
+            } else {
+                failure = inferenceCheck(fixedPlaceHolders, candidateGenerics, 
candidateMethod.getDeclaringClass(), receiver, false);
+
+                GenericsType[] gts = candidateMethod.getGenericsTypes();
+                if (candidateMethod.isStatic()) {
+                    candidateGenerics.clear(); // not in scope
+                } else if (gts != null) {
+                    // first remove hidden params
+                    for (GenericsType gt : gts) {
+                        candidateGenerics.remove(new 
GenericsTypeName(gt.getName()));
+                    }
+                    // GROOVY-8034: non-static method may use class generics
+                    gts = applyGenericsContext(candidateGenerics, gts);
+                }
+                
GenericsUtils.extractPlaceholders(GenericsUtils.makeClassSafe0(OBJECT_TYPE, 
gts), candidateGenerics);
+
+                fixedPlaceHolders = 
extractResolvedPlaceHolders(candidateGenerics);
             }
-            // use the remaining information to refine given generics
-            applyGenericsConnections(classGTs, resolvedMethodGenerics);
         }
 
-        boolean failure = false;
-        // start checks with the receiver
-        if (!skipBecauseOfInnerClassNotReceiver) {
-            failure = inferenceCheck(Collections.EMPTY_SET, 
resolvedMethodGenerics, candidateMethod.getDeclaringClass(), receiver, false);
-        }
-        // the outside context parts till now define placeholder we are not 
allowed to
-        // generalize, thus we save that for later use...
-        // extension methods are special, since they set the receiver as
-        // first parameter. While we normally allow generalization for the 
first
-        // parameter, in case of an extension method we must not.
-        Set<GenericsTypeName> fixedGenericsPlaceHolders = 
extractResolvedPlaceHolders(resolvedMethodGenerics);
-        if ("<init>".equals(candidateMethod.getName())) {
-            fixedGenericsPlaceHolders.addAll(resolvedMethodGenerics.keySet());
-        }
-        for (int i = 0, n = arguments.length, nthParameter = parameters.length 
- 1; i < n; i += 1) {
-            ClassNode argumentType = arguments[i];
-            ClassNode parameterType = parameters[Math.min(i, 
nthParameter)].getOriginType();
-            failure |= inferenceCheck(fixedGenericsPlaceHolders, 
resolvedMethodGenerics, parameterType, argumentType, i >= nthParameter);
-
-            if (i == 0 && isExtensionMethod) // set real fixed generics for 
extension methods
-                fixedGenericsPlaceHolders = 
extractResolvedPlaceHolders(resolvedMethodGenerics);
+        for (int i = 0, n = argumentTypes.length, nthParameter = 
parameters.length - 1; i < n; i += 1) {
+            ClassNode argumentType = argumentTypes[i], parameterType = 
parameters[Math.min(i,nthParameter)].getOriginType();
+            failure |= inferenceCheck(fixedPlaceHolders, candidateGenerics, 
parameterType, argumentType, i >= nthParameter);
+
+            if (i == 0 && isExtensionMethod) // re-load fixed names for 
extension
+                fixedPlaceHolders = 
extractResolvedPlaceHolders(candidateGenerics);
         }
+
         return !failure;
     }
 
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 84b838358b..ffc67db72c 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -119,7 +119,6 @@ import org.objectweb.asm.Opcodes;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -225,7 +224,6 @@ import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Collec
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Matcher_TYPE;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.NUMBER_OPS;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.UNKNOWN_PARAMETER_TYPE;
-import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.addMethodLevelDeclaredGenerics;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.allParametersAndArgumentsMatchWithDefaultParams;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.applyGenericsConnections;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.applyGenericsContext;
@@ -974,7 +972,7 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
             @Override public ClassNode apply(final MethodNode method) {
                 ClassNode type = method.getParameters()[0].getOriginType();
                 if (!method.isStatic() && !(method instanceof 
ExtensionMethodNode) && GenericsUtils.hasUnresolvedGenerics(type)) {
-                    Map<GenericsTypeName, GenericsType> spec = 
extractPlaceHolders(null, setterInfo.receiverType, method.getDeclaringClass());
+                    Map<GenericsTypeName, GenericsType> spec = 
extractPlaceHolders(setterInfo.receiverType, method.getDeclaringClass());
                     type = applyGenericsContext(spec, type);
                 }
                 return type;
@@ -1834,11 +1832,9 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
         return method instanceof ExtensionMethodNode ? ((ExtensionMethodNode) 
method).isStaticExtension() : method.isStatic();
     }
 
-    private void storeWithResolve(ClassNode typeToResolve, ClassNode receiver, 
ClassNode declaringClass, boolean isStatic, PropertyExpression 
expressionToStoreOn) {
-        ClassNode type = typeToResolve;
-        if (getGenericsWithoutArray(type) != null) {
-            Map<GenericsTypeName, GenericsType> resolvedPlaceholders = 
resolvePlaceHoldersFromDeclaration(receiver, declaringClass, null, isStatic);
-            type = resolveGenericsWithContext(resolvedPlaceholders, type);
+    private void storeWithResolve(ClassNode type, final ClassNode receiver, 
final ClassNode declaringClass, final boolean isStatic, final 
PropertyExpression expressionToStoreOn) {
+        if (!isStatic && GenericsUtils.hasUnresolvedGenerics(type)) {
+            type = resolveGenericsWithContext(extractPlaceHolders(receiver, 
declaringClass), type);
         }
         storeInferredTypeForPropertyExpression(expressionToStoreOn, type);
         storeType(expressionToStoreOn, type);
@@ -2000,7 +1996,7 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
             } else {
                 componentType = inferLoopElementType(collectionType);
             }
-            if (getUnwrapper(componentType) == forLoopVariableType) {
+            if (getUnwrapper(componentType).equals(forLoopVariableType)) {
                 // prefer primitive type over boxed type
                 componentType = forLoopVariableType;
             }
@@ -2363,7 +2359,7 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
                         ctor = typeCheckMapConstructor(call, receiver, 
arguments);
                     } else {
                         if (parameters.length > 0 && 
asBoolean(receiver.getGenericsTypes())) { // GROOVY-10283, GROOVY-10316, 
GROOVY-10482, GROOVY-10624, et al.
-                            Map<GenericsTypeName, GenericsType> context = 
extractPlaceHolders(null, receiver, ctor.getDeclaringClass());
+                            Map<GenericsTypeName, GenericsType> context = 
extractPlaceHolders(receiver, ctor.getDeclaringClass());
                             parameters = parameters.clone(); for (int i = 0; i 
< parameters.length; i += 1)
                                 parameters[i] = new 
Parameter(applyGenericsContext(context, parameters[i].getType()), 
parameters[i].getName());
                         }
@@ -3062,13 +3058,17 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
 
         // First we try to get as much information about the declaration
         // class through the receiver
-        Map<GenericsTypeName, GenericsType> 
targetMethodDeclarationClassConnections = new HashMap<GenericsTypeName, 
GenericsType>();
-        extractGenericsConnections(targetMethodDeclarationClassConnections, 
receiver, receiver.redirect());
+        Map<GenericsTypeName, GenericsType> 
targetMethodDeclarationClassConnections;
+        if (methodWithSAMParameter.isStatic()) {
+            targetMethodDeclarationClassConnections = new 
HashMap<GenericsTypeName, GenericsType>();
+        } else {
+            targetMethodDeclarationClassConnections = 
extractPlaceHolders(receiver, getDeclaringClass(methodWithSAMParameter, 
originalMethodCallArguments));
+        }
         // then we use the method with the SAM parameter to get more 
information about the declaration
         Parameter[] parametersOfMethodContainingSAM = 
methodWithSAMParameter.getParameters();
-        for (int i = 0; i < parametersOfMethodContainingSAM.length; i++) {
+        for (int i = 0, n = parametersOfMethodContainingSAM.length; i < n; i 
+= 1) {
             // potentially skip empty varargs
-            if (i == parametersOfMethodContainingSAM.length - 1
+            if (i == n - 1
                     && i == originalMethodCallArguments.getExpressions().size()
                     && parametersOfMethodContainingSAM[i].getType().isArray())
                 continue;
@@ -3674,9 +3674,16 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
                                 returnType = 
typeCheckingContext.getEnclosingClassNode();
                             }
                         }
-                        // GROOVY-8961, GROOVY-9734
-                        resolvePlaceholdersFromImplicitTypeHints(args, 
argumentList, directMethodCallCandidate.getParameters());
-                        if 
(typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, 
directMethodCallCandidate, call)) {
+                        Parameter[] parameters = 
directMethodCallCandidate.getParameters();
+                        // GROOVY-7106, GROOVY-7274, GROOVY-8909, GROOVY-8961, 
GROOVY-9734, GROOVY-9844, GROOVY-9915, et al.
+                        if (chosenReceiver.getType().getGenericsTypes() != 
null && !directMethodCallCandidate.isStatic() && !(directMethodCallCandidate 
instanceof ExtensionMethodNode)) {
+                            Map<GenericsTypeName, GenericsType> context = 
extractPlaceHoldersVisibleToDeclaration(chosenReceiver.getType(), 
directMethodCallCandidate, argumentList);
+                            parameters = parameters.clone(); for (int i = 0; i 
< parameters.length; i += 1)
+                                parameters[i] = new 
Parameter(applyGenericsContext(context, parameters[i].getType()), 
parameters[i].getName());
+                        }
+                        resolvePlaceholdersFromImplicitTypeHints(args, 
argumentList, parameters);
+
+                        if 
(typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, 
directMethodCallCandidate, call)){
                             returnType = 
adjustWithTraits(directMethodCallCandidate, chosenReceiver.getType(), args, 
returnType);
 
                             storeType(call, returnType);
@@ -5039,13 +5046,12 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
             if (vexp.isSuperExpression()) return makeSuper();
             final Variable variable = vexp.getAccessedVariable();
             if (variable instanceof FieldNode) {
-                ClassNode fieldType = variable.getOriginType();
-                if (isUsingGenericsOrIsArrayUsingGenerics(fieldType)) {
-                    boolean isStatic = 
Modifier.isStatic(variable.getModifiers());
-                    ClassNode thisType = 
typeCheckingContext.getEnclosingClassNode(), declType = ((FieldNode) 
variable).getDeclaringClass();
-                    Map<GenericsTypeName, GenericsType> placeholders = 
resolvePlaceHoldersFromDeclaration(thisType, declType, null, isStatic);
-
-                    fieldType = resolveGenericsWithContext(placeholders, 
fieldType);
+                FieldNode fieldNode = (FieldNode) variable;
+                ClassNode fieldType = fieldNode.getOriginType();
+                if (!fieldNode.isStatic() && 
GenericsUtils.hasUnresolvedGenerics(fieldType)) {
+                    ClassNode declType = fieldNode.getDeclaringClass();
+                    ClassNode thisType = 
typeCheckingContext.getEnclosingClassNode();
+                    fieldType = 
resolveGenericsWithContext(extractPlaceHolders(thisType, declType), fieldType);
                 }
                 return fieldType;
             }
@@ -5328,7 +5334,7 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
         }
 
         Map<GenericsTypeName, GenericsType> context = method.isStatic() || 
method instanceof ConstructorNode
-                                            ? null : extractPlaceHolders(null, 
receiver, getDeclaringClass(method, arguments));
+                                            ? null : 
extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
         GenericsType[] methodGenericTypes = method instanceof ConstructorNode 
? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, 
method.getGenericsTypes());
 
         // 1) resolve type parameters of method
@@ -5596,111 +5602,99 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
         return resolveClassNodeGenerics(resolvedPlaceholders, 
placeholdersFromContext, currentType);
     }
 
-    private static ClassNode getDeclaringClass(MethodNode method, Expression 
arguments) {
+    private static ClassNode getDeclaringClass(MethodNode method, Expression 
argument) {
         ClassNode declaringClass = method.getDeclaringClass();
-
-        // correcting declaring class for extension methods:
-        if (arguments instanceof ArgumentListExpression) {
-            ArgumentListExpression al = (ArgumentListExpression) arguments;
-            List<Expression> list = al.getExpressions();
-            if (list.isEmpty()) return declaringClass;
-            Expression exp = list.get(0);
-            ClassNode cn = 
exp.getNodeMetaData(ExtensionMethodDeclaringClass.class);
-            if (cn != null) return cn;
+        if (argument instanceof TupleExpression) { // resolve extension method 
class
+            List<Expression> arguments = ((TupleExpression) 
argument).getExpressions();
+            if (!arguments.isEmpty()) {
+                ClassNode cn = 
arguments.get(0).getNodeMetaData(ExtensionMethodDeclaringClass.class);
+                if (cn != null)
+                    declaringClass = cn;
+            }
         }
         return declaringClass;
     }
 
-    private static Map<GenericsTypeName, GenericsType> 
resolvePlaceHoldersFromDeclaration(ClassNode receiver, ClassNode declaration, 
MethodNode method, boolean isStaticTarget) {
-        Map<GenericsTypeName, GenericsType> resolvedPlaceholders;
-        if (isStaticTarget && CLASS_Type.equals(receiver) &&
-                receiver.isUsingGenerics() &&
-                receiver.getGenericsTypes().length > 0 &&
-                !OBJECT_TYPE.equals(receiver.getGenericsTypes()[0].getType())) 
{
-            return 
resolvePlaceHoldersFromDeclaration(receiver.getGenericsTypes()[0].getType(), 
declaration, method, isStaticTarget);
-        } else {
-            resolvedPlaceholders = extractPlaceHolders(method, receiver, 
declaration);
-        }
-        return resolvedPlaceholders;
-    }
-
     private static boolean isGenericsPlaceHolderOrArrayOf(ClassNode cn) {
         if (cn.isArray()) return 
isGenericsPlaceHolderOrArrayOf(cn.getComponentType());
         return cn.isGenericsPlaceHolder();
     }
 
-    private static Map<GenericsTypeName, GenericsType> 
extractPlaceHolders(MethodNode method, ClassNode receiver, ClassNode 
declaringClass) {
-        if (declaringClass.equals(OBJECT_TYPE)) {
-            Map<GenericsTypeName, GenericsType> resolvedPlaceholders = new 
HashMap<GenericsTypeName, GenericsType>();
-            if (method != null) addMethodLevelDeclaredGenerics(method, 
resolvedPlaceholders);
-            return resolvedPlaceholders;
-        }
-
-        Map<GenericsTypeName, GenericsType> resolvedPlaceholders = null;
-        if (isPrimitiveType(receiver) && !isPrimitiveType(declaringClass)) {
-            receiver = getWrapper(receiver);
-        }
-        final List<ClassNode> queue;
+    private static Map<GenericsTypeName, GenericsType> 
extractPlaceHolders(final ClassNode receiver, final ClassNode declaringClass) {
+        Map<GenericsTypeName, GenericsType> result = null;
+        ClassNode[] todo;
         if (receiver instanceof UnionTypeClassNode) {
-            queue = Arrays.asList(((UnionTypeClassNode) 
receiver).getDelegates());
+            todo = ((UnionTypeClassNode) receiver).getDelegates();
         } else {
-            queue = Collections.singletonList(receiver);
+            todo = new ClassNode[] {!isPrimitiveType(declaringClass) ? 
wrapTypeIfNecessary(receiver) : receiver};
         }
-        for (ClassNode item : queue) {
-            ClassNode current = item;
+        for (ClassNode type : todo) {
+            ClassNode current = type;
             while (current != null) {
-                boolean continueLoop = true;
-                // extract the place holders
-                Map<GenericsTypeName, GenericsType> currentPlaceHolders = new 
HashMap<GenericsTypeName, GenericsType>();
-                if (isGenericsPlaceHolderOrArrayOf(declaringClass) || 
declaringClass.equals(current)) {
-                    extractGenericsConnections(currentPlaceHolders, current, 
declaringClass);
-                    if (method != null) addMethodLevelDeclaredGenerics(method, 
currentPlaceHolders);
-                    continueLoop = false;
+                Map<GenericsTypeName, GenericsType> placeHolders = new 
HashMap<GenericsTypeName, GenericsType>();
+                boolean currentIsDeclaring = current.equals(declaringClass) || 
isGenericsPlaceHolderOrArrayOf(declaringClass);
+                if (currentIsDeclaring) {
+                    extractGenericsConnections(placeHolders, current, 
declaringClass);
                 } else {
-                    GenericsUtils.extractPlaceholders(current, 
currentPlaceHolders);
+                    GenericsUtils.extractPlaceholders(current, placeHolders);
                 }
 
-                if (resolvedPlaceholders != null) {
-                    // merge maps
-                    Set<Map.Entry<GenericsTypeName, GenericsType>> entries = 
currentPlaceHolders.entrySet();
-                    for (Map.Entry<GenericsTypeName, GenericsType> entry : 
entries) {
+                if (result != null) { // merge maps
+                    for (Map.Entry<GenericsTypeName, GenericsType> entry : 
placeHolders.entrySet()) {
                         GenericsType gt = entry.getValue();
                         if (!gt.isPlaceholder()) continue;
-                        GenericsType referenced = resolvedPlaceholders.get(new 
GenericsTypeName(gt.getName()));
+                        GenericsType referenced = result.get(new 
GenericsTypeName(gt.getName()));
                         if (referenced == null) continue;
                         entry.setValue(referenced);
                     }
                 }
-                resolvedPlaceholders = currentPlaceHolders;
+                result = placeHolders;
 
                 // we are done if we are now in the declaring class
-                if (!continueLoop) break;
+                if (currentIsDeclaring) break;
 
                 current = getNextSuperClass(current, declaringClass);
-                if (current == null && CLASS_Type.equals(declaringClass)) {
+                if (current == null && declaringClass.equals(CLASS_Type)) {
                     // this can happen if the receiver is Class<Foo>, then
                     // the actual receiver is Foo and declaringClass is Class
                     current = declaringClass;
+                } else {
+                    current = applyGenericsContext(placeHolders, current);
                 }
             }
         }
-        if (resolvedPlaceholders == null) {
-            String descriptor = "<>";
-            if (method != null) descriptor = method.getTypeDescriptor();
-            throw new GroovyBugError(
-                    "Declaring class for method call to '" +
-                            descriptor + "' declared in " + 
declaringClass.getName() +
-                            " was not matched with found receiver " + 
receiver.getName() + "." +
-                            " This should not have happened!");
+        if (result == null) {
+            throw new GroovyBugError("Declaring class " + 
prettyPrintTypeName(declaringClass) + " was not matched with receiver " + 
prettyPrintTypeName(receiver) + ". This should not have happened!");
         }
-        return resolvedPlaceholders;
+        return result;
+    }
+
+    private static Map<GenericsTypeName, GenericsType> 
extractPlaceHoldersVisibleToDeclaration(final ClassNode receiver, final 
MethodNode method, final Expression argument) {
+        Map<GenericsTypeName, GenericsType> result;
+        if (method.isStatic()) {
+            result = new HashMap<GenericsTypeName, GenericsType>();
+        } else {
+            result = extractPlaceHolders(receiver, getDeclaringClass(method, 
argument));
+            // GROOVY-10322: class type parameters hidden by method type 
parameters
+            if (!result.isEmpty() && asBoolean(method.getGenericsTypes())) {
+                for (GenericsType tp : method.getGenericsTypes()) {
+                    result.remove(new GenericsTypeName(tp.getName()));
+                }
+            }
+        }
+        return result;
     }
 
     protected boolean typeCheckMethodsWithGenericsOrFail(final ClassNode 
receiver, final ClassNode[] arguments, final MethodNode candidateMethod, final 
Expression location) {
         if (!typeCheckMethodsWithGenerics(receiver, arguments, 
candidateMethod)) {
             Map<GenericsTypeName, GenericsType> generics = 
GenericsUtils.extractPlaceholders(receiver);
             
applyGenericsConnections(extractGenericsParameterMapOfThis(typeCheckingContext),
 generics);
-            addMethodLevelDeclaredGenerics(candidateMethod, generics);
+            GenericsType[] mgt = candidateMethod.getGenericsTypes();
+            if (!candidateMethod.isStatic() && !generics.isEmpty()){
+                mgt = applyGenericsContext(generics, mgt);
+            }
+            
GenericsUtils.extractPlaceholders(GenericsUtils.makeClassSafe0(OBJECT_TYPE,mgt),
 generics);
+
             Parameter[] parameters = candidateMethod.getParameters();
             ClassNode[] paramTypes = new ClassNode[parameters.length];
             for (int i = 0, n = parameters.length; i < n; i += 1) {
@@ -5710,7 +5704,7 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
                     return false;
                 }
             }
-            GenericsType[] mgt = candidateMethod.getGenericsTypes();
+
             addStaticTypeError("Cannot call " + (mgt == null ? "" : 
GenericsUtils.toGenericTypesString(mgt)) + receiver.toString(false) + "#" +
                     toMethodParametersString(candidateMethod.getName(), 
paramTypes) + " with arguments " + formatArgumentList(arguments), location);
             return false;
@@ -5988,7 +5982,7 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
 
         private void storePropertyType(ClassNode type, final ClassNode 
declaringClass) {
             if (declaringClass != null && 
GenericsUtils.hasUnresolvedGenerics(type)) { // GROOVY-10787
-                Map<GenericsTypeName, GenericsType> spec = 
extractPlaceHolders(null, receiverType, declaringClass);
+                Map<GenericsTypeName, GenericsType> spec = 
extractPlaceHolders(receiverType, declaringClass);
                 type = applyGenericsContext(spec, type);
             }
             // TODO: if (propertyType != null) merge types
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy 
b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 65baa46a24..d7768e04be 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2553,6 +2553,19 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase 
{
         '''
     }
 
+    void testShouldUseMethodGenericType18() {
+        if (!GroovyAssert.isAtLeastJdk('1.8')) return
+
+        assertScript '''
+            List<String> names = Arrays.stream(this.class.fields)
+                .parallel() // declared in BaseStream
+                .map { f ->
+                    f.getName()
+                }
+                .collect(java.util.stream.Collectors.toList())
+        '''
+    }
+
     // GROOVY-5516
     void testAddAllWithCollectionShouldBeAllowed() {
         assertScript '''import 
org.codehaus.groovy.transform.stc.ExtensionMethodNode
@@ -3616,7 +3629,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-10322
-    @NotYetImplemented
     void testShouldFindMethodEvenWithRepeatNames4() {
         assertScript '''
             class C<T> {
@@ -4090,20 +4102,17 @@ class GenericsSTCTest extends 
StaticTypeCheckingTestCase {
 
     // GROOVY-5839
     void testMethodShadowGenerics() {
-        shouldFailWithMessages '''
-            public class GoodCodeRed<T> {
-                Collection<GoodCodeRed<T>> attached = []
-                public <T> void attach(GoodCodeRed<T> toAttach) {
+        assertScript '''
+            class C<T> {
+                Collection<C<T>> attached = []
+                def <T> void attach(C<T> toAttach) {
                     attached.add(toAttach)
                 }
-                static void foo() {
-                    def g1 = new GoodCodeRed<Long>()
-                    def g2 = new GoodCodeRed<Integer>()
-                    g1.attach(g2);
-                }
             }
-            GoodCodeRed.foo()
-        ''', 'Cannot call <T> GoodCodeRed <Long>#attach(GoodCodeRed <Long>) 
with arguments [GoodCodeRed <Integer>]'
+            def c1 = new C<Short>()
+            def c2 = new C<Long>()
+            c1.attach(c2)
+        '''
     }
 
     void testHiddenGenerics() {

Reply via email to