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/09/14 15:25:51 UTC
[groovy] branch GROOVY_3_0_X updated: GROOVY-10322: STC: remove hidden generics when checking method arguments
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
new 2c2b46b728 GROOVY-10322: STC: remove hidden generics when checking method arguments
2c2b46b728 is described below
commit 2c2b46b72873c5070806849cf4e917064c2d9585
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Sep 14 09:38:39 2022 -0500
GROOVY-10322: STC: remove hidden generics when checking method arguments
3_0_X backport
---
.../transform/stc/StaticTypeCheckingSupport.java | 125 ++++++-------
.../transform/stc/StaticTypeCheckingVisitor.java | 196 ++++++++++-----------
.../groovy/transform/stc/GenericsSTCTest.groovy | 103 ++++++-----
3 files changed, 217 insertions(+), 207 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 3bd4db3dd4..e2e33aa2f8 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -57,6 +57,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;
@@ -1041,14 +1042,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;
@@ -1383,15 +1398,6 @@ public abstract class StaticTypeCheckingSupport {
return true;
}
- static void addMethodLevelDeclaredGenerics(final MethodNode method, final 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(final ClassNode receiver, final ClassNode[] argumentTypes, final MethodNode candidateMethod) {
if (candidateMethod instanceof ExtensionMethodNode) {
ClassNode[] realTypes = new ClassNode[argumentTypes.length + 1];
@@ -1411,61 +1417,51 @@ public abstract class StaticTypeCheckingSupport {
}
private static boolean typeCheckMethodsWithGenerics(final ClassNode receiver, final ClassNode[] argumentTypes, final MethodNode candidateMethod, final boolean isExtensionMethod) {
- 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 are default parameters!
+ 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.emptySet(), 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 = argumentTypes.length, nthParameter = parameters.length - 1; i < n; i += 1) {
- ClassNode argumentType = argumentTypes[i];
- ClassNode parameterType = parameters[Math.min(i, nthParameter)].getOriginType();
- failure = failure || inferenceCheck(fixedGenericsPlaceHolders, resolvedMethodGenerics, parameterType, argumentType, i >= nthParameter);
+ ClassNode argumentType = argumentTypes[i], parameterType = parameters[Math.min(i,nthParameter)].getOriginType();
+ failure |= inferenceCheck(fixedPlaceHolders, candidateGenerics, parameterType, argumentType, i >= nthParameter);
- if (i == 0 && isExtensionMethod) // set real fixed generics for extension methods
- fixedGenericsPlaceHolders = extractResolvedPlaceHolders(resolvedMethodGenerics);
+ if (i == 0 && isExtensionMethod) // re-load fixed names for extension
+ fixedPlaceHolders = extractResolvedPlaceHolders(candidateGenerics);
}
+
return !failure;
}
@@ -1607,9 +1603,7 @@ public abstract class StaticTypeCheckingSupport {
if (newValue == null) {
newValue = connections.get(entry.getKey());
if (newValue != null) { // GROOVY-10315, GROOVY-10317
- ClassNode o = GenericsUtils.makeClassSafe0(CLASS_Type, oldValue),
- n = GenericsUtils.makeClassSafe0(CLASS_Type, newValue);
- newValue = WideningCategories.lowestUpperBound(o,n).getGenericsTypes()[0];
+ newValue = getCombinedGenericsType(oldValue, newValue);
}
}
if (newValue == null) {
@@ -1923,6 +1917,13 @@ public abstract class StaticTypeCheckingSupport {
return genericsType.getType();
}
+ static GenericsType getCombinedGenericsType(final GenericsType gt1, final GenericsType gt2) {
+ ClassNode cn1 = GenericsUtils.makeClassSafe0(CLASS_Type, gt1);
+ ClassNode cn2 = GenericsUtils.makeClassSafe0(CLASS_Type, gt2);
+ ClassNode lub = WideningCategories.lowestUpperBound(cn1, cn2);
+ return lub.getGenericsTypes()[0];
+ }
+
private static Map<GenericsTypeName, GenericsType> getGenericsParameterMapOfThis(final ClassNode cn) {
if (cn == null) return null;
Map<GenericsTypeName, GenericsType> map = null;
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 3c44234b0b..4b74502987 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -239,7 +239,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;
@@ -616,7 +615,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
} else {
checkOrMarkPrivateAccess(vexp, (FieldNode) accessedVariable, typeCheckingContext.isTargetOfEnclosingAssignment(vexp));
- // GROOVY-9454
ClassNode inferredType = getInferredTypeFromTempInfo(vexp, null);
if (inferredType != null && !inferredType.equals(OBJECT_TYPE)) {
vexp.putNodeMetaData(INFERRED_RETURN_TYPE, inferredType);
@@ -986,7 +984,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
Function<MethodNode, ClassNode> firstParamType = (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;
@@ -1408,17 +1406,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
* @param arguments the constructor arguments
*/
protected MethodNode checkGroovyStyleConstructor(final ClassNode node, final ClassNode[] arguments, final ASTNode source) {
- if (node.equals(OBJECT_TYPE) || node.equals(DYNAMIC_TYPE)) {
+ if (node.equals(OBJECT_TYPE)) {
// in that case, we are facing a list constructor assigned to a def or object
return null;
}
- List<ConstructorNode> constructors = node.getDeclaredConstructors();
+ List<? extends MethodNode> constructors = node.getDeclaredConstructors();
if (constructors.isEmpty() && arguments.length == 0) {
return null;
}
- List<MethodNode> constructorList = findMethod(node, "<init>", arguments);
- if (constructorList.isEmpty()) {
- if (isBeingCompiled(node) && arguments.length == 1 && LINKEDHASHMAP_CLASSNODE.equals(arguments[0])) {
+ constructors = findMethod(node, "<init>", arguments);
+ if (constructors.isEmpty()) {
+ if (isBeingCompiled(node) && !node.isInterface() && arguments.length == 1 && arguments[0].equals(LINKEDHASHMAP_CLASSNODE)) {
// there will be a default hash map constructor added later
ConstructorNode cn = new ConstructorNode(Opcodes.ACC_PUBLIC, new Parameter[]{new Parameter(LINKEDHASHMAP_CLASSNODE, "args")}, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE);
return cn;
@@ -1426,11 +1424,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
addStaticTypeError("No matching constructor found: " + prettyPrintTypeName(node) + toMethodParametersString("", arguments), source);
return null;
}
- } else if (constructorList.size() > 1) {
+ } else if (constructors.size() > 1) {
addStaticTypeError("Ambiguous constructor call " + prettyPrintTypeName(node) + toMethodParametersString("", arguments), source);
return null;
}
- return constructorList.get(0);
+ return constructors.get(0);
}
/**
@@ -1509,8 +1507,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
+ final String isserName, getterName, setterName;
+ {
+ final String properName = capitalize(propertyName);
+ isserName = "is" + properName; getterName = "get" + properName; setterName = "set" + properName;
+ }
+
boolean foundGetterOrSetter = false;
- String capName = capitalize(propertyName);
Set<ClassNode> handledNodes = new HashSet<>();
List<Receiver<String>> receivers = new ArrayList<>();
addReceivers(receivers, makeOwnerList(objectExpression), pexp.isImplicitThis());
@@ -1571,11 +1574,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
- MethodNode getter = findGetter(current, "get" + capName, pexp.isImplicitThis());
+ MethodNode getter = findGetter(current, getterName, pexp.isImplicitThis());
getter = allowStaticAccessToMember(getter, staticOnly);
- if (getter == null) getter = findGetter(current, "is" + capName, pexp.isImplicitThis());
+ if (getter == null) getter = findGetter(current, isserName, pexp.isImplicitThis());
getter = allowStaticAccessToMember(getter, staticOnly);
- List<MethodNode> setters = findSetters(current, getSetterName(propertyName), false);
+ List<MethodNode> setters = findSetters(current, setterName, false);
setters = allowStaticAccessToMember(setters, staticOnly);
// need to visit even if we only look for setters for compatibility
@@ -1639,8 +1642,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
// GROOVY-5568: the property may be defined by DGM
for (ClassNode dgmReceiver : isPrimitiveType(receiverType) ? new ClassNode[]{receiverType, getWrapper(receiverType)} : new ClassNode[]{receiverType}) {
- List<MethodNode> methods = findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "get" + capName, ClassNode.EMPTY_ARRAY);
- for (MethodNode method : findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) {
+ List<MethodNode> methods = findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, getterName, ClassNode.EMPTY_ARRAY);
+ for (MethodNode method : findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, isserName, ClassNode.EMPTY_ARRAY)) {
if (Boolean_TYPE.equals(getWrapper(method.getReturnType()))) methods.add(method);
}
if (isUsingGenericsOrIsArrayUsingGenerics(dgmReceiver)) {
@@ -1823,8 +1826,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
private void storeWithResolve(ClassNode type, final ClassNode receiver, final ClassNode declaringClass, final boolean isStatic, final Expression expressionToStoreOn) {
- if (GenericsUtils.hasUnresolvedGenerics(type)) {
- type = resolveGenericsWithContext(resolvePlaceHoldersFromDeclaration(receiver, declaringClass, null, isStatic), type);
+ if (!isStatic && GenericsUtils.hasUnresolvedGenerics(type)) {
+ type = resolveGenericsWithContext(extractPlaceHolders(receiver, declaringClass), type);
}
if (expressionToStoreOn instanceof PropertyExpression) {
storeInferredTypeForPropertyExpression((PropertyExpression) expressionToStoreOn, type);
@@ -1950,7 +1953,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;
}
@@ -2293,7 +2296,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ctor = typeCheckMapConstructor(call, receiver, arguments);
} else {
if (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 = Arrays.stream(parameters).map(p -> new Parameter(applyGenericsContext(context, p.getType()), p.getName())).toArray(Parameter[]::new);
}
resolvePlaceholdersFromImplicitTypeHints(argumentTypes, argumentList, parameters);
@@ -2927,7 +2930,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
} else if (isSAMType(target.getOriginType())) { // SAM-type coercion
- Map<GenericsTypeName, GenericsType> context = method.isStatic() ? new HashMap<>() : extractPlaceHolders(null, receiver, getDeclaringClass(method, arguments));
+ Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
GenericsType[] typeParameters = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
if (typeParameters != null) {
@@ -3551,9 +3554,15 @@ 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 = 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, directMethodCallCandidate, call)){
returnType = adjustWithTraits(directMethodCallCandidate, chosenReceiver.getType(), args, returnType);
storeType(call, returnType);
@@ -4994,13 +5003,12 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (vexp.isSuperExpression()) return makeSuper();
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;
}
@@ -5291,7 +5299,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ArgumentListExpression args = new ArgumentListExpression();
VariableExpression vexp = varX("$self", receiver);
args.addExpression(vexp);
- if (arguments instanceof TupleExpression) {
+ if (arguments instanceof TupleExpression) { // NO_ARGUMENTS
for (Expression argument : (TupleExpression) arguments) {
args.addExpression(argument);
}
@@ -5303,7 +5311,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
@@ -5582,111 +5590,101 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return getInferredTypeFromTempInfo(expression, declaredOrInferred);
}
- private static ClassNode getDeclaringClass(final MethodNode method, final Expression arguments) {
- 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;
- }
- return declaringClass;
- }
-
- private Map<GenericsTypeName, GenericsType> resolvePlaceHoldersFromDeclaration(final ClassNode receiver, final ClassNode declaration, final MethodNode method, final 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(final ClassNode cn) {
if (cn.isArray()) return isGenericsPlaceHolderOrArrayOf(cn.getComponentType());
return cn.isGenericsPlaceHolder();
}
- private static Map<GenericsTypeName, GenericsType> extractPlaceHolders(final MethodNode method, ClassNode receiver, final ClassNode declaringClass) {
- if (declaringClass.equals(OBJECT_TYPE)) {
- Map<GenericsTypeName, GenericsType> resolvedPlaceholders = new HashMap<>();
- if (method != null) addMethodLevelDeclaredGenerics(method, resolvedPlaceholders);
- return resolvedPlaceholders;
- }
-
- Map<GenericsTypeName, GenericsType> resolvedPlaceholders = null;
- if (isPrimitiveType(receiver) && !isPrimitiveType(declaringClass)) {
- receiver = getWrapper(receiver);
- }
+ private static Map<GenericsTypeName, GenericsType> extractPlaceHolders(final ClassNode receiver, final ClassNode declaringClass) {
+ Map<GenericsTypeName, GenericsType> result = null;
ClassNode[] todo;
if (receiver instanceof UnionTypeClassNode) {
todo = ((UnionTypeClassNode) receiver).getDelegates();
} else {
- todo = new ClassNode[] {receiver};
+ todo = new ClassNode[] {!isPrimitiveType(declaringClass) ? wrapTypeIfNecessary(receiver) : receiver};
}
for (ClassNode type : todo) {
ClassNode current = type;
while (current != null) {
- boolean continueLoop = true;
- // extract the place holders
- Map<GenericsTypeName, GenericsType> currentPlaceHolders = new HashMap<>();
- if (isGenericsPlaceHolderOrArrayOf(declaringClass) || declaringClass.equals(current)) {
- extractGenericsConnections(currentPlaceHolders, current, declaringClass);
- if (method != null) addMethodLevelDeclaredGenerics(method, currentPlaceHolders);
- continueLoop = false;
+ Map<GenericsTypeName, GenericsType> placeHolders = new HashMap<>();
+ // GROOVY-10055: handle diamond or raw
+ if (current.getGenericsTypes() != null
+ ? current.getGenericsTypes().length == 0
+ : current.redirect().getGenericsTypes() != null) {
+ for (GenericsType gt : current.redirect().getGenericsTypes()) {
+ ClassNode cn = gt.getUpperBounds() != null ? gt.getUpperBounds()[0] : gt.getType().redirect();
+ placeHolders.put(new GenericsTypeName(gt.getName()), cn.getPlainNodeReference().asGenericsType());
+ }
+ }
+
+ 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 && 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<>();
+ } else {
+ ClassNode declaring = method.getDeclaringClass();
+ 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)
+ declaring = cn;
+ }
+ }
+ result = extractPlaceHolders(receiver, declaring);
+ if (!result.isEmpty()) Optional.ofNullable(method.getGenericsTypes()).ifPresent(methodGenerics ->
+ Arrays.stream(methodGenerics).map(gt -> new GenericsTypeName(gt.getName())).forEach(result::remove)); // GROOVY-10322
+ }
+ 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) {
@@ -5696,7 +5694,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;
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 2dce5be83f..a68c732f7f 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -215,7 +215,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10062
+ // GROOVY-10062
+ @NotYetImplemented
void testReturnTypeInferenceWithMethodGenerics2() {
assertScript '''
def <T> T m(T t, ... zeroOrMore) {
@@ -304,7 +305,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10053
+ // GROOVY-10053
+ @NotYetImplemented
void testReturnTypeInferenceWithMethodGenericsA() {
['t::cast', 'n -> t.cast(n)', 'n -> (N) n', '{ n -> (N) n }'].each { cast ->
assertScript """
@@ -411,7 +413,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10067
+ // GROOVY-10067
+ @NotYetImplemented
void testReturnTypeInferenceWithMethodGenerics11() {
assertScript '''
def <N extends Number> N getNumber() {
@@ -480,7 +483,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
}
- @NotYetImplemented // GROOVY-8409, GROOVY-10067
+ // GROOVY-8409, GROOVY-10067
+ @NotYetImplemented
void testReturnTypeInferenceWithMethodGenerics15() {
shouldFailWithMessages '''
List<CharSequence> list = ['x'].collect() // GROOVY-10074
@@ -594,7 +598,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10339
+ // GROOVY-10339
+ @NotYetImplemented
void testReturnTypeInferenceWithMethodGenerics21() {
for (type in ['Character', 'Integer']) {
shouldFailWithMessages """
@@ -669,7 +674,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10622
+ // GROOVY-10622
+ @NotYetImplemented
void testReturnTypeInferenceWithMethodGenerics26() {
String types = '''
@groovy.transform.TupleConstructor(defaults=false)
@@ -702,7 +708,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10637
+ // GROOVY-10637
+ @NotYetImplemented
void testReturnTypeInferenceWithMethodGenerics27() {
assertScript '''
class Outer extends groovy.transform.stc.MyBean<Inner> {
@@ -719,7 +726,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10646
+ // GROOVY-10646
+ @NotYetImplemented
void testReturnTypeInferenceWithMethodGenerics28() {
String types = '''
class Model {
@@ -1082,7 +1090,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10080
+ // GROOVY-10080
+ @NotYetImplemented
void testDiamondInferrenceFromConstructor11() {
assertScript '''
@groovy.transform.TupleConstructor(defaults=false)
@@ -1235,7 +1244,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
class B<T1 extends Number, T2 extends A<C, ? extends T1>> {
T2 t
B(T2 t) {
- this.t = t
+ this.t = t // Cannot assign value of type A<C,? extends T1> to variable of type T2
}
}
class C {
@@ -1266,7 +1275,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10228
+ // GROOVY-10228
+ @NotYetImplemented
void testDiamondInferrenceFromConstructor18() {
assertScript '''
@groovy.transform.TupleConstructor(defaults=false)
@@ -1482,7 +1492,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10633
+ // GROOVY-10633
+ @NotYetImplemented
void testDiamondInferrenceFromConstructor32() {
assertScript '''
class A<T, Y> {
@@ -1633,7 +1644,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10342
+ // GROOVY-10342
+ @NotYetImplemented
void testAssignmentShouldWorkForParameterizedType2() {
assertScript '''
class C<T> {
@@ -3128,7 +3140,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10153
+ // GROOVY-10153
+ @NotYetImplemented
void testCompatibleArgumentsForPlaceholders11() {
['A', 'B', 'C'].each { T ->
assertScript """
@@ -3151,7 +3164,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'#test(int, java.lang.String). Please check if the declared type is correct and if the method exists.'
}
- @NotYetImplemented // GROOVY-7720
+ // GROOVY-7720
+ @NotYetImplemented
void testIncompatibleArgumentsForPlaceholders2() {
shouldFailWithMessages '''
class Consumer<T> {
@@ -3216,9 +3230,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
test(new Holder<Object>())
''',
- 'Cannot call TypedProperty <String, Object>#eq(java.lang.String) with arguments [groovy.lang.GString]',
- 'Cannot call TypedProperty <String, Unknown>#eq(java.lang.String) with arguments [int]',
- 'Cannot call TypedProperty <Number, Unknown>#eq(java.lang.Number) with arguments [java.lang.String]'
+ 'Cannot call TypedProperty','#eq(java.lang.String) with arguments [groovy.lang.GString]',
+ 'Cannot find matching method TypedProperty#eq(int)', // chooseBestMethod removes "eq"
+ 'Cannot find matching method TypedProperty#eq(java.lang.String)'
}
// GROOVY-5748
@@ -3616,7 +3630,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10049, GROOVY-10166
+ // GROOVY-10049, GROOVY-10166
void testShouldFindMethodEvenWithRepeatNames2() {
assertScript '''
abstract class A<T extends C> {
@@ -3658,7 +3672,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-10322
+ // GROOVY-10322
void testShouldFindMethodEvenWithRepeatNames4() {
assertScript '''
class C<T> {
@@ -3684,29 +3698,25 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-5893
+ // GROOVY-5893
void testPlusInClosure() {
- assertScript '''
- def list = [1, 2, 3]
+ ['def', 'Object', 'Number', 'Integer', 'Comparable'].each { type ->
+ assertScript """
+ List<Integer> list = [1, 2, 3]
- @ASTTest(phase=INSTRUCTION_SELECTION,value={
- assert node.getNodeMetaData(INFERRED_TYPE) == int_TYPE
- })
- def sum = 0
- list.each { int i -> sum = sum+i }
- assert sum == 6
+ int sum = 0
+ list.each { int i -> sum = sum + i }
+ assert sum == 6
- sum = 0
- list.each { int i -> sum += i }
- assert sum == 6
+ sum = 0
+ list.each { int i -> sum += i }
+ assert sum == 6
- @ASTTest(phase=INSTRUCTION_SELECTION, value={
- assert node.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
- })
- def sumWithInject = list.inject(0, { int x, int y -> x + y })
- sum = sumWithInject
- assert sum == 6
- '''
+ $type sumWithInject = list.inject(0, { int x, int y -> x + y })
+ // ^^^^^^^^^^^^^ T ^^^^ E[] ^ U ^ U ^ E ^^^^^ V
+ assert sumWithInject == 6
+ """
+ }
}
// GROOVY-10320
@@ -4135,7 +4145,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
// GROOVY-5839
void testMethodShadowGenerics() {
- shouldFailWithMessages '''
+ assertScript '''
class C<T> {
Collection<C<T>> attached = []
def <T> void attach(C<T> toAttach) {
@@ -4145,8 +4155,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
def c1 = new C<Short>()
def c2 = new C<Long>()
c1.attach(c2)
- ''',
- 'Cannot call <T> C <Short>#attach(C <Short>) with arguments [C <Long>]'
+ '''
}
void testSuperClassGenerics() {
@@ -4430,7 +4439,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-9998
+ // GROOVY-9998
+ @NotYetImplemented
void testContravariantMethodResolution2() {
assertScript '''import groovy.transform.*
@@ -4685,7 +4695,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'Cannot assign java.util.List <java.lang.Integer> to: java.util.List <CharSequence>'
}
- @NotYetImplemented // GROOVY-10295
+ // GROOVY-10295
+ @NotYetImplemented
void testReturnTypeChecking2() {
assertScript '''
List<CharSequence> test() {
@@ -4854,7 +4865,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
}
- @NotYetImplemented // GROOVY-10055
+ // GROOVY-10055
void testSelfReferentialTypeParameter2() {
assertScript '''
abstract class A<Self extends A<Self>> {
@@ -4914,7 +4925,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
// GROOVY-7804
void testParameterlessClosureToGenericSAMTypeArgumentCoercion() {
assertScript '''
- interface Supplier<T> { def <T> T get() }
+ import java.util.function.Supplier
def <T> T doGet(Supplier<T> supplier) { supplier.get() }