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 674c94e867 GROOVY-10648: do not mix type parameter contexts when
resolving
674c94e867 is described below
commit 674c94e8674ac1b08cd813ef613265c754d91909
Author: Eric Milles <[email protected]>
AuthorDate: Sat Mar 11 10:03:25 2023 -0600
GROOVY-10648: do not mix type parameter contexts when resolving
[1].stream().reduce(0, {r,e -> r + e})
^ Stream<Integer> ^ ^ both Integer -- not int or unknown
def <T extends Number> List<T> f(Class<T> t) {
[t.newInstance(1)].stream().filter{n -> n.intValue() > 0}.toList()
^ Stream<T ext Number> ^ T or Number not Object
2_5_X backport
---
.../codehaus/groovy/ast/tools/GenericsUtils.java | 2 +
.../transform/stc/StaticTypeCheckingSupport.java | 60 +++---
.../transform/stc/StaticTypeCheckingVisitor.java | 224 ++++++++++-----------
.../groovy/transform/stc/GenericsSTCTest.groovy | 182 +++++++++--------
.../transform/stc/TypeInferenceSTCTest.groovy | 8 +-
5 files changed, 248 insertions(+), 228 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index bc841f0645..8c4906dcb0 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -883,6 +883,8 @@ public class GenericsUtils {
/**
* Checks if the type has any placeholder (aka unresolved) generics.
+ * <p>
+ * Backported from 3.0.0
*
* @since 2.5.10
*/
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 03433f68c7..cb024a3b4c 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1874,7 +1874,7 @@ public abstract class StaticTypeCheckingSupport {
return newGTs;
}
- private static GenericsType applyGenericsContext(Map<GenericsTypeName,
GenericsType> spec, GenericsType gt) {
+ private static GenericsType applyGenericsContext(final
Map<GenericsTypeName, GenericsType> spec, final GenericsType gt) {
if (gt.isPlaceholder()) {
GenericsTypeName name = new GenericsTypeName(gt.getName());
GenericsType specType = spec.get(name);
@@ -1895,7 +1895,9 @@ public abstract class StaticTypeCheckingSupport {
if (type.isArray()) {
newType = applyGenericsContext(spec,
type.getComponentType()).makeArray();
} else {
- if (type.getGenericsTypes()==null) return gt;
+ if (type.getGenericsTypes() == null) {
+ return gt;
+ }
newType = type.getPlainNodeReference();
newType.setGenericsPlaceHolder(type.isGenericsPlaceHolder());
newType.setGenericsTypes(applyGenericsContext(spec,
type.getGenericsTypes()));
@@ -1903,13 +1905,18 @@ public abstract class StaticTypeCheckingSupport {
return new GenericsType(newType);
}
- private static boolean hasNonTrivialBounds(GenericsType gt) {
+ private static boolean hasNonTrivialBounds(final GenericsType gt) {
+ if (gt.isWildcard()) {
+ return true;
+ }
+ if (gt.getLowerBound() != null) {
+ return true;
+ }
ClassNode[] upperBounds = gt.getUpperBounds();
- return gt.getLowerBound() != null || gt.isWildcard() ||
- (upperBounds != null && (
- upperBounds.length != 1
- || upperBounds[0].isGenericsPlaceHolder()
- || !OBJECT_TYPE.equals(upperBounds[0])));
+ if (upperBounds != null) {
+ return (upperBounds.length != 1 ||
upperBounds[0].isGenericsPlaceHolder() || !OBJECT_TYPE.equals(upperBounds[0]));
+ }
+ return false;
}
static ClassNode[] applyGenericsContext(final Map<GenericsTypeName,
GenericsType> spec, final ClassNode[] types) {
@@ -1929,27 +1936,30 @@ public abstract class StaticTypeCheckingSupport {
if (type.isArray()) {
return applyGenericsContext(spec,
type.getComponentType()).makeArray();
}
- ClassNode newType = type.getPlainNodeReference();
- GenericsType[] gt = type.getGenericsTypes();
- if (spec != null) {
- gt = applyGenericsContext(spec, gt);
- }
- newType.setGenericsTypes(gt);
- if (type.isGenericsPlaceHolder()) {
- boolean nonTrivial = hasNonTrivialBounds(gt[0]);
- if (nonTrivial || !gt[0].isPlaceholder()) {
+
+ GenericsType[] gt = applyGenericsContext(spec,
type.getGenericsTypes());
+
+ boolean typeVariable = type.isGenericsPlaceHolder();
+ if (typeVariable) {
+ if (!gt[0].isPlaceholder() || hasNonTrivialBounds(gt[0])) {
return getCombinedBoundType(gt[0]);
}
- String placeholderName = gt[0].getName();
- if (!placeholderName.equals(newType.getUnresolvedName())) {
- ClassNode clean = make(placeholderName);
- clean.setGenericsTypes(gt);
- clean.setRedirect(newType);
- newType = clean;
+ String gt_name = gt[0].getName();
+ ClassNode gt_type = gt[0].getType();
+ if (!type.getUnresolvedName().equals(gt_name) ||
!type.equals(gt_type)) {
+ ClassNode cn = !gt_name.equals(gt_type.getName()) ? gt_type :
type;
+ ClassNode tp = make(gt_name);
+ tp.setRedirect(cn);
+ tp.setGenericsTypes(gt);
+ tp.setGenericsPlaceHolder(true);
+ return tp;
}
- newType.setGenericsPlaceHolder(true);
}
- return newType;
+
+ ClassNode cn = type.getPlainNodeReference();
+ cn.setGenericsPlaceHolder(typeVariable);
+ cn.setGenericsTypes(gt);
+ return cn;
}
static ClassNode getCombinedBoundType(GenericsType genericsType) {
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 ffc67db72c..50e83b5b21 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -21,6 +21,7 @@ package org.codehaus.groovy.transform.stc;
import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
import groovy.lang.IntRange;
+import groovy.lang.Tuple2;
import groovy.transform.NamedParam;
import groovy.transform.NamedParams;
import groovy.transform.TypeChecked;
@@ -238,6 +239,7 @@ import static
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.extrac
import static
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsForClassNode;
import static
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findSetters;
import static
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findTargetVariable;
+import static
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.fullyResolve;
import static
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.fullyResolveType;
import static
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.getCombinedBoundType;
import static
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.getGenericsWithoutArray;
@@ -1033,15 +1035,9 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
if (rhsExpression instanceof ClosureExpression) {
MethodNode abstractMethod = findSAM(lhsType);
ClosureExpression closure = (ClosureExpression) rhsExpression;
- Map<GenericsType, GenericsType> mappings =
GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(abstractMethod.getDeclaringClass(),
lhsType);
-
- ClassNode[] samParameterTypes =
extractTypesFromParameters(abstractMethod.getParameters());
- for (int i = 0; i < samParameterTypes.length; i += 1) {
- if (samParameterTypes[i].isGenericsPlaceHolder()) {
- samParameterTypes[i] =
GenericsUtils.findActualTypeByGenericsPlaceholderName(samParameterTypes[i].getUnresolvedName(),
mappings);
- }
- }
+ Tuple2<ClassNode[], ClassNode> signature =
parameterizeSAM(abstractMethod, lhsType);
+ ClassNode[] samParameterTypes = signature.getFirst();
Parameter[] closureParameters = getParametersSafe(closure);
if (samParameterTypes.length == 1 &&
hasImplicitParameter(closure)) {
Variable it =
closure.getVariableScope().getDeclaredVariable("it"); // GROOVY-7141
@@ -1057,15 +1053,11 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
}
}
} else {
- String descriptor =
toMethodParametersString(findSAM(lhsType).getName(), samParameterTypes);
+ String descriptor =
toMethodParametersString(abstractMethod.getName(), samParameterTypes);
addStaticTypeError("Wrong number of parameters for method
target " + descriptor, rhsExpression);
}
- ClassNode returnType = abstractMethod.getReturnType();
- if (returnType.isGenericsPlaceHolder()) {
- returnType =
GenericsUtils.findActualTypeByGenericsPlaceholderName(returnType.getUnresolvedName(),
mappings);
- }
- storeInferredReturnType(rhsExpression, returnType);
+ storeInferredReturnType(rhsExpression, signature.getSecond());
} else if (rhsExpression instanceof MapExpression) { // GROOVY-7141
List<MapEntryExpression> spec = ((MapExpression)
rhsExpression).getMapEntryExpressions();
@@ -1076,6 +1068,23 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
}
}
+ // Backported from 3.0.0
+ private static Tuple2<ClassNode[], ClassNode> parameterizeSAM(final
MethodNode abstractMethod, final ClassNode targetType) {
+ Map<GenericsType, GenericsType> mappings =
GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(abstractMethod.getDeclaringClass(),
targetType);
+
+ ClassNode[] paramTypes =
extractTypesFromParameters(abstractMethod.getParameters());
+ for (int i = 0, n = paramTypes.length; i < n; i += 1) {
+ if (paramTypes[i].isGenericsPlaceHolder())
+ paramTypes[i] =
GenericsUtils.findActualTypeByGenericsPlaceholderName(paramTypes[i].getUnresolvedName(),
mappings);
+ }
+
+ ClassNode returnType = abstractMethod.getReturnType();
+ if (returnType.isGenericsPlaceHolder())
+ returnType =
GenericsUtils.findActualTypeByGenericsPlaceholderName(returnType.getUnresolvedName(),
mappings);
+
+ return new Tuple2<ClassNode[], ClassNode>(paramTypes, returnType);
+ }
+
private static boolean isCompoundAssignment(final Expression exp) {
if (!(exp instanceof BinaryExpression)) return false;
int type = ((BinaryExpression) exp).getOperation().getType();
@@ -2990,7 +2999,7 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
if (typeX != null) {
expectedType = typeX.getType();
}
- if (!entries.keySet().contains(name)) {
+ if (!entries.containsKey(name)) {
if (required) {
addStaticTypeError("required named param '" + name + "' not
found.", expression);
}
@@ -3015,118 +3024,108 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
}
/**
- * This method is responsible for performing type inference on closure
argument types whenever code like this is
- * found: <code>foo.collect { it.toUpperCase() }</code>.
- * In this case, the type checker tries to find if the
<code>collect</code> method has its {@link Closure} argument
- * annotated with {@link groovy.transform.stc.ClosureParams}. If yes, then
additional type inference can be performed
- * and the type of <code>it</code> may be inferred.
+ * Performs type inference on closure argument types whenever code like
this
+ * is found: <code>foo.collect { it.toUpperCase() }</code>.
+ * <p>
+ * In this case the type checker tries to find if the {@code collect}
method
+ * has its {@link Closure} argument annotated with {@link ClosureParams}.
If
+ * so, then additional type inference can be performed and the type of
+ * {@code it} may be inferred.
*
* @param receiver
* @param arguments
- * @param expression a closure expression for which the argument types
should be inferred
- * @param param the parameter where to look for a {@link
groovy.transform.stc.ClosureParams} annotation.
- * @param selectedMethod the method accepting a closure
+ * @param expression closure or lambda expression for which the argument
types should be inferred
+ * @param target parameter which may provide {@link ClosureParams}
annotation or SAM type
+ * @param method method that declares {@code target}
*/
- protected void inferClosureParameterTypes(final ClassNode receiver, final
Expression arguments, final ClosureExpression expression, final Parameter
param, final MethodNode selectedMethod) {
- List<AnnotationNode> annotations =
param.getAnnotations(CLOSUREPARAMS_CLASSNODE);
+ protected void inferClosureParameterTypes(final ClassNode receiver, final
Expression arguments, final ClosureExpression expression, final Parameter
target, final MethodNode method) {
+ MethodNode abstractMethod;
+ List<AnnotationNode> annotations =
target.getAnnotations(CLOSUREPARAMS_CLASSNODE);
if (annotations != null && !annotations.isEmpty()) {
for (AnnotationNode annotation : annotations) {
Expression hintClass = annotation.getMember("value");
if (hintClass instanceof ClassExpression) {
Expression options = annotation.getMember("options");
Expression resolverClass =
annotation.getMember("conflictResolutionStrategy");
- doInferClosureParameterTypes(receiver, arguments,
expression, selectedMethod, hintClass, resolverClass, options);
+ doInferClosureParameterTypes(receiver, arguments,
expression, method, hintClass, resolverClass, options);
}
}
- } else if (isSAMType(param.getOriginType())) {
- // SAM coercion
- inferSAMType(param, receiver, selectedMethod,
InvocationWriter.makeArgumentList(arguments), expression);
- }
- }
-
- private void inferSAMType(Parameter param, ClassNode receiver, MethodNode
methodWithSAMParameter, ArgumentListExpression originalMethodCallArguments,
ClosureExpression openBlock) {
- // In a method call with SAM coercion the inference is to be
- // understood as a two phase process. We have the normal method call
- // to the target method with the closure argument and we have the
- // SAM method that will be called inside the normal target method.
- // To infer correctly we have to "simulate" this process. We know the
- // call to the closure will be done through the SAM type, so the SAM
- // type generics deliver information about the Closure. At the same
- // time the SAM class is used in the target method parameter,
- // providing a connection from the SAM type and the target method
- // declaration class.
-
- // First we try to get as much information about the declaration
- // class through the receiver
- 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, n = parametersOfMethodContainingSAM.length; i < n; i
+= 1) {
- // potentially skip empty varargs
- if (i == n - 1
- && i == originalMethodCallArguments.getExpressions().size()
- && parametersOfMethodContainingSAM[i].getType().isArray())
- continue;
- Expression callArg = originalMethodCallArguments.getExpression(i);
- // we look at the closure later in detail, so skip it here
- if (callArg == openBlock) continue;
- ClassNode parameterType =
parametersOfMethodContainingSAM[i].getType();
-
extractGenericsConnections(targetMethodDeclarationClassConnections,
getType(callArg), parameterType);
- }
-
- // To make a connection to the SAM class we use that new information
- // to replace the generics in the SAM type parameter of the target
- // method and than that to make the connections to the SAM type
generics
- ClassNode paramTypeWithReceiverInformation =
applyGenericsContext(targetMethodDeclarationClassConnections,
param.getOriginType());
- Map<GenericsTypeName, GenericsType> SAMTypeConnections = new
HashMap<GenericsTypeName, GenericsType>();
- ClassNode classForSAM = paramTypeWithReceiverInformation.redirect();
- extractGenericsConnections(SAMTypeConnections,
paramTypeWithReceiverInformation, classForSAM);
-
- // should the open block provide final information we apply that
- // to the corresponding parameters of the SAM type method
- MethodNode methodForSAM = findSAM(classForSAM);
- ClassNode[] parameterTypesForSAM =
extractTypesFromParameters(methodForSAM.getParameters());
- ClassNode[] blockParameterTypes = (ClassNode[])
openBlock.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS);
- if (blockParameterTypes == null) {
- Parameter[] p = openBlock.getParameters();
- if (p == null) {
- // zero parameter closure e.g. { -> println 'no args' }
- blockParameterTypes = ClassNode.EMPTY_ARRAY;
- } else if (p.length == 0 && parameterTypesForSAM.length != 0) {
- // implicit it
- blockParameterTypes = parameterTypesForSAM;
+ } else if ((abstractMethod = findSAM(target.getOriginType())) != null)
{
+ Map<GenericsTypeName, GenericsType> context;
+ if (method.isStatic()) {
+ context = new HashMap<GenericsTypeName, GenericsType>();
} else {
- blockParameterTypes = new ClassNode[p.length];
- for (int i = 0; i < p.length; i++) {
- if (p[i] != null && !p[i].isDynamicTyped()) {
- blockParameterTypes[i] = p[i].getType();
- } else {
- blockParameterTypes[i] =
typeOrNull(parameterTypesForSAM, i);
+ context = extractPlaceHoldersVisibleToDeclaration(receiver,
method, arguments);
+ }
+ GenericsType[] typeParameters = method instanceof ConstructorNode
? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context,
method.getGenericsTypes());
+
+ if (typeParameters != null) {
+ boolean typeParametersResolved = false;
+ // first check for explicit type arguments
+ Expression emc = typeCheckingContext.getEnclosingMethodCall();
+ if (emc instanceof MethodCallExpression) {
+ MethodCallExpression mce = (MethodCallExpression) emc;
+ if (mce.getArguments() == arguments) {
+ GenericsType[] typeArguments = mce.getGenericsTypes();
+ if (typeArguments != null) {
+ int n = typeParameters.length;
+ if (n == typeArguments.length) {
+ typeParametersResolved = true;
+ for (int i = 0; i < n; i += 1) {
+ context.put(new
GenericsTypeName(typeParameters[i].getName()), typeArguments[i]);
+ }
+ }
+ }
+ }
+ }
+ if (!typeParametersResolved) {
+ // check for implicit type arguments
+ int i = -1; Parameter[] p = method.getParameters();
+ for (Expression argument : (TupleExpression) arguments) {
i += 1;
+ if (argument instanceof ClosureExpression ||
isNullConstant(argument)) continue;
+
+ ClassNode pType = p[Math.min(i, p.length -
1)].getType();
+ Map<GenericsTypeName, GenericsType> gc = new
HashMap<>();
+ extractGenericsConnections(gc,
wrapTypeIfNecessary(getType(argument)), pType);
+
+ for (Map.Entry<GenericsTypeName, GenericsType> entry :
gc.entrySet()) {
+ for (GenericsType tp : typeParameters) {
+ GenericsTypeName name = entry.getKey();
+ if (tp.getName().equals(name.getName()) &&
!context.containsKey(name)) {
+ context.put(name, entry.getValue());
+ break;
+ }
+ }
+ }
+ }
+
+ for (GenericsType tp : typeParameters) {
+ GenericsTypeName name = new
GenericsTypeName(tp.getName());
+ if (!context.containsKey(name)) context.put(name,
fullyResolve(tp, context));
}
}
}
- }
- for (int i = 0; i < blockParameterTypes.length; i++) {
- extractGenericsConnections(SAMTypeConnections,
blockParameterTypes[i], typeOrNull(parameterTypesForSAM, i));
- }
- // and finally we apply the generics information to the parameters and
- // store the type of parameter and block type as meta information
- for (int i = 0; i < blockParameterTypes.length; i++) {
- ClassNode resolvedParameter =
- applyGenericsContext(SAMTypeConnections,
typeOrNull(parameterTypesForSAM, i));
- blockParameterTypes[i] = resolvedParameter;
- }
- openBlock.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS,
blockParameterTypes);
- }
+ ClassNode[] samParamTypes = parameterizeSAM(abstractMethod,
applyGenericsContext(context, target.getType())).getFirst();
- private ClassNode typeOrNull(ClassNode[] parameterTypesForSAM, int i) {
- return i < parameterTypesForSAM.length ? parameterTypesForSAM[i] :
null;
+ ClassNode[] paramTypes =
expression.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS);
+ if (paramTypes == null) {
+ int n; Parameter[] p = expression.getParameters();
+ if (p == null) {
+ // zero parameters
+ paramTypes = ClassNode.EMPTY_ARRAY;
+ } else if ((n = p.length) == 0) {
+ // implicit parameter(s)
+ paramTypes = samParamTypes;
+ } else {
+ paramTypes = new ClassNode[n];
+ for (int i = 0; i < n; i += 1) {
+ paramTypes[i] = !p[i].isDynamicTyped() ?
p[i].getOriginType() : (i < samParamTypes.length ? samParamTypes[i] : null);
+ }
+ }
+
expression.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, paramTypes);
+ }
+ }
}
private List<ClassNode[]> getSignaturesFromHint(final ClosureExpression
expression, final MethodNode selectedMethod, final Expression hintClass, final
Expression options) {
@@ -3244,12 +3243,9 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
addError("Incorrect number of parameters. Expected
" + inferred.length + " but found " + closureParams.length, expression);
}
}
- boolean lastArg = i == (n - 1);
-
- if (!typeCheckMethodArgumentWithGenerics(originType,
inferredType, lastArg)) {
- addError("Expected parameter of type " +
inferredType.toString(false) + " but got " + originType.toString(false),
closureParam.getType());
+ if (!typeCheckMethodArgumentWithGenerics(originType,
inferredType, i == n-1)) {
+ addError("Expected parameter of type " +
prettyPrintType(inferredType) + " but got " + prettyPrintType(originType),
closureParam.getType());
}
-
typeCheckingContext.controlStructureVariables.put(closureParam, inferredType);
}
}
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index d7768e04be..3c8ce122d7 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -165,10 +165,10 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
void testReturnTypeInference2() {
assertScript '''
- Object m() {
- def s = '1234'
- println 'Hello'
- }
+ Object m() {
+ def s = '1234'
+ println 'Hello'
+ }
'''
}
@@ -308,7 +308,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Collections.singleton(x.newInstance(42))
}
def <T extends Number> List<T> g(Class<T> t) {
- f(t).stream().filter{n -> n.intValue() > 0}.toList()
+ f(t).stream().filter{it.intValue() > 0}.toList()
}
def result = g(Integer)
@@ -316,6 +316,53 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-10053
+ @NotYetImplemented
+ void testReturnTypeInferenceWithMethodGenericsA() {
+ if (!GroovyAssert.isAtLeastJdk('1.8')) return
+
+ for (cast in ['t.&cast', '{ n -> t.cast(n) }', '{ n -> (N) n }']) {
+ assertScript """
+ Set<Number> f() {
+ Collections.<Number>singleton(42)
+ }
+ def <N extends Number> Set<N> g(Class<N> t) {
+ Set<N> result = new HashSet<>()
+ f().stream().filter(t.&isInstance)
+ .map($cast).forEach{result.add(it)}
+ return result
+ }
+
+ def result = g(Integer)
+ assert result == [42] as Set
+ """
+ }
+ }
+
+ @NotYetImplemented
+ void testReturnTypeInferenceWithMethodGenericsB() {
+ if (!GroovyAssert.isAtLeastJdk('1.8')) return
+
+ assertScript '''
+ def <T> String test(Iterable<T> iterable) {
+ Iterator<T> it = iterable.iterator()
+ if (it.hasNext()) {
+ List<String[]> table = []
+ it.forEachRemaining { r ->
+ if (r instanceof List) {
+ String[] cells = ((List)
r).stream().map{it?.toString()}
+ table.add(cells)
+ }
+ }
+ return table
+ }
+ }
+
+ String result = test([ ['x'], ['y'], [null] ])
+ assert result == '[[x], [y], [null]]'
+ '''
+ }
+
// GROOVY-10051
void testReturnTypeInferenceWithMethodGenerics10() {
assertScript '''
@@ -423,10 +470,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
// GROOVY-9064
- @NotYetImplemented
void testReturnTypeInferenceWithMethodGenerics13() {
assertScript '''
- List getSomeRows() { [new String[]{'x'}] }
+ List getSomeRows() { [['x'] as String[]] }
List<String[]> rows = getSomeRows()
rows.each { row ->
def col = row[0].toUpperCase()
@@ -495,7 +541,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
T p
T m() {
Closure<T> x = { -> p }
- x() // Cannot return value of type Object for method returning
type T
+ x() // Cannot return value of type Object for method returning
T
}
}
assert new C<>(42).m() == 42
@@ -1250,7 +1296,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
}
}
class C {
@@ -2190,93 +2236,65 @@ class GenericsSTCTest extends
StaticTypeCheckingTestCase {
// GROOVY-5415
void testShouldUseMethodGenericType1() {
assertScript '''import groovy.transform.stc.GenericsSTCTest.ClassA
- class ClassB {
- void bar() {
- def ClassA<Long> a = new ClassA<Long>();
- a.foo(this.getClass());
+ class ClassB {
+ void test() {
+ def ClassA<Long> a = new ClassA<Long>()
+ a.foo(this.getClass())
+ }
}
- }
- new ClassB()
+ new ClassB().test()
'''
}
// GROOVY-5415
void testShouldUseMethodGenericType2() {
shouldFailWithMessages '''import
groovy.transform.stc.GenericsSTCTest.ClassA
- class ClassB {
- void bar() {
- def ClassA<Long> a = new ClassA<Long>();
- a.bar(this.getClass());
+ class ClassB {
+ void test() {
+ def ClassA<Long> a = new ClassA<Long>()
+ a.bar(this.getClass())
+ }
}
- }
- new ClassB()
''',
'Cannot call <X> groovy.transform.stc.GenericsSTCTest$ClassA
<Long>#bar(java.lang.Class <Long>) with arguments [java.lang.Class <? extends
java.lang.Object>]'
}
// GROOVY-8961
void testShouldUseMethodGenericType3() {
- assertScript '''
- void setM(List<String> strings) {
- }
- void test() {
- m = Collections.emptyList() // Cannot assign value of type
List<T> to variable of List<String>
- }
- test()
- '''
- assertScript '''
- void setM(Collection<String> strings) {
- }
- void test() {
- m = Collections.emptyList()
- }
- test()
- '''
- assertScript '''
- void setM(Iterable<String> strings) {
- }
- void test() {
- m = Collections.emptyList()
- }
- test()
- '''
-
- shouldFailWithMessages '''
- void setM(List<String> strings) {
+ for (mode in ['', 'static']) {
+ for (type in ['List', 'Collection', 'Iterable']) {
+ assertScript """
+ $mode void setX(${type}<String> strings) { }
+ x = Collections.emptyList()
+ """
}
- void test() {
- m = Collections.<Integer>emptyList()
- }
- ''',
- 'Cannot assign value of type java.util.List <Integer> to variable of
type java.util.List <String>'
+ shouldFailWithMessages """
+ $mode void setX(List<String> strings) { }
+ x = Collections.<Integer>emptyList()
+ """,
+ 'Cannot assign value of type java.util.List <Integer> to variable
of type java.util.List <String>'
+ }
}
// GROOVY-9734
void testShouldUseMethodGenericType4() {
- assertScript '''
- void m(List<String> strings) {
- }
- void test() {
- m(Collections.emptyList()) // Cannot call m(List<String>) with
arguments [List<T>]
- }
- test()
- '''
- assertScript '''
- void m(Collection<String> strings) {
- }
- void test() {
- m(Collections.emptyList())
- }
- test()
- '''
- assertScript '''
- void m(Iterable<String> strings) {
- }
- void test() {
- m(Collections.emptyList())
- }
- test()
- '''
+ for (type in ['List', 'Collection', 'Iterable']) {
+ assertScript """
+ def m(${type}<String> strings) { }
+ m(Collections.emptyList())
+ """
+ }
+ }
+
+ // GROOVY-9734
+ @NotYetImplemented
+ void testShouldUseMethodGenericType4a() {
+ for (type in ['List', 'Collection', 'Iterable']) {
+ assertScript """
+ static m(${type}<String> strings) { }
+ m(Collections.emptyList())
+ """
+ }
}
// GROOVY-9751
@@ -2529,7 +2547,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
// GROOVY-10648
- @NotYetImplemented
void testShouldUseMethodGenericType16() {
assertScript '''
def <T extends Number> Set<T> test(Iterable<T> iterable) {
@@ -2570,22 +2587,22 @@ class GenericsSTCTest extends
StaticTypeCheckingTestCase {
void testAddAllWithCollectionShouldBeAllowed() {
assertScript '''import
org.codehaus.groovy.transform.stc.ExtensionMethodNode
List<String> list = ['a','b','c']
- Collection<String> e = list.findAll { it }
+ Collection<String> strings = list.findAll { it }
@ASTTest(phase=INSTRUCTION_SELECTION, value={
def dmt =
node.rightExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
assert dmt.declaringClass.implementsInterface(LIST_TYPE)
assert !(dmt instanceof ExtensionMethodNode)
assert dmt.name == 'addAll'
})
- boolean r = list.addAll(e)
+ boolean r = list.addAll(strings)
'''
}
void testAddAllWithCollectionShouldNotBeAllowed() {
shouldFailWithMessages '''
List<String> list = ['a','b','c']
- Collection<Integer> e = (Collection<Integer>) [1,2,3]
- boolean r = list.addAll(e)
+ Collection<Integer> numbers = (Collection<Integer>) [1,2,3]
+ boolean r = list.addAll(numbers)
''',
'Cannot call java.util.ArrayList
<java.lang.String>#addAll(java.util.Collection <? extends java.lang.String>)
with arguments [java.util.Collection <Integer>]'
}
@@ -2794,7 +2811,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
r.bindings2['a'] = 'A'
r.bindings2.put('b', 'B')
-
'''
}
void testInferDiamondForAssignment() {
diff --git a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
index d4fe1c834f..87e985c935 100644
--- a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -1318,11 +1318,7 @@ class TypeInferenceSTCTest extends
StaticTypeCheckingTestCase {
// GROOVY-9077
void testInferredTypeForPropertyThatResolvesToMethod() {
assertScript '''
- import groovy.transform.*
- import static
org.codehaus.groovy.transform.stc.StaticTypesMarker.DIRECT_METHOD_CALL_TARGET
-
- @CompileStatic
- void meth() {
+ void test() {
def items = [1, 2] as LinkedList
@ASTTest(phase=INSTRUCTION_SELECTION, value={
@@ -1344,7 +1340,7 @@ class TypeInferenceSTCTest extends
StaticTypeCheckingTestCase {
def alsoOne = items.peek()
}
- meth()
+ test()
'''
}