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

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


The following commit(s) were added to refs/heads/master by this push:
     new 2e084efe49 GROOVY-5893: STC: merge type info for `<T, U extends T, V 
extends T>`
2e084efe49 is described below

commit 2e084efe49c0ef21a866232d6ab8a55182efba62
Author: Eric Milles <eric.mil...@thomsonreuters.com>
AuthorDate: Tue Nov 7 14:36:59 2023 -0600

    GROOVY-5893: STC: merge type info for `<T, U extends T, V extends T>`
---
 .../groovy/runtime/ArrayGroovyMethods.java         |  83 +++++---
 .../groovy/runtime/DefaultGroovyMethods.java       | 236 +++++++++++----------
 .../transform/stc/StaticTypeCheckingVisitor.java   |   8 +-
 .../stc/ClosureParamTypeInferenceSTCTest.groovy    |  76 ++++---
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  35 +--
 .../asm/sc/GenericsStaticCompileTest.groovy        |   6 +
 6 files changed, 263 insertions(+), 181 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/runtime/ArrayGroovyMethods.java 
b/src/main/java/org/codehaus/groovy/runtime/ArrayGroovyMethods.java
index 4751572e27..3df60a0e95 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ArrayGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ArrayGroovyMethods.java
@@ -81,10 +81,10 @@ import java.util.function.LongConsumer;
 import java.util.function.LongUnaryOperator;
 
 /**
- * This class defines new groovy methods which appear on primitive arrays 
inside
- * the Groovy environment. Static methods are used with the first parameter 
being
- * the destination class, i.e. <code>public static int[] each(int[] self, 
Closure closure)</code>
- * provides a <code>each({i -> })</code> method for <code>int[]</code>.
+ * Defines new groovy methods which appear on arrays inside the Groovy 
environment.
+ * Static methods are used with the first parameter being the destination 
class,
+ * i.e. <code>public static int[] each(int[] self, Closure closure)</code>
+ * provides an <code>each({i -> })</code> method for <code>int[]</code>.
  * <p>
  * NOTE: While this class contains many 'public' static methods, it is
  * primarily regarded as an internal class (its internal package name
@@ -4616,42 +4616,77 @@ public class ArrayGroovyMethods extends 
DefaultGroovyMethodsSupport {
     // inject
 
     /**
-     * Iterates through the given array as with 
inject(Object[],initialValue,closure), but
-     * using the first element of the array as the initialValue, and then 
iterating
-     * the remaining elements of the array.
+     * Iterates through the given array, passing the first two elements to the
+     * closure. The result is passed back (injected) to the closure along with
+     * the third element and so on until all array elements have been consumed.
+     *
+     * <pre class="groovyTestCase">
+     * def array = new Number[] {1, 2, 3, 4}
+     * def value = array.inject { acc, val -> acc * val }
+     * assert value == 24
+     *
+     * array = new Object[] {['a','b'], ['b','c'], ['d','b']}
+     * value = array.inject { acc, val -> acc.intersect(val) }
+     * assert value == ['b']
+     *
+     * array = new String[] {'t', 'i', 'm'}
+     * value = array.inject { acc, val -> acc + val }
+     * assert value == 'tim'
+     * </pre>
      *
      * @param self    an array
      * @param closure a closure
-     * @return the result of the last closure call
-     * @throws NoSuchElementException if the array is empty.
+     * @return first value for single-element array or the result of the last 
closure call
+     * @throws NoSuchElementException if the array is empty
      * @see #inject(Object[], Object, Closure)
      * @since 1.8.7
      */
     public static <E, T, V extends T> T inject(E[] self, 
@ClosureParams(value=FromString.class,options="E,E") Closure<V> closure) {
-        return DefaultGroovyMethods.inject((Object) self, closure);
+        if (self.length == 0) {
+            throw new NoSuchElementException("Cannot call inject() on an empty 
array without passing an initial value.");
+        }
+        T value = (T) self[0];
+        Object[] params = new Object[2];
+        for (int i = 1; i < self.length; i += 1) {
+            params[0] = value;
+            params[1] = self[i];
+            value = closure.call(params);
+        }
+        return value;
     }
 
     /**
-     * Iterates through the given array, passing in the initial value to
-     * the closure along with the first item. The result is passed back 
(injected) into
-     * the closure along with the second item. The new result is injected back 
into
-     * the closure along with the third item and so on until all elements of 
the array
-     * have been used.
-     * <p>
-     * Also known as foldLeft in functional parlance.
+     * Iterates through the given array, passing in the initial value and the
+     * first element to the closure. The result is sent back (injected) with 
the
+     * second element. The new result is injected back into the closure with 
the
+     * third element and so on until all elements have been consumed.
+     *
+     * <pre class="groovyTestCase">
+     * def array = new Number[] {2, 3, 4}
+     * def value = array.inject(1) { acc, val -> acc * val }
+     * assert value == 24
+     *
+     * array = new Object[] {['a','b'], ['b','c'], ['d','b']}
+     * value = array.inject(['a','b','c']) { acc, val -> acc.intersect(val) }
+     * assert value == ['b']
      *
-     * @param self         an Object[]
-     * @param initialValue some initial value
+     * array = new String[] {'t', 'i', 'm'}
+     * value = array.inject("") { acc, val -> acc + val }
+     * assert value == 'tim'
+     * </pre>
+     *
+     * @param self         an array
+     * @param initialValue base value
      * @param closure      a closure
-     * @return the result of the last closure call
+     * @return base value for empty array or the result of the last closure 
call
      * @since 1.5.0
      */
     public static <E, T, U extends T, V extends T> T inject(E[] self, U 
initialValue, @ClosureParams(value=FromString.class,options="U,E") Closure<V> 
closure) {
-        Object[] params = new Object[2];
         T value = initialValue;
-        for (Object next : self) {
+        Object[] params = new Object[2];
+        for (int i = 0; i < self.length; i += 1) {
             params[0] = value;
-            params[1] = next;
+            params[1] = self[i];
             value = closure.call(params);
         }
         return value;
@@ -5064,7 +5099,7 @@ public class ArrayGroovyMethods extends 
DefaultGroovyMethodsSupport {
      */
     public static <T> T last(T[] self) {
         if (self.length == 0) {
-            throw new NoSuchElementException("Cannot access last() element 
from an empty Array");
+            throw new NoSuchElementException("Cannot access last() element 
from an empty array");
         }
         return self[self.length - 1];
     }
diff --git 
a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java 
b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index abe244f857..49391aae2c 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -157,11 +157,10 @@ import java.util.function.Function;
 import static groovy.lang.groovydoc.Groovydoc.EMPTY_GROOVYDOC;
 
 /**
- * This class defines new groovy methods which appear on normal JDK
- * classes inside the Groovy environment. Static methods are used with the
- * first parameter being the destination class,
- * i.e. <code>public static String reverse(String self)</code>
- * provides a <code>reverse()</code> method for <code>String</code>.
+ * Defines new groovy methods which appear on classes inside the Groovy 
environment.
+ * Static methods are used with the first parameter being the destination 
class,
+ * i.e. <code>public static String reverse(String self)</code> provides a
+ * <code>reverse()</code> method for <code>String</code>.
  * <p>
  * NOTE: While this class contains many 'public' static methods, it is
  * primarily regarded as an internal class (its internal package name
@@ -7304,43 +7303,90 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
     // inject
 
     /**
-     * Performs the same function as the version of inject that takes an 
initial value, but
-     * uses the head of the Collection as the initial value, and iterates over 
the tail.
-     * <pre class="groovyTestCase">
-     * assert 1 * 2 * 3 * 4 == [ 1, 2, 3, 4 ].inject { acc, val {@code ->} acc 
* val }
-     * assert ['b'] == [['a','b'], ['b','c'], ['d','b']].inject { acc, val 
{@code ->} acc.intersect( val ) }
-     * LinkedHashSet set = [ 't', 'i', 'm' ]
-     * assert 'tim' == set.inject { a, b {@code ->} a + b }
-     * </pre>
+     * Iterates through the given object, passing the first two elements to the
+     * closure. The result is passed back (injected) to the closure along with
+     * the third element and so on until all elements have been consumed.
      *
-     * @param self         a Collection
+     * @param self         an object
      * @param closure      a closure
      * @return the result of the last closure call
-     * @throws NoSuchElementException if the collection is empty.
-     * @see #inject(Collection, Object, Closure)
+     * @throws NoSuchElementException if the iterator is empty
+     * @see #inject(Object, Object, Closure)
      * @since 1.8.7
      */
-    @SuppressWarnings("unchecked")
-    public static <T, V extends T> T inject(Collection<T> self, 
@ClosureParams(value=FromString.class,options="V,T") Closure<V> closure ) {
-        if( self.isEmpty() ) {
-            throw new NoSuchElementException( "Cannot call inject() on an 
empty collection without passing an initial value." ) ;
+    public static <T, V extends T> T inject(Object self, 
@ClosureParams(value=FromString.class,options="?,?") Closure<V> closure) {
+        Iterator<?> iter = InvokerHelper.asIterator(self);
+        if (!iter.hasNext()) {
+            throw new NoSuchElementException("Cannot call inject() on an empty 
iterable without passing an initial value.");
         }
-        Iterator<T> iter = self.iterator();
-        T head = iter.next();
-        Collection<T> tail = tail(self);
-        if (!tail.iterator().hasNext()) {
-            return head;
+        return (T) inject(iter, iter.next(), closure);
+    }
+
+    /**
+     * Iterates through the given object, passing the first two elements to the
+     * closure. The result is passed back (injected) to the closure along with
+     * the third element and so on until all elements have been consumed.
+     *
+     * <pre class="groovyTestCase">
+     * def items = [1, 2, 3, 4]
+     * def value = items.inject { acc, val -> acc * val }
+     * assert value == 1 * 2 * 3 * 4
+     *
+     * items = [['a','b'], ['b','c'], ['d','b']]
+     * value = items.inject { acc, val -> acc.intersect(val) }
+     * assert value == ['b']
+     *
+     * items = ['j', 'o', 'i', 'n'] as Set
+     * value = items.inject(String.&plus)
+     * assert value == 'join'
+     * </pre>
+     *
+     * @param self         an iterable
+     * @param closure      a closure
+     * @return the result of the last closure call
+     * @throws NoSuchElementException if the iterator is empty
+     * @see #inject(Iterable, Object, Closure)
+     * @since 5.0.0
+     */
+    public static <E, T, V extends T> T inject(Iterable<E> self, 
@ClosureParams(value=FromString.class,options="E,E") Closure<V> closure) {
+        Iterator<E> iter = self.iterator();
+        if (!iter.hasNext()) {
+            throw new NoSuchElementException("Cannot call inject() on an empty 
iterable without passing an initial value.");
         }
-        // cast with explicit weaker generics for now to keep jdk6 happy, 
TODO: find better fix
-        return (T) inject((Collection) tail, head, closure);
+        return (T) inject(iter, iter.next(), closure);
+    }
+
+    //
+
+    /**
+     * Iterates through the given object, passing in the initial value to
+     * the closure along with the first item. The result is passed back 
(injected) into
+     * the closure along with the second item. The new result is injected back 
into
+     * the closure along with the third item and so on until further iteration 
is
+     * not possible.
+     * <p>
+     * Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in functional 
parlance.
+     *
+     * @param self         an object
+     * @param initialValue some initial value
+     * @param closure      a closure
+     * @return the result of the last closure call
+     * @see #inject(Iterator, Object, Closure)
+     * @since 1.5.0
+     */
+    public static <T, U extends T, V extends T> T inject(Object self, U 
initialValue, @ClosureParams(value=FromString.class,options="U,?") Closure<V> 
closure) {
+        Iterator iter = InvokerHelper.asIterator(self);
+        return (T) inject(iter, initialValue, closure);
     }
 
     /**
-     * Iterates through the given Collection, passing in the initial value to
+     * Iterates through the given object, passing in the initial value to
      * the 2-arg closure along with the first item. The result is passed back 
(injected) into
      * the closure along with the second item. The new result is injected back 
into
-     * the closure along with the third item and so on until the entire 
collection
-     * has been used. Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in 
functional parlance.
+     * the closure along with the third item and so on until further iteration 
is
+     * not possible.
+     * <p>
+     * Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in functional 
parlance.
      *
      * Examples:
      * <pre class="groovyTestCase">
@@ -7369,25 +7415,51 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
      *                                          max('rat',  'cat')  {@code =>} 
 'rat'
      * </pre>
      *
-     * @param self         a Collection
+     * @param self         an iterable
      * @param initialValue some initial value
      * @param closure      a closure
      * @return the result of the last closure call
-     * @since 1.0
+     * @since 5.0.0
      */
-    @SuppressWarnings("unchecked")
-    public static <E, T, U extends T, V extends T> T inject(Collection<E> 
self, U initialValue, @ClosureParams(value=FromString.class,options="U,E") 
Closure<V> closure) {
-        // cast with explicit weaker generics for now to keep jdk6 happy, 
TODO: find better fix
-        return (T) inject((Iterator) self.iterator(), initialValue, closure);
+    public static <E, T, U extends T, V extends T> T inject(Iterable<E> self, 
U initialValue, @ClosureParams(value=FromString.class,options="U,E") Closure<V> 
closure) {
+        return inject(self.iterator(), initialValue, closure);
+    }
+
+    /**
+     * Iterates through the given iterator, passing in the initial value to
+     * the closure along with the first item. The result is passed back 
(injected) into
+     * the closure along with the second item. The new result is injected back 
into
+     * the closure along with the third item and so on until further iteration 
is
+     * not possible.
+     * <p>
+     * Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in functional 
parlance.
+     *
+     * @param self         an iterator
+     * @param initialValue some initial value
+     * @param closure      a closure
+     * @return the result of the last closure call
+     * @since 1.5.0
+     */
+    public static <E, T, U extends T, V extends T> T inject(Iterator<E> self, 
U initialValue, @ClosureParams(value=FromString.class,options="U,E") Closure<V> 
closure) {
+        T value = initialValue;
+        Object[] params = new Object[2];
+        while (self.hasNext()) {
+            params[0] = value;
+            params[1] = self.next();
+            value = closure.call(params);
+        }
+        return value;
     }
 
     /**
-     * Iterates through the given Map, passing in the initial value to
+     * Iterates through the given map, passing in the initial value to
      * the 2-arg Closure along with the first item (or 3-arg Closure along 
with the first key and value).
      * The result is passed back (injected) into
      * the closure along with the second item. The new result is injected back 
into
-     * the closure along with the third item and so on until the entire 
collection
-     * has been used. Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in 
functional parlance.
+     * the closure along with the third item and so on until further iteration 
is
+     * not possible.
+     * <p>
+     * Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in functional 
parlance.
      *
      * Examples:
      * <pre class="groovyTestCase">
@@ -7397,13 +7469,13 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
      * } == ['a', 'b', 'b', 'c', 'c', 'c']
      * </pre>
      *
-     * @param self         a Map
+     * @param self         a map
      * @param initialValue some initial value
      * @param closure      a 2 or 3 arg Closure
      * @return the result of the last closure call
      * @since 1.8.1
      */
-    public static <K, V, T, U extends T, W extends T> T inject(Map<K, V> self, 
U initialValue, 
@ClosureParams(value=FromString.class,options={"U,Map.Entry<K,V>","U,K,V"})  
Closure<W> closure) {
+    public static <K, V, T, U extends T, W extends T> T inject(Map<K, V> self, 
U initialValue, 
@ClosureParams(value=FromString.class,options={"U,Map.Entry<K,V>","U,K,V"}) 
Closure<W> closure) {
         T value = initialValue;
         for (Map.Entry<K, V> entry : self.entrySet()) {
             if (closure.getMaximumNumberOfParameters() == 3) {
@@ -7415,76 +7487,6 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
         return value;
     }
 
-    /**
-     * Iterates through the given Iterator, passing in the initial value to
-     * the closure along with the first item. The result is passed back 
(injected) into
-     * the closure along with the second item. The new result is injected back 
into
-     * the closure along with the third item and so on until the Iterator has 
been
-     * expired of values. Also known as foldLeft in functional parlance.
-     *
-     * @param self         an Iterator
-     * @param initialValue some initial value
-     * @param closure      a closure
-     * @return the result of the last closure call
-     * @see #inject(Collection, Object, Closure)
-     * @since 1.5.0
-     */
-    public static <E,T, U extends T, V extends T> T inject(Iterator<E> self, U 
initialValue, @ClosureParams(value=FromString.class,options="U,E") Closure<V> 
closure) {
-        T value = initialValue;
-        Object[] params = new Object[2];
-        while (self.hasNext()) {
-            Object item = self.next();
-            params[0] = value;
-            params[1] = item;
-            value = closure.call(params);
-        }
-        return value;
-    }
-
-    /**
-     * Iterates through the given Object, passing in the first value to
-     * the closure along with the first item. The result is passed back 
(injected) into
-     * the closure along with the second item. The new result is injected back 
into
-     * the closure along with the third item and so on until further iteration 
of
-     * the object is not possible. Also known as foldLeft in functional 
parlance.
-     *
-     * @param self         an Object
-     * @param closure      a closure
-     * @return the result of the last closure call
-     * @throws NoSuchElementException if the collection is empty.
-     * @see #inject(Collection, Object, Closure)
-     * @since 1.8.7
-     */
-    @SuppressWarnings("unchecked")
-    public static <T, V extends T> T inject(Object self, Closure<V> closure) {
-        Iterator iter = InvokerHelper.asIterator(self);
-        if( !iter.hasNext() ) {
-            throw new NoSuchElementException( "Cannot call inject() over an 
empty iterable without passing an initial value." ) ;
-        }
-        Object initialValue = iter.next() ;
-        return (T) inject(iter, initialValue, closure);
-    }
-
-    /**
-     * Iterates through the given Object, passing in the initial value to
-     * the closure along with the first item. The result is passed back 
(injected) into
-     * the closure along with the second item. The new result is injected back 
into
-     * the closure along with the third item and so on until further iteration 
of
-     * the object is not possible. Also known as foldLeft in functional 
parlance.
-     *
-     * @param self         an Object
-     * @param initialValue some initial value
-     * @param closure      a closure
-     * @return the result of the last closure call
-     * @see #inject(Collection, Object, Closure)
-     * @since 1.5.0
-     */
-    @SuppressWarnings("unchecked")
-    public static <T, U extends T, V extends T> T inject(Object self, U 
initialValue, Closure<V> closure) {
-        Iterator iter = InvokerHelper.asIterator(self);
-        return (T) inject(iter, initialValue, closure);
-    }
-
     
//--------------------------------------------------------------------------
     // inspect
 
@@ -16680,15 +16682,25 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
     }
 
     @Deprecated(since = "5.0.0")
-    public static <E,T, V extends T> T inject(E[] self, 
@ClosureParams(value=FromString.class,options="E,E") Closure<V> closure) {
+    public static <E, T, V extends T> T inject(E[] self, 
@ClosureParams(value=FromString.class,options="E,E") Closure<V> closure) {
         return ArrayGroovyMethods.inject(self, closure);
     }
 
+    @Deprecated(since = "5.0.0")
+    public static <E, T, V extends T> T inject(Collection<E> self, 
@ClosureParams(value=FromString.class,options="E,E") Closure<V> closure) {
+        return inject((Iterable<E>) self, closure);
+    }
+
     @Deprecated(since = "5.0.0")
     public static <E, T, U extends T, V extends T> T inject(E[] self, U 
initialValue, @ClosureParams(value=FromString.class,options="U,E") Closure<V> 
closure) {
         return ArrayGroovyMethods.inject(self, initialValue, closure);
     }
 
+    @Deprecated(since = "5.0.0")
+    public static <E, T, U extends T, V extends T> T inject(Collection<E> 
self, U initialValue, @ClosureParams(value=FromString.class,options="U,E") 
Closure<V> closure) {
+        return inject((Iterable<E>) self, initialValue, closure);
+    }
+
     @Deprecated(since = "5.0.0")
     public static <T> Iterator<T> iterator(T[] self) {
         return ArrayGroovyMethods.iterator(self);
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 fa97205798..7dff684042 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -5465,7 +5465,7 @@ out:                if (mn.size() != 1) {
                 }
             }
 
-            // in case of "<T, U extends Type<T>>" we can learn about "T" from 
a resolved "U"
+            // in case of "<T, U extends Type<T>>", we can learn about "T" 
from a resolved "U"
             extractGenericsConnectionsForBoundTypes(methodGenericTypes, 
resolvedPlaceholders);
         }
 
@@ -5506,7 +5506,7 @@ out:                if (mn.size() != 1) {
             } else if (a instanceof MapExpression) {
                 actuals[i] = getLiteralResultType(pt, at, LinkedHashMap_TYPE);
             } else if (a instanceof ConstructorCallExpression) {
-                inferDiamondType((ConstructorCallExpression) a, pt); // 
GROOVY-8974, GROOVY-9983, GROOVY-10086, et al.
+                inferDiamondType((ConstructorCallExpression) a, pt); // 
GROOVY-8974, GROOVY-9983, GROOVY-10086, GROOVY-10890, et al.
             } else if (a instanceof TernaryExpression && at.getGenericsTypes() 
!= null && at.getGenericsTypes().length == 0) {
                 // GROOVY-9983: double diamond scenario -- "m(flag ? new 
Type<>(...) : new Type<>(...))"
                 
typeCheckingContext.pushEnclosingBinaryExpression(assignX(varX(p), a, a));
@@ -5556,6 +5556,7 @@ out:                if (mn.size() != 1) {
 
     private static void extractGenericsConnectionsForBoundTypes(final 
GenericsType[] spec, final Map<GenericsTypeName, GenericsType> target) {
         if (spec.length < 2) return;
+        Map<GenericsTypeName, GenericsType> outer = new HashMap<>();
         for (GenericsType tp : spec) {
             ClassNode[] bounds = tp.getUpperBounds();
             if (bounds == null || bounds.length == 0) continue;
@@ -5568,8 +5569,9 @@ out:                if (mn.size() != 1) {
             for (ClassNode bound : bounds) {
                 extractGenericsConnections(inner,value.getType(),bound);
             }
-            inner.forEach(target::putIfAbsent); // GROOVY-10890
+            inner.forEach((k, v) -> outer.merge(k, v, 
StaticTypeCheckingSupport::getCombinedGenericsType)); // GROOVY-5893
         }
+        outer.forEach(target::putIfAbsent);
     }
 
     private static ClassNode[] collateMethodReferenceParameterTypes(final 
MethodPointerExpression source, final MethodNode target) {
diff --git 
a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy 
b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
index 3edaff876b..64a4e9a68e 100644
--- a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
@@ -18,6 +18,7 @@
  */
 package groovy.transform.stc
 
+import groovy.test.NotYetImplemented
 import org.codehaus.groovy.control.customizers.ImportCustomizer
 
 /**
@@ -1498,6 +1499,7 @@ class ClosureParamTypeInferenceSTCTest extends 
StaticTypeCheckingTestCase {
             assert [a:'1',b:'2',c:'C'].groupBy { 
it.key.toUpperCase()==it.value?1:0 } == [0:[a:'1',b:'2'], 1:[c:'C']]
         '''
     }
+
     void testDGM_groupEntriesBy() {
         assertScript '''
             def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupEntriesBy { k,v -> v % 
2 }
@@ -1505,57 +1507,71 @@ class ClosureParamTypeInferenceSTCTest extends 
StaticTypeCheckingTestCase {
             result = [a:1,b:2,c:3,d:4,e:5,f:6].groupEntriesBy { e -> e.value % 
2 }
             assert result[0]*.key == ["b", "d", "f"]
             assert result[1]*.value == [1, 3, 5]
-     '''
-    }
-
-    void testDGM_injectOnCollectionWithInitialValue() {
-        assertScript '''
-            assert ['a','bb','ccc'].inject(0) { acc, str -> acc += 
str.length(); acc } == 6
         '''
     }
 
-    void testDGM_injectOnArrayWithInitialValue() {
+    void testDGM_injectOnCollection() {
         assertScript '''
-            String[] array = ['a','bb','ccc']
-            assert array.inject(0) { acc, str -> acc += str.length(); acc } == 
6
+            def items = ['a','bb','ccc']
+            def value = items.inject { acc, str -> acc += str.toUpperCase(); 
acc }
+            assert value == 'aBBCCC'
         '''
-    }
-
-    void testDGM_injectOnIteratorWithInitialValue() {
-        assertScript '''
-            assert ['a','bb','ccc'].iterator().inject(0) { acc, str -> acc += 
str.length(); acc } == 6
+        assertScript '''import 
org.codehaus.groovy.runtime.DefaultGroovyMethods as DGM
+            def items = ['a','bb','ccc']
+            def value = DGM.inject(items, { acc, str -> acc += 
str.toUpperCase(); acc })
+            assert value == 'aBBCCC'
         '''
     }
-
-    void testDGM_injectOnCollection() {
+    @NotYetImplemented
+    void testDGM_injectOnIterator() {
         assertScript '''
-            assert ['a','bb','ccc'].inject { acc, str -> acc += 
str.toUpperCase(); acc } == 'aBBCCC'
+            def items = ['a','bb','ccc'].iterator()
+            def value = items.inject { acc, str -> acc += str.toUpperCase(); 
acc }
+            //                ^^^^^^ inject(Object,Closure) without metadata
+            assert value == 'aBBCCC'
         '''
     }
-
     void testDGM_injectOnArray() {
         assertScript '''
-            String[] array = ['a','bb','ccc']
-            assert array.inject { acc, str -> acc += str.toUpperCase(); acc } 
== 'aBBCCC'
+            def items = new String[]{'a','bb','ccc'}
+            def value = items.inject { acc, str -> acc += str.toUpperCase(); 
acc }
+            assert value == 'aBBCCC'
         '''
     }
 
-    void testDGM_injectOnCollectionWithInitialValueDirect() {
+    void testDGM_injectOnCollectionWithInitialValue() {
+        assertScript '''
+            def items = ['a','bb','ccc']
+            def value = items.inject(0) { acc, str -> acc += str.length(); acc 
}
+            assert value == 6
+        '''
         assertScript '''import 
org.codehaus.groovy.runtime.DefaultGroovyMethods as DGM
-            assert DGM.inject(['a','bb','ccc'],0) { acc, str -> acc += 
str.length(); acc } == 6
+            def items = ['a','bb','ccc']
+            def value = DGM.inject(items, 0, { acc, str -> acc += 
str.length(); acc })
+            assert value == 6
         '''
     }
-
-    void testDGM_injectOnCollectionDirect() {
-        assertScript '''import 
org.codehaus.groovy.runtime.DefaultGroovyMethods as DGM
-            assert DGM.inject(['a','bb','ccc']) { acc, str -> acc += 
str.toUpperCase(); acc } == 'aBBCCC'
+    void testDGM_injectOnIteratorWithInitialValue() {
+        assertScript '''
+            def items = ['a','bb','ccc'].iterator()
+            def value = items.inject(0) { acc, str -> acc += str.length(); acc 
}
+            assert value == 6
         '''
     }
-
-    void testDGM_injectOnMap() {
+    void testDGM_injectOnArrayWithInitialValue() {
+        assertScript '''
+            def items = new String[]{'a','bb','ccc'}
+            def value = items.inject(0) { acc, str -> acc += str.length(); acc 
}
+            assert value == 6
+        '''
+    }
+    void testDGM_injectOnMapWithInitialValue() {
         assertScript '''
-            assert [a:1,b:2].inject(0) { acc, entry -> acc += entry.value; 
acc} == 3
-            assert [a:1,b:2].inject(0) { acc, k, v -> acc += v; acc} == 3
+            def items = [a:1,b:2]
+            def value = items.inject(0) { acc, entry -> acc += entry.value; 
acc}
+            assert value == 3
+            value = items.inject(0) { acc, k, v -> acc += v; acc}
+            assert value == 3
         '''
     }
 
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy 
b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 86024fae2b..e21e84ba3d 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -4050,9 +4050,21 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase 
{
                 list.each { int i -> sum += i }
                 assert sum == 6
 
-                $type sumWithInject = list.inject(0, { int x, int y -> x + y })
-                //    ^^^^^^^^^^^^^ T ^^^^ E[]    ^ U      ^ U    ^ E  ^^^^^ V
-                assert sumWithInject == 6
+                $type  sumViaInject = list.inject(0, { int x, int y -> x + y })
+                //     ^^^^^^^^^^^^ T ^^^^ E[]    ^ U      ^ U    ^ E  ^^^^^ V
+                assert sumViaInject == 6
+            """
+        }
+    }
+
+    // GROOVY-5893, GROOVY-7934
+    void testPlusInClosure2() {
+        for (type in ['def', 'var', 'Object', 'String', 'Serializable']) {
+            assertScript """
+                String[] array = ['1', '2', '3']
+                $type  joinViaInject = array.inject(0, { x,y -> (x + ',' + y) 
})
+                //                     integer or string ^ ^ string
+                assert joinViaInject == '0,1,2,3'
             """
         }
     }
@@ -4616,30 +4628,29 @@ class GenericsSTCTest extends 
StaticTypeCheckingTestCase {
     }
 
     // GROOVY-6504
-    void testInjectMethodWithInitialValueChoosesTheCollectionVersion() {
-        assertScript '''import 
org.codehaus.groovy.transform.stc.ExtensionMethodNode
+    void testInjectMethodWithInitialValueChoosesTheIterableVersion() {
+        assertScript '''
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 def method = 
node.rightExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
                 assert method.name == 'inject'
-                assert method instanceof ExtensionMethodNode
                 method = method.extensionMethodNode
-                assert method.parameters[0].type == make(Collection)
+                assert method.parameters[0].type == ITERABLE_TYPE
             })
             def result = ['a','bb','ccc'].inject(0) { int acc, String str -> 
acc += str.length(); acc }
-            assert  result == 6
+            assert result == 6
         '''
     }
 
     // GROOVY-6504
-    void testInjectMethodWithInitialValueChoosesTheCollectionVersionUsingDGM() 
{
+    void testInjectMethodWithInitialValueChoosesTheCollectionVersion() {
         assertScript '''import org.codehaus.groovy.runtime.DefaultGroovyMethods
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 def method = 
node.rightExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
                 assert method.name == 'inject'
-                assert method.parameters[0].type == make(Collection)
+                assert method.parameters[0].type == COLLECTION_TYPE
             })
-            def result = DefaultGroovyMethods.inject(['a','bb','ccc'],0, { int 
acc, String str -> acc += str.length(); acc })
-            assert  result == 6
+            def result = DefaultGroovyMethods.inject(['a','bb','ccc'], 0, { 
int acc, String str -> acc += str.length(); acc })
+            assert result == 6
         '''
     }
 
diff --git 
a/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy 
b/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy
index 4a599b2827..03c8aae050 100644
--- 
a/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy
+++ 
b/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy
@@ -18,10 +18,16 @@
  */
 package org.codehaus.groovy.classgen.asm.sc
 
+import groovy.test.NotYetImplemented
 import groovy.transform.stc.GenericsSTCTest
 
 /**
  * Unit tests for static compilation : generics.
  */
 class GenericsStaticCompileTest extends GenericsSTCTest implements 
StaticCompilationTestSupport {
+
+    @NotYetImplemented
+    void testPlusInClosure2() {
+        super.testPlusInClosure2()
+    }
 }

Reply via email to