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() {