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/26 23:57:57 UTC

[groovy] branch GROOVY_2_5_X updated (c666e5b -> 0273ab8)

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

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


    from c666e5b  GROOVY-9896, GROOVY-4727: add return for last case if no default present
     new 593c021  GROOVY-8961, GROOVY-9734: use type(s) from call site to resolve generics
     new df3630e  GROOVY-9462: print placeholder using getUnresolvedName()
     new 0273ab8  GROOVY-9591: no error for dynamic variable in closure in static context

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:
 .../codehaus/groovy/ast/tools/GenericsUtils.java   | 133 ++++++-------
 .../codehaus/groovy/control/StaticVerifier.java    |  94 ++++-----
 .../groovy/tools/javac/JavaStubGenerator.java      |   2 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   |  50 +++++
 src/test/groovy/bugs/Groovy8327.groovy             | 221 +++++++++++++++++++++
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  69 ++++++-
 ...PropertiesStubTest.groovy => Groovy9462.groovy} |  62 +++---
 7 files changed, 467 insertions(+), 164 deletions(-)
 create mode 100644 src/test/groovy/bugs/Groovy8327.groovy
 copy src/test/org/codehaus/groovy/tools/stubgenerator/{TraitPropertiesStubTest.groovy => Groovy9462.groovy} (51%)

[groovy] 02/03: GROOVY-9462: print placeholder using getUnresolvedName()

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

commit df3630ee9c5185ecc177b80eb6c52330f2bb4c2f
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Mar 12 11:29:52 2020 -0500

    GROOVY-9462: print placeholder using getUnresolvedName()
    
    (cherry picked from commit 37ed4ae9b21171ed6f4f45e20aefc4951189c2d0)
---
 .../groovy/tools/javac/JavaStubGenerator.java      |  2 +-
 .../groovy/tools/stubgenerator/Groovy9462.groovy   | 64 ++++++++++++++++++++++
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
index 829b0f9..fc095b0 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
@@ -812,7 +812,7 @@ public class JavaStubGenerator {
             printType(out, type.getComponentType());
             out.print("[]");
         } else if (java5 && type.isGenericsPlaceHolder()) {
-            out.print(type.getGenericsTypes()[0].getName());
+            out.print(type.getUnresolvedName());
         } else {
             printGenericsBounds(out, type, false);
         }
diff --git a/src/test/org/codehaus/groovy/tools/stubgenerator/Groovy9462.groovy b/src/test/org/codehaus/groovy/tools/stubgenerator/Groovy9462.groovy
new file mode 100644
index 0000000..addb970
--- /dev/null
+++ b/src/test/org/codehaus/groovy/tools/stubgenerator/Groovy9462.groovy
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.tools.stubgenerator
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+final class Groovy9462 extends StringSourcesStubTestCase {
+
+    @Override
+    Map<String, String> provideSources() {
+        return [
+            'Main.java': '''
+                public class Main {
+                    public static void main(String[] args) {
+                        new Neo4jRelationship<Byte, Character>(null, null, "Type");
+                    }
+                }
+            ''',
+            'Neo4jRelationship.groovy': '''
+                @groovy.transform.CompileStatic
+                class Neo4jRelationship<F, T> implements Relationship<F, T> {
+                    String type
+
+                    Neo4jRelationship(F from, T to, String type) {
+                        this.from = from
+                        this.to = to
+                        this.type = type
+                    }
+                }
+            ''',
+            'Relationship.groovy': '''
+                @groovy.transform.CompileStatic
+                trait Relationship<F, T> {
+                    Long id
+                    F from
+                    T to
+                }
+            '''
+        ]
+    }
+
+    @Override
+    void verifyStubs() {
+        def javaStub = stubJavaSourceFor('Neo4jRelationship')
+        assert javaStub.contains("public Neo4jRelationship${System.lineSeparator()}(F from, T to,")
+    }
+}

[groovy] 03/03: GROOVY-9591: no error for dynamic variable in closure in static context

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

commit 0273ab8e71138a5df9d064bbf8161e3a48fab28c
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Jun 13 14:17:41 2020 -0500

    GROOVY-9591: no error for dynamic variable in closure in static context
    
    (cherry picked from commit 836887ceec754027083339e56a8fedd3a12c7380)
    
    Conflicts:
    	src/main/java/org/codehaus/groovy/control/StaticVerifier.java
    	src/test/groovy/bugs/Groovy8327Bug.groovy
---
 .../codehaus/groovy/control/StaticVerifier.java    |  94 ++++-----
 src/test/groovy/bugs/Groovy8327.groovy             | 221 +++++++++++++++++++++
 2 files changed, 264 insertions(+), 51 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/control/StaticVerifier.java b/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
index a0442a4..daeb2e5 100644
--- a/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
+++ b/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
@@ -42,26 +42,21 @@ import java.util.Set;
 import static org.apache.groovy.ast.tools.ClassNodeUtils.isInnerClass;
 
 /**
- * Verifier to check non-static access in static contexts
+ * Checks for dynamic variables in static contexts.
  */
 public class StaticVerifier extends ClassCodeVisitorSupport {
-    private boolean inSpecialConstructorCall;
-    private boolean inPropertyExpression; // TODO use it or lose it
-    private boolean inClosure;
-    private MethodNode currentMethod;
-    private SourceUnit source;
-
-    public void visitClass(ClassNode node, SourceUnit source) {
-        this.source = source;
-        super.visitClass(node);
-    }
+    private boolean inClosure, inSpecialConstructorCall;
+    private MethodNode methodNode;
+    private SourceUnit sourceUnit;
 
     @Override
-    public void visitVariableExpression(VariableExpression ve) {
-        Variable v = ve.getAccessedVariable();
-        if (v instanceof DynamicVariable) {
-            if (!inPropertyExpression || inSpecialConstructorCall) addStaticVariableError(ve);
-        }
+    protected SourceUnit getSourceUnit() {
+        return sourceUnit;
+    }
+
+    public void visitClass(ClassNode node, SourceUnit unit) {
+        sourceUnit = unit;
+        visitClass(node);
     }
 
     @Override
@@ -82,11 +77,11 @@ public class StaticVerifier extends ClassCodeVisitorSupport {
 
     @Override
     public void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
-        MethodNode oldCurrentMethod = currentMethod;
-        currentMethod = node;
+        MethodNode oldMethodNode = methodNode;
+        methodNode = node;
         super.visitConstructorOrMethod(node, isConstructor);
         if (isConstructor) {
-            final Set<String> exceptions = new HashSet<String>();
+            final Set<String> exceptions = new HashSet<>();
             for (final Parameter param : node.getParameters()) {
                 exceptions.add(param.getName());
                 if (param.hasInitialExpression()) {
@@ -120,12 +115,12 @@ public class StaticVerifier extends ClassCodeVisitorSupport {
                 }
             }
         }
-        currentMethod = oldCurrentMethod;
+        methodNode = oldMethodNode;
     }
 
     @Override
     public void visitMethodCallExpression(MethodCallExpression mce) {
-        if (inSpecialConstructorCall && !isInnerClass(currentMethod.getDeclaringClass())) {
+        if (inSpecialConstructorCall && !isInnerClass(methodNode.getDeclaringClass())) {
             Expression objectExpression = mce.getObjectExpression();
             if (objectExpression instanceof VariableExpression) {
                 VariableExpression ve = (VariableExpression) objectExpression;
@@ -138,40 +133,38 @@ public class StaticVerifier extends ClassCodeVisitorSupport {
         super.visitMethodCallExpression(mce);
     }
 
-    @Override
+    @Override // TODO: dead code?
     public void visitPropertyExpression(PropertyExpression pe) {
-        if (!inSpecialConstructorCall) checkStaticScope(pe);
-    }
-
-    @Override
-    protected SourceUnit getSourceUnit() {
-        return source;
-    }
-
-
-    private void checkStaticScope(PropertyExpression pe) {
-        if (inClosure) return;
-        for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {
-            if (it instanceof PropertyExpression) continue;
-            if (it instanceof VariableExpression) {
-                addStaticVariableError((VariableExpression) it);
+        if (!inClosure && !inSpecialConstructorCall) {
+            for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {
+                if (it instanceof PropertyExpression) continue;
+                if (it instanceof VariableExpression) {
+                    VariableExpression ve = (VariableExpression) it;
+                    if (ve.isThisExpression() || ve.isSuperExpression()) return;
+                    if (!inSpecialConstructorCall && (inClosure || !ve.isInStaticContext())) return;
+                    if (methodNode != null && methodNode.isStatic()) {
+                        FieldNode fieldNode = getDeclaredOrInheritedField(methodNode.getDeclaringClass(), ve.getName());
+                        if (fieldNode != null && fieldNode.isStatic()) return;
+                    }
+                    Variable v = ve.getAccessedVariable();
+                    if (v != null && !(v instanceof DynamicVariable) && v.isInStaticContext()) return;
+                    addVariableError(ve);
+                }
+                return;
             }
-            return;
         }
     }
 
-    private void addStaticVariableError(VariableExpression ve) {
-        // closures are always dynamic
-        // propertyExpressions will handle the error a bit differently
-        if (!inSpecialConstructorCall && (inClosure || !ve.isInStaticContext())) return;
-        if (ve.isThisExpression() || ve.isSuperExpression()) return;
-        Variable v = ve.getAccessedVariable();
-        if (currentMethod != null && currentMethod.isStatic()) {
-            FieldNode fieldNode = getDeclaredOrInheritedField(currentMethod.getDeclaringClass(), ve.getName());
-            if (fieldNode != null && fieldNode.isStatic()) return;
+    @Override
+    public void visitVariableExpression(VariableExpression ve) {
+        if (ve.getAccessedVariable() instanceof DynamicVariable && (ve.isInStaticContext() || inSpecialConstructorCall) && !inClosure) {
+            // GROOVY-5687: interface constants not visible to implementing sub-class in static context
+            if (methodNode != null && methodNode.isStatic()) {
+                FieldNode fieldNode = getDeclaredOrInheritedField(methodNode.getDeclaringClass(), ve.getName());
+                if (fieldNode != null && fieldNode.isStatic()) return;
+            }
+            addVariableError(ve);
         }
-        if (v != null && !(v instanceof DynamicVariable) && v.isInStaticContext()) return;
-        addVariableError(ve);
     }
 
     private void addVariableError(VariableExpression ve) {
@@ -188,7 +181,7 @@ public class StaticVerifier extends ClassCodeVisitorSupport {
         while (node != null) {
             FieldNode fn = node.getDeclaredField(fieldName);
             if (fn != null) return fn;
-            List<ClassNode> interfacesToCheck = new ArrayList<ClassNode>(Arrays.asList(node.getInterfaces()));
+            List<ClassNode> interfacesToCheck = new ArrayList<>(Arrays.asList(node.getInterfaces()));
             while (!interfacesToCheck.isEmpty()) {
                 ClassNode nextInterface = interfacesToCheck.remove(0);
                 fn = nextInterface.getDeclaredField(fieldName);
@@ -199,5 +192,4 @@ public class StaticVerifier extends ClassCodeVisitorSupport {
         }
         return null;
     }
-
 }
diff --git a/src/test/groovy/bugs/Groovy8327.groovy b/src/test/groovy/bugs/Groovy8327.groovy
new file mode 100644
index 0000000..986e6f7
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy8327.groovy
@@ -0,0 +1,221 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs
+
+import groovy.transform.NotYetImplemented
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy8327 {
+
+    @Test
+    void testCallStaticMethodInThisConstructor() {
+        assertScript '''
+            class A {
+                static String f = '123'
+                static String g() { 'abc' }
+                A() {
+                    this(g() + getF())
+                }
+                A(a) { assert a == 'abc123' }
+            }
+
+            new A()
+        '''
+    }
+
+    @Test
+    void testCallStaticMethodInSuperConstructor() {
+        assertScript '''
+            class A {
+                A(a) { assert a == 'abc123' }
+            }
+
+            class B extends A {
+                static String f = '123'
+                static String g() { 'abc' }
+                B() {
+                    super(g() + getF())
+                }
+            }
+
+            new B()
+        '''
+    }
+
+    @Test
+    void testCallSuperStaticMethodInSuperConstructor() {
+        assertScript '''
+            class A {
+                static String f = '123'
+                static String g() { 'abc' }
+                A(a) { assert a == 'abc123' }
+            }
+
+            class B extends A {
+                B() {
+                    super(g() + getF())
+                }
+            }
+
+            new B()
+        '''
+    }
+
+    // GROOVY-8327:
+
+    @Test
+    void testCallStaticMethodInClosureParamOfThisConstructor() {
+        assertScript '''
+            class A {
+                static String f = '123'
+                static String g() { 'abc' }
+                A() {
+                    this({ -> g() + getF()})
+                }
+                A(a) { assert a() == 'abc123' }
+            }
+
+            new A()
+        '''
+    }
+
+    @Test
+    void testCallStaticMethodInClosureParamOfSuperConstructor() {
+        assertScript '''
+            class A {
+                A(a) { assert a() == 'abc123' }
+            }
+
+            class B extends A {
+                static String f = '123'
+                static String g() { 'abc' }
+                B() {
+                    super({ -> g() + getF() })
+                }
+            }
+
+            new B()
+        '''
+    }
+
+    @Test
+    void testCallSuperStaticMethodInClosureParamOfSuperConstructor() {
+        assertScript '''
+            class A {
+                static String f = '123'
+                static String g() { 'abc' }
+                A(a) { assert a() == 'abc123' }
+            }
+
+            class B extends A {
+                B() {
+                    super({ -> g() + getF() })
+                }
+            }
+
+            new B()
+        '''
+    }
+
+    // GROOVY-9591:
+
+    @Test
+    void testTapExpressionAsSpecialConstructorArgument1() {
+        assertScript '''
+            @groovy.transform.ToString
+            class A {
+                A(x) {}
+                def b
+            }
+            class C {
+                C() {
+                    this(new A(null).tap { b = 42 })
+                }
+                C(x) {
+                    assert x.toString() == 'A(42)'
+                }
+            }
+            new C()
+        '''
+    }
+
+    @NotYetImplemented @Test
+    void testTapExpressionAsSpecialConstructorArgument2() {
+        assertScript '''
+            @groovy.transform.ToString
+            class A {
+                A(x) {}
+                def b
+                void init() { b = 42 }
+            }
+            class C {
+                C() {
+                    this(new A(null).tap { init() })
+                }
+                C(x) {
+                    assert x.toString() == 'A(42)'
+                }
+            }
+            new C()
+        '''
+    }
+
+    @Test
+    void testWithExpressionAsSpecialConstructorArgument1() {
+        assertScript '''
+            @groovy.transform.ToString
+            class A {
+                A(x) {}
+                def b
+            }
+            class C {
+                C() {
+                    this(new A(null).with { b = 42; return it })
+                }
+                C(x) {
+                    assert x.toString() == 'A(42)'
+                }
+            }
+            new C()
+        '''
+    }
+
+    @NotYetImplemented @Test
+    void testWithExpressionAsSpecialConstructorArgument2() {
+        assertScript '''
+            @groovy.transform.ToString
+            class A {
+                A(x) {}
+                def b
+                void init() { b = 42 }
+            }
+            class C {
+                C() {
+                    this(new A(null).with { init(); return it })
+                }
+                C(x) {
+                    assert x.toString() == 'A(42)'
+                }
+            }
+            new C()
+        '''
+    }
+}

[groovy] 01/03: GROOVY-8961, GROOVY-9734: use type(s) from 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_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 593c0219cb4568ac1793736e34992942388fdfb0
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Sep 16 16:31:45 2020 -0500

    GROOVY-8961, GROOVY-9734: use type(s) from call site to resolve generics
    
    "m(Collections.emptyList())" does not supply type arguments to emptyList
    
    Conflicts:
    	src/test/groovy/transform/stc/GenericsSTCTest.groovy
---
 .../codehaus/groovy/ast/tools/GenericsUtils.java   | 133 +++++++++------------
 .../transform/stc/StaticTypeCheckingVisitor.java   |  50 ++++++++
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  69 ++++++++++-
 3 files changed, 174 insertions(+), 78 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 0ff3b50..a5605e0 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -511,7 +511,7 @@ public class GenericsUtils {
                 ClassNode corrected = getCorrectedClassNode(type, superClass, false);
                 extractSuperClassGenerics(corrected, target, spec);
             } else {
-                // if we reach here, we have an unhandled case 
+                // if we reach here, we have an unhandled case
                 throw new GroovyBugError("The type " + type + " seems not to normally extend " + target + ". Sorry, I cannot handle this.");
             }
         }
@@ -539,7 +539,7 @@ public class GenericsUtils {
     }
 
     private static void extractSuperClassGenerics(GenericsType[] usage, GenericsType[] declaration, Map<String, ClassNode> spec) {
-        // if declaration does not provide generics, there is no connection to make 
+        // if declaration does not provide generics, there is no connection to make
         if (declaration == null || declaration.length == 0) return;
 
         // if usage is a raw type, remove type parameters from spec
@@ -744,96 +744,79 @@ public class GenericsUtils {
      * @param actualType the actual type
      * @return the parameterized type
      */
-    public static ClassNode findParameterizedType(ClassNode genericsClass, ClassNode actualType) {
+    public static ClassNode findParameterizedType(final ClassNode genericsClass, final ClassNode actualType) {
         return findParameterizedType(genericsClass, actualType, false);
     }
 
-    // Backported from 3.0.0
-    private static ClassNode findParameterizedType(ClassNode genericsClass, ClassNode actualType, boolean tryToFindExactType) {
-        ClassNode parameterizedType = null;
-
-        if (null == genericsClass.getGenericsTypes()) {
-            return parameterizedType;
+    // Backported from 3.0.8
+    private static ClassNode findParameterizedType(final ClassNode genericsClass, final ClassNode actualType, final boolean tryToFindExactType) {
+        final GenericsType[] genericsTypes = genericsClass.getGenericsTypes();
+        if (genericsTypes == null || genericsClass.isGenericsPlaceHolder()) {
+            return null;
         }
 
-        GenericsType[] declaringGenericsTypes = genericsClass.getGenericsTypes();
-
-        List<ClassNode> classNodeList = new LinkedList<>(getAllSuperClassesAndInterfaces(actualType));
-        classNodeList.add(0, actualType);
-
-        LinkedList<ClassNode> parameterizedTypeCandidateList = new LinkedList<>();
-
-        for (ClassNode cn : classNodeList) {
-            if (cn == genericsClass) {
-                continue;
-            }
-
-            if (tryToFindExactType && null != cn.getGenericsTypes() && hasNonPlaceHolders(cn)) {
-                parameterizedTypeCandidateList.add(cn);
-            }
+        if (actualType.equals(genericsClass)) {
+            return actualType;
+        }
 
-            if (!(genericsClass.equals(cn.redirect()))) {
-                continue;
-            }
+        java.util.Set<ClassNode> done = new java.util.HashSet<>();
+        LinkedList<ClassNode> todo = new LinkedList<>();
+        todo.add(actualType);
+        ClassNode type;
 
-            if (isGenericsTypeArraysLengthEqual(declaringGenericsTypes, cn.getGenericsTypes())) {
-                parameterizedType = cn;
-                break;
+        while ((type = todo.poll()) != null) {
+            if (type.equals(genericsClass)) {
+                return type;
             }
-        }
-
-        if (null == parameterizedType) {
-            if (!parameterizedTypeCandidateList.isEmpty()) {
-                parameterizedType = parameterizedTypeCandidateList.getLast();
+            if (done.add(type)) {
+                boolean parameterized = (type.getGenericsTypes() != null);
+                for (ClassNode cn : type.getInterfaces()) {
+                    if (parameterized)
+                        cn = parameterizeType(type, cn);
+                    todo.add(cn);
+                }
+                if (!actualType.isInterface()) {
+                    ClassNode cn = type.getUnresolvedSuperClass();
+                    if (cn != null && cn.redirect() != ClassHelper.OBJECT_TYPE) {
+                        if (parameterized)
+                            cn = parameterizeType(type, cn);
+                        todo.add(cn);
+                    }
+                }
             }
         }
 
-        return parameterizedType;
+        return null;
     }
 
     /**
-     * Backported from 3.0.0.
-     *
-     * Check whether the ClassNode has non generics placeholders, aka not placeholder
-     *
-     * @param parameterizedType the class node
-     * @return the result
-     * @since 2.5.9
+     * Checks for any placeholder (aka unresolved) generics.
+     * <p>
+     * Backported from 3.0.8
      */
-    private static boolean hasNonPlaceHolders(ClassNode parameterizedType) {
-        return checkPlaceHolders(parameterizedType, new Predicate<GenericsType>() {
-            @Override
-            public boolean test(GenericsType genericsType) {
-                return !genericsType.isPlaceholder();
-            }
-        });
-    }
-
-    private static boolean isGenericsTypeArraysLengthEqual(GenericsType[] declaringGenericsTypes, GenericsType[] actualGenericsTypes) {
-        return null != actualGenericsTypes && declaringGenericsTypes.length == actualGenericsTypes.length;
-    }
-
-    private static List<ClassNode> getAllSuperClassesAndInterfaces(ClassNode actualReceiver) {
-        List<ClassNode> superClassAndInterfaceList = new LinkedList<>();
-        List<ClassNode> allSuperClassNodeList = getAllUnresolvedSuperClasses(actualReceiver);
-        superClassAndInterfaceList.addAll(allSuperClassNodeList);
-        superClassAndInterfaceList.addAll(actualReceiver.getAllInterfaces());
-
-        for (ClassNode superClassNode : allSuperClassNodeList) {
-            superClassAndInterfaceList.addAll(superClassNode.getAllInterfaces());
+    public static boolean hasUnresolvedGenerics(final ClassNode type) {
+        if (type.isGenericsPlaceHolder()) return true;
+        if (type.isArray()) {
+            return hasUnresolvedGenerics(type.getComponentType());
         }
-
-        return superClassAndInterfaceList;
-    }
-
-    private static List<ClassNode> getAllUnresolvedSuperClasses(ClassNode actualReceiver) {
-        List<ClassNode> superClassNodeList = new LinkedList<>();
-
-        for (ClassNode cn = actualReceiver.getUnresolvedSuperClass(); null != cn && ClassHelper.OBJECT_TYPE != cn; cn = cn.getUnresolvedSuperClass()) {
-            superClassNodeList.add(cn);
+        GenericsType[] genericsTypes = type.getGenericsTypes();
+        if (genericsTypes != null) {
+            for (GenericsType genericsType : genericsTypes) {
+                if (genericsType.isPlaceholder()) return true;
+                ClassNode lowerBound = genericsType.getLowerBound();
+                ClassNode[] upperBounds = genericsType.getUpperBounds();
+                if (lowerBound != null) {
+                    if (hasUnresolvedGenerics(lowerBound)) return true;
+                } else if (upperBounds != null) {
+                    for (ClassNode upperBound : upperBounds) {
+                        if (hasUnresolvedGenerics(upperBound)) return true;
+                    }
+                } else {
+                    if (hasUnresolvedGenerics(genericsType.getType())) return true;
+                }
+            }
         }
-
-        return superClassNodeList;
+        return false;
     }
 
     private static final EvictableCache<ParameterizedTypeCacheKey, SoftReference<ClassNode>> PARAMETERIZED_TYPE_CACHE = new ConcurrentSoftCache<>(64);
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 34f8b9e..d6508cb 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -3432,6 +3432,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                                 returnType = md.getType();
                             }
                         }
+                        // GROOVY-8961, GROOVY-9734
+                        resolvePlaceholdersFromImplicitTypeHints(args, argumentList, directMethodCallCandidate);
                         if (typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, directMethodCallCandidate, call)) {
                             returnType = adjustWithTraits(directMethodCallCandidate, chosenReceiver.getType(), args, returnType);
 
@@ -5043,6 +5045,54 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
+    /**
+     * Given method call like "m(Collections.emptyList())", the type of the call
+     * argument is {@code List<T>} without explicit type arguments. Knowning the
+     * method target of "m", {@code T} could be resolved.
+     */
+    private static void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals, final ArgumentListExpression argumentList, final MethodNode inferredMethod) {
+        for (int i = 0, n = actuals.length; i < n; i += 1) {
+            // check for method call with known target
+            Expression a = argumentList.getExpression(i);
+            if (!(a instanceof MethodCallExpression)) continue;
+            if (((MethodCallExpression) a).isUsingGenerics()) continue;
+            MethodNode aNode = a.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
+            if (aNode == null || aNode.getGenericsTypes() == null) continue;
+
+            // and unknown generics
+            ClassNode at = actuals[i];
+            if (!GenericsUtils.hasUnresolvedGenerics(at)) continue;
+
+            int np = inferredMethod.getParameters().length;
+            Parameter p = inferredMethod.getParameters()[Math.min(i, np - 1)];
+
+            ClassNode pt = p.getOriginType();
+            if (i >= (np - 1) && pt.isArray() && !at.isArray()) pt = pt.getComponentType();
+
+            // try to resolve placeholder(s) in argument type using parameter type
+
+            Map<GenericsTypeName, GenericsType> linked = new HashMap<>();
+            Map<GenericsTypeName, GenericsType> source = GenericsUtils.extractPlaceholders(at);
+            Map<GenericsTypeName, GenericsType> target = GenericsUtils.extractPlaceholders(pt);
+
+            // connect E:T from source to E:Type from target
+            for (GenericsType placeholder : aNode.getGenericsTypes()) {
+                for (Map.Entry<GenericsTypeName, GenericsType> e : source.entrySet()) {
+                    if (e.getValue() == placeholder) {
+                        GenericsType gt = target.get(e.getKey());
+                        // skip "f(g())" for "f(T<String>)" and "<U extends Number> U g()"
+                        if (gt != null && isAssignableTo(gt.getType(), placeholder.getType())) {
+                            linked.put(new GenericsTypeName(placeholder.getName()), gt);
+                        }
+                        break;
+                    }
+                }
+            }
+
+            actuals[i] = applyGenericsContext(linked, at);
+        }
+    }
+
     private static void extractGenericsConnectionsForSuperClassAndInterfaces(final Map<GenericsTypeName, GenericsType> resolvedPlaceholders, final Map<GenericsTypeName, GenericsType> connections) {
         for (GenericsType value : new HashSet<GenericsType>(connections.values())) {
             if (!value.isPlaceholder() && !value.isWildcard()) {
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 0532f90..d14cdff 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -195,7 +195,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-
     void testLinkedListWithListArgument() {
         assertScript '''
             List<String> list = new LinkedList<String>(['1','2','3'])
@@ -504,6 +503,70 @@ 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
+    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) {
+            }
+            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
+    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()
+        '''
+    }
+
     // GROOVY-5516
     void testAddAllWithCollectionShouldBeAllowed() {
         assertScript '''import org.codehaus.groovy.transform.stc.ExtensionMethodNode
@@ -1546,7 +1609,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             def cl = {1}
             assert foo(cl.class, cl) == cl
          '''
-         //GROOVY-5885
+         // GROOVY-5885
          assertScript '''
             class Test {
                 public <X extends Test> X castToMe(Class<X> type, Object o) {
@@ -1558,7 +1621,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
          '''
     }
 
-    // Groovy-5839
+    // GROOVY-5839
     void testMethodShadowGenerics() {
         shouldFailWithMessages '''
             public class GoodCodeRed<T> {