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 2021/08/18 15:41:26 UTC

[groovy] branch GROOVY_3_0_X updated (7c62395 -> 45ec584)

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

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


    from 7c62395  GROOVY-10033, GROOVY-10046, GROOVY-10047: refs for ctor and static calls
     new fffe63f  GROOVY-9915: use type(s) from static call site to resolve generics
     new c24376d  GROOVY-9971: STC: restore "Closure<String> c = { -> return "$v" }"
     new 45ec584  GROOVY-9984: don't use null arguments for return type generics inference

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../groovy/classgen/asm/ClosureWriter.java         |  31 ++---
 .../transform/stc/StaticTypeCheckingVisitor.java   |  97 +++++++++-----
 .../groovy/transform/stc/ClosuresSTCTest.groovy    |  15 +++
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 140 ++++++++++++---------
 4 files changed, 177 insertions(+), 106 deletions(-)

[groovy] 02/03: GROOVY-9971: STC: restore "Closure c = { -> return "$v" }"

Posted by em...@apache.org.
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

commit c24376de8a60a8f46e1fab73d40e3b35584d8a66
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Apr 4 12:50:38 2021 -0500

    GROOVY-9971: STC: restore "Closure<String> c = { -> return "$v" }"
    
    - convert GString to String at the point of return
    
    Conflicts:
    	src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
    	src/test/groovy/transform/stc/ClosuresSTCTest.groovy
---
 .../groovy/classgen/asm/ClosureWriter.java         | 31 ++++----
 .../transform/stc/StaticTypeCheckingVisitor.java   | 90 +++++++++++++++-------
 .../groovy/transform/stc/ClosuresSTCTest.groovy    | 15 ++++
 3 files changed, 93 insertions(+), 43 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
index 13e1370..607d6e3 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
@@ -38,6 +38,7 @@ import org.codehaus.groovy.ast.expr.FieldExpression;
 import org.codehaus.groovy.ast.expr.TupleExpression;
 import org.codehaus.groovy.ast.expr.VariableExpression;
 import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.tools.GenericsUtils;
 import org.codehaus.groovy.classgen.AsmClassGenerator;
 import org.codehaus.groovy.classgen.Verifier;
 import org.objectweb.asm.MethodVisitor;
@@ -57,6 +58,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_RETURN_TYPE;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
 import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
@@ -188,7 +190,6 @@ public class ClosureWriter {
         ClassNode classNode = controller.getClassNode();
         ClassNode outerClass = controller.getOutermostClass();
         String name = genClosureClassName();
-        boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass();
 
         Parameter[] parameters = expression.getParameters();
         if (parameters == null) {
@@ -204,20 +205,22 @@ public class ClosureWriter {
         Parameter[] localVariableParams = getClosureSharedVariables(expression);
         removeInitialValues(localVariableParams);
 
+        // GROOVY-9971: closure return type is mapped to Groovy cast by classgen
+        ClassNode returnType = expression.getNodeMetaData(INFERRED_RETURN_TYPE);
+        if (returnType == null) returnType = ClassHelper.OBJECT_TYPE; // not STC or unknown path
+        else if (returnType.isPrimaryClassNode()) returnType = returnType.getPlainNodeReference();
+        else if (ClassHelper.isPrimitiveType(returnType)) returnType = ClassHelper.getWrapper(returnType);
+        else if (GenericsUtils.hasUnresolvedGenerics(returnType)) returnType = GenericsUtils.nonGeneric(returnType);
+
         InnerClassNode answer = new InnerClassNode(classNode, name, modifiers, ClassHelper.CLOSURE_TYPE.getPlainNodeReference());
         answer.setEnclosingMethod(controller.getMethodNode());
+        answer.setScriptBody(controller.isInScriptBody());
+        answer.setSourcePosition(expression);
+        answer.setStaticClass(controller.isStaticMethod() || classNode.isStaticClass());
         answer.setSynthetic(true);
         answer.setUsingGenerics(outerClass.isUsingGenerics());
-        answer.setSourcePosition(expression);
 
-        if (staticMethodOrInStaticClass) {
-            answer.setStaticClass(true);
-        }
-        if (controller.isInScriptBody()) {
-            answer.setScriptBody(true);
-        }
-        MethodNode method =
-                answer.addMethod("doCall", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
+        MethodNode method = answer.addMethod("doCall", ACC_PUBLIC, returnType, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
         method.setSourcePosition(expression);
 
         VariableScope varScope = expression.getVariableScope();
@@ -229,14 +232,14 @@ public class ClosureWriter {
         }
         if (parameters.length > 1
                 || (parameters.length == 1
-                && parameters[0].getType() != null
-                && parameters[0].getType() != ClassHelper.OBJECT_TYPE
-                && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType().getComponentType()))) {
+                    && parameters[0].getType() != null
+                    && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType())
+                    && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType().getComponentType()))) {
             // let's add a typesafe call method
             MethodNode call = new MethodNode(
                     "call",
                     ACC_PUBLIC,
-                    ClassHelper.OBJECT_TYPE,
+                    returnType,
                     parameters,
                     ClassNode.EMPTY_ARRAY,
                     returnS(callThisX("doCall", args(parameters))));
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 f6d1b19..7b5f4fc 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -263,6 +263,7 @@ import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isBitO
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isBoolIntrinsicOp;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isClassClassNodeWrappingConcreteType;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isCompareToBoolean;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isGStringOrGStringStringLUB;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isOperationInGroup;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isParameterizedWithGStringOrGStringString;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isParameterizedWithString;
@@ -748,9 +749,16 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
                 lType = getType(leftExpression);
             } else {
-                lType = getType(leftExpression);
-                if (op == ASSIGN && isFunctionalInterface(lType)) {
-                    processFunctionalInterfaceAssignment(lType, rightExpression);
+                if (op != ASSIGN && op != ELVIS_EQUAL) {
+                    lType = getType(leftExpression);
+                } else {
+                    lType = getOriginalDeclarationType(leftExpression);
+
+                    if (isFunctionalInterface(lType)) {
+                        processFunctionalInterfaceAssignment(lType, rightExpression);
+                    } else if (isClosureWithType(lType) && rightExpression instanceof ClosureExpression) {
+                        storeInferredReturnType(rightExpression, getCombinedBoundType(lType.getGenericsTypes()[0]));
+                    }
                 }
                 rightExpression.visit(this);
             }
@@ -997,13 +1005,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    private boolean isCompoundAssignment(final Expression exp) {
+    private static boolean isClosureWithType(final ClassNode type) {
+        return type.equals(CLOSURE_TYPE) && Optional.ofNullable(type.getGenericsTypes()).filter(gts -> gts != null && gts.length == 1).isPresent();
+    }
+
+    private static boolean isCompoundAssignment(final Expression exp) {
         if (!(exp instanceof BinaryExpression)) return false;
         int type = ((BinaryExpression) exp).getOperation().getType();
         return isAssignment(type) && type != ASSIGN;
     }
 
-    private Token getOpWithoutEqual(final Expression exp) {
+    private static Token getOpWithoutEqual(final Expression exp) {
         if (!(exp instanceof BinaryExpression)) return null; // should never happen
         Token op = ((BinaryExpression) exp).getOperation();
         int typeWithoutEqual = TokenUtil.removeAssignment(op.getType());
@@ -1836,28 +1848,35 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             typeCheckingContext.isInStaticContext = node.isInStaticContext();
             currentField = node;
             visitAnnotations(node);
-            Expression init = node.getInitialExpression();
-            if (init != null) {
-                ClassNode lType = getType(node);
-                if (isFunctionalInterface(lType)) { // GROOVY-9977
-                    processFunctionalInterfaceAssignment(lType, init);
-                }
-                init.visit(this);
-                ClassNode rType = getType(init);
-                if (init instanceof ConstructorCallExpression) {
-                    inferDiamondType((ConstructorCallExpression) init, lType);
-                }
-
-                FieldExpression left = new FieldExpression(node);
-                BinaryExpression bexp = assignX(left, init, node);
-                typeCheckAssignment(bexp, left, lType, init, getResultType(lType, ASSIGN, rType, bexp));
-            }
+            visitInitialExpression(node.getInitialExpression(), new FieldExpression(node), node);
         } finally {
             currentField = null;
             typeCheckingContext.isInStaticContext = osc;
         }
     }
 
+    private void visitInitialExpression(final Expression value, final Expression target, final ASTNode position) {
+        if (value != null) {
+            ClassNode lType = target.getType();
+            if (isFunctionalInterface(lType)) { // GROOVY-9977
+                processFunctionalInterfaceAssignment(lType, value);
+            } else if (isClosureWithType(lType) && value instanceof ClosureExpression) {
+                storeInferredReturnType(value, getCombinedBoundType(lType.getGenericsTypes()[0]));
+            }
+
+            typeCheckingContext.pushEnclosingBinaryExpression(assignX(target, value, position));
+
+            value.visit(this);
+            ClassNode rType = getType(value);
+            if (value instanceof ConstructorCallExpression) {
+                inferDiamondType((ConstructorCallExpression) value, lType);
+            }
+
+            BinaryExpression dummy = typeCheckingContext.popEnclosingBinaryExpression();
+            typeCheckAssignment(dummy, target, lType, value, getResultType(lType, ASSIGN, rType, dummy));
+        }
+    }
+
     @Override
     public void visitForLoop(final ForStatement forLoop) {
         // collect every variable expression used in the loop body
@@ -2146,11 +2165,15 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         Expression expression = statement.getExpression();
         ClassNode type;
         if (expression instanceof VariableExpression && hasInferredReturnType(expression)) {
-            type = expression.getNodeMetaData(INFERRED_RETURN_TYPE);
+            type = getInferredReturnType(expression);
         } else {
             type = getType(expression);
         }
         if (typeCheckingContext.getEnclosingClosure() != null) {
+            ClassNode inferredReturnType = getInferredReturnType(typeCheckingContext.getEnclosingClosure().getClosureExpression());
+            if (inferredReturnType != null && inferredReturnType.equals(STRING_TYPE) && isGStringOrGStringStringLUB(type)) {
+                type = STRING_TYPE; // GROOVY-9971: convert GString to String at point of return
+            }
             return type;
         }
         MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
@@ -2160,7 +2183,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     && !type.equals(void_WRAPPER_TYPE)
                     && !checkCompatibleAssignmentTypes(enclosingMethod.getReturnType(), type, null, false)) {
                 if (!extension.handleIncompatibleReturnType(statement, type)) {
-                    addStaticTypeError("Cannot return value of type " + type.toString(false) + " on method returning type " + enclosingMethod.getReturnType().toString(false), expression);
+                    addStaticTypeError("Cannot return value of type " + prettyPrintType(type) + " on method returning type " + prettyPrintType(enclosingMethod.getReturnType()), expression);
                 }
             } else {
                 ClassNode previousType = getInferredReturnType(enclosingMethod);
@@ -2183,8 +2206,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     protected void addClosureReturnType(final ClassNode returnType) {
-        if (VOID_TYPE.equals(returnType)) return; // GROOVY-8202
-        typeCheckingContext.getEnclosingClosure().addReturnType(returnType);
+        if (returnType != null && !returnType.equals(VOID_TYPE)) // GROOVY-8202
+            typeCheckingContext.getEnclosingClosure().addReturnType(returnType);
     }
 
     @Override
@@ -2710,14 +2733,21 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             if (visitClosures && expression instanceof ClosureExpression
                     || !visitClosures && !(expression instanceof ClosureExpression)) {
                 if (i < params.length && visitClosures) {
-                    Parameter param = params[i];
-                    checkClosureWithDelegatesTo(receiver, selectedMethod, newArgs, params, expression, param);
+                    Parameter target = params[i];
+                    ClassNode targetType = target.getType();
+                    ClosureExpression source = (ClosureExpression) expression;
+                    checkClosureWithDelegatesTo(receiver, selectedMethod, newArgs, params, source, target);
                     if (selectedMethod instanceof ExtensionMethodNode) {
                         if (i > 0) {
-                            inferClosureParameterTypes(receiver, arguments, (ClosureExpression) expression, param, selectedMethod);
+                            inferClosureParameterTypes(receiver, arguments, source, target, selectedMethod);
                         }
                     } else {
-                        inferClosureParameterTypes(receiver, newArgs, (ClosureExpression) expression, param, selectedMethod);
+                        inferClosureParameterTypes(receiver, newArgs, source, target, selectedMethod);
+                    }
+                    if (isFunctionalInterface(targetType)) {
+                        storeInferredReturnType(source, GenericsUtils.parameterizeSAM(targetType).getV2());
+                    } else if (isClosureWithType(targetType)) {
+                        storeInferredReturnType(source, getCombinedBoundType(targetType.getGenericsTypes()[0]));
                     }
                 }
                 expression.visit(this);
@@ -4090,6 +4120,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         Expression target = expression.getExpression();
         if (isFunctionalInterface(type)) { // GROOVY-9997
             processFunctionalInterfaceAssignment(type, target);
+        } else if (isClosureWithType(type) && target instanceof ClosureExpression) {
+            storeInferredReturnType(target, getCombinedBoundType(type.getGenericsTypes()[0]));
         }
 
         target.visit(this);
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index 29e9e2e..b148665 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -192,6 +192,21 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-9971
+    void testClosureReturnTypeInference8() {
+        assertScript '''
+            def m(Closure<String> c) {
+                c.call()
+            }
+            final x = 123
+            Closure<String> c = { -> "x=$x" }
+            String type = c.call().class.name
+            assert type == 'java.lang.String'
+            type = (m { -> "x=$x" }).class.name
+            assert type == 'java.lang.String' // not GStringImpl
+        '''
+    }
+
     // GROOVY-5145
     void testCollect() {
         assertScript '''

[groovy] 03/03: GROOVY-9984: don't use null arguments for return type generics inference

Posted by em...@apache.org.
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

commit 45ec584724788d89b9151ef2a50b71a29af569c7
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Mar 15 11:26:13 2021 -0500

    GROOVY-9984: don't use null arguments for return type generics inference
    
    Conflicts:
    	src/test/groovy/transform/stc/GenericsSTCTest.groovy
---
 .../transform/stc/StaticTypeCheckingVisitor.java   |  2 ++
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 22 +++++++++++++++++-----
 2 files changed, 19 insertions(+), 5 deletions(-)

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 7b5f4fc..876e34e 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -5214,6 +5214,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         int paramLength = parameters.length;
         if (expressions.size() >= paramLength) {
             for (int i = 0; i < paramLength; i += 1) {
+                if (isNullConstant(expressions.get(i)))
+                    continue; // GROOVY-9984: skip null
                 boolean lastArg = (i == paramLength - 1);
                 ClassNode paramType = parameters[i].getType();
                 ClassNode argumentType = getDeclaredOrInferredType(expressions.get(i));
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 13483df..61de438 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -214,26 +214,38 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testDiamondInferrenceFromConstructorWithoutAssignment() {
+    void testDiamondInferrenceFromConstructor2() {
         assertScript '''
             new HashSet<>(Arrays.asList(0L,0L));
         '''
     }
 
-    void testDiamondInferrenceFromConstructor2() {
+    void testDiamondInferrenceFromConstructor3() {
         shouldFailWithMessages '''
             Set< Number > s3 = new HashSet<>(Arrays.asList(0L,0L));
         ''', 'Cannot assign java.util.HashSet <java.lang.Long> to: java.util.Set <Number>'
     }
 
-    void testDiamondInferrenceFromConstructor3() {
+    void testDiamondInferrenceFromConstructor4() {
         assertScript '''
             Set<Number> s4 = new HashSet<Number>(Arrays.asList(0L,0L))
         '''
     }
+    // GROOVY-9984
+    void testDiamondInferrenceFromConstructor5() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> {
+                T p
+            }
+
+            C<Integer> c = new C<>(null)
+            assert c.p === null
+        '''
+    }
 
     // GROOVY-9996
-    void testDiamondInferrenceFromConstructor8a() {
+    void testDiamondInferrenceFromConstructor6() {
         assertScript '''
             @groovy.transform.TupleConstructor(defaults=false)
             class C<T> {
@@ -250,7 +262,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-10011
-    void testDiamondInferrenceFromConstructor8b() {
+    void testDiamondInferrenceFromConstructor7() {
         assertScript '''
             @groovy.transform.TupleConstructor(defaults=false)
             class C<T> {

[groovy] 01/03: GROOVY-9915: use type(s) from static call site to resolve generics

Posted by em...@apache.org.
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

commit fffe63ff8694896022210348547d0ebebb56fc6e
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Jan 27 10:23:15 2021 -0600

    GROOVY-9915: use type(s) from static call site to resolve generics
---
 .../transform/stc/StaticTypeCheckingVisitor.java   |   5 +-
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 118 +++++++++++----------
 2 files changed, 65 insertions(+), 58 deletions(-)

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 ed0c92c..f6d1b19 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2619,8 +2619,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             for (Receiver<String> currentReceiver : receivers) {
                 mn = findMethod(currentReceiver.getType(), name, args);
                 if (!mn.isEmpty()) {
-                    if (mn.size() == 1)
+                    if (mn.size() == 1) {
+                        // GROOVY-8961, GROOVY-9734, GROOVY-9915
+                        resolvePlaceholdersFromImplicitTypeHints(args, argumentList, mn.get(0));
                         typeCheckMethodsWithGenericsOrFail(currentReceiver.getType(), args, mn.get(0), call);
+                    }
                     chosenReceiver = currentReceiver;
                     break;
                 }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 0f29974..13483df 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -665,68 +665,72 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         ''', '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
+    // GROOVY-8961, GROOVY-9915
     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()
-        '''
+        ['', 'static'].each { mods ->
+            assertScript """
+                $mods void setM(List<String> strings) {
+                }
+                void test() {
+                  m = Collections.emptyList() // Cannot assign value of type List<T> to variable of List<String>
+                }
+                test()
+            """
+            assertScript """
+                $mods void setM(Collection<String> strings) {
+                }
+                void test() {
+                  m = Collections.emptyList()
+                }
+                test()
+            """
+            assertScript """
+                $mods void setM(Iterable<String> strings) {
+                }
+                void test() {
+                  m = Collections.emptyList()
+                }
+                test()
+            """
 
-        shouldFailWithMessages '''
-            void setM(List<String> strings) {
-            }
-            void test() {
-              m = Collections.<Integer>emptyList()
-            }
-        ''', '[Static type checking] - Cannot assign value of type java.util.List <Integer> to variable of type java.util.List <String>'
+            shouldFailWithMessages """
+                $mods void setM(List<String> strings) {
+                }
+                void test() {
+                  m = Collections.<Integer>emptyList()
+                }
+            """, '[Static type checking] - Cannot assign value of type java.util.List <Integer> to variable of type java.util.List <String>'
+        }
     }
 
-    // GROOVY-9734
+    // GROOVY-9734, GROOVY-9915
     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()
-        '''
+        ['', 'static'].each { mods ->
+            assertScript """
+                $mods void m(List<String> strings) {
+                }
+                void test() {
+                  m(Collections.emptyList()) // Cannot call m(List<String>) with arguments [List<T>]
+                }
+                test()
+            """
+            assertScript """
+                $mods void m(Collection<String> strings) {
+                }
+                void test() {
+                  m(Collections.emptyList())
+                }
+                test()
+            """
+            assertScript """
+                $mods void m(Iterable<String> strings) {
+                }
+                void test() {
+                  m(Collections.emptyList())
+                }
+                test()
+            """
+        }
     }
 
     // GROOVY-9751