[groovy] branch master updated: GROOVY-9033: STC: resolve "def list = []" to List not List

Sun, 18 Apr 2021 05:56:08 -0700

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

sunlan 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 6f0f5ce  GROOVY-9033: STC: resolve "def list = []" to List<Object> not 
List<E>
6f0f5ce is described below

commit 6f0f5cee090616149de777123d8dd2843777b615
Author: Eric Milles <[email protected]>
AuthorDate: Fri Apr 16 16:01:20 2021 -0500

    GROOVY-9033: STC: resolve "def list = []" to List<Object> not List<E>
---
 .../transform/stc/StaticTypeCheckingSupport.java   | 79 ++++++++++------------
 .../transform/stc/StaticTypeCheckingVisitor.java   | 36 ++++++----
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 54 ++++++++++-----
 .../classgen/asm/sc/BugsStaticCompileTest.groovy   |  8 +--
 .../ginq/provider/collection/GinqAstWalker.groovy  |  4 +-
 5 files changed, 98 insertions(+), 83 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index d9f8c3e..9b47aaa 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1326,32 +1326,33 @@ public abstract class StaticTypeCheckingSupport {
     }
 
     protected static ClassNode fullyResolveType(final ClassNode type, final 
Map<GenericsTypeName, GenericsType> placeholders) {
-        if (type.isUsingGenerics() && !type.isGenericsPlaceHolder()) {
-            GenericsType[] gts = type.getGenericsTypes();
-            if (gts != null) {
-                GenericsType[] copy = new GenericsType[gts.length];
-                for (int i = 0, n = gts.length; i < n; i += 1) {
-                    GenericsType genericsType = gts[i];
-                    if (genericsType.isPlaceholder() && 
placeholders.containsKey(new GenericsTypeName(genericsType.getName()))) {
-                        copy[i] = placeholders.get(new 
GenericsTypeName(genericsType.getName()));
-                    } else {
-                        copy[i] = fullyResolve(genericsType, placeholders);
-                    }
-                }
-                gts = copy;
-            }
-            ClassNode result = type.getPlainNodeReference();
-            result.setGenericsTypes(gts);
-            return result;
-        } else if (type.isUsingGenerics() && OBJECT_TYPE.equals(type) && 
type.getGenericsTypes() != null) {
-            // Object<T>
-            GenericsType genericsType = placeholders.get(new 
GenericsTypeName(type.getGenericsTypes()[0].getName()));
-            if (genericsType != null) {
-                return genericsType.getType();
-            }
-        } else if (type.isArray()) {
+        if (type.isArray()) {
             return fullyResolveType(type.getComponentType(), 
placeholders).makeArray();
         }
+        if (type.isUsingGenerics()) {
+            if (type.isGenericsPlaceHolder()) {
+                GenericsType gt = placeholders.get(new 
GenericsTypeName(type.getUnresolvedName()));
+                if (gt != null) {
+                    return gt.getType();
+                }
+                ClassNode cn = type.redirect();
+                return cn != type ? cn : OBJECT_TYPE;
+            } else {
+                GenericsType[] gts = type.getGenericsTypes();
+                if (gts != null) {
+                    gts = Arrays.stream(gts).map(gt -> {
+                        if (gt.isPlaceholder()) {
+                            GenericsTypeName gtn = new 
GenericsTypeName(gt.getName());
+                            return placeholders.getOrDefault(gtn, 
extractType(gt).asGenericsType());
+                        }
+                        return fullyResolve(gt, placeholders);
+                    }).toArray(GenericsType[]::new);
+                }
+                ClassNode cn = type.getPlainNodeReference();
+                cn.setGenericsTypes(gts);
+                return cn;
+            }
+        }
         return type;
     }
 
@@ -1751,7 +1752,7 @@ public abstract class StaticTypeCheckingSupport {
     }
 
     public static ClassNode getCorrectedClassNode(final ClassNode type, final 
ClassNode superClass, final boolean handlingGenerics) {
-        if (handlingGenerics && missesGenericsTypes(type)) return 
superClass.getPlainNodeReference();
+        if (handlingGenerics && GenericsUtils.hasUnresolvedGenerics(type)) 
return superClass.getPlainNodeReference();
         return 
GenericsUtils.correctToGenericsSpecRecurse(GenericsUtils.createGenericsSpec(type),
 superClass);
     }
 
@@ -2142,26 +2143,14 @@ public abstract class StaticTypeCheckingSupport {
         return node.getSuperClass() != null && 
isParameterizedWithString(node.getUnresolvedSuperClass());
     }
 
-    public static boolean missesGenericsTypes(final ClassNode cn) {
-        if (cn.isArray()) return missesGenericsTypes(cn.getComponentType());
-        GenericsType[] cnTypes = cn.getGenericsTypes();
-        GenericsType[] rnTypes = cn.redirect().getGenericsTypes();
-        if (rnTypes != null && cnTypes == null) return true;
-        if (cnTypes != null) {
-            for (GenericsType genericsType : cnTypes) {
-                if (genericsType.isPlaceholder()) return true;
-                if (genericsType.isWildcard()) {
-                    ClassNode lowerBound = genericsType.getLowerBound();
-                    ClassNode[] upperBounds = genericsType.getUpperBounds();
-                    if (lowerBound != null) {
-                        if (lowerBound.isGenericsPlaceHolder() || 
missesGenericsTypes(lowerBound)) return true;
-                    } else if (upperBounds != null) {
-                        if (upperBounds[0].isGenericsPlaceHolder() || 
missesGenericsTypes(upperBounds[0])) return true;
-                    }
-                }
-            }
-        }
-        return false;
+    /**
+     * Determines if node is a raw type or references any generics 
placeholders.
+     */
+    public static boolean missesGenericsTypes(ClassNode cn) {
+        while (cn.isArray()) cn = cn.getComponentType();
+        GenericsType[] cnGenerics = cn.getGenericsTypes();
+        GenericsType[] rnGenerics = cn.redirect().getGenericsTypes();
+        return cnGenerics == null ? rnGenerics != null : 
GenericsUtils.hasUnresolvedGenerics(cn);
     }
 
     /**
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 5daab43..b546226 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -794,15 +794,20 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
 
             if (resultType == null) {
                 resultType = lType;
-            } else if (lType.isUsingGenerics() && isAssignment(op) && 
missesGenericsTypes(resultType)) {
-                // unchecked assignment
-                // List<Type> list = new LinkedList()
-                // Iterable<Type> iter = new LinkedList()
-                // Collection<Type> coll = Collections.emptyList()
+            } else if (isAssignment(op)) {
+                if (lType.isUsingGenerics() && 
missesGenericsTypes(resultType)) {
+                    // unchecked assignment
+                    // List<Type> list = new LinkedList()
+                    // Iterable<Type> iter = new LinkedList()
+                    // Collection<Type> coll = Collections.emptyList()
 
-                // the inferred type of the binary expression is the type of 
the RHS
-                // "completed" with generics type information available from 
the LHS
-                resultType = GenericsUtils.parameterizeType(lType, 
resultType.getPlainNodeReference());
+                    // the inferred type of the binary expression is the type 
of the RHS
+                    // "completed" with generics type information available 
from the LHS
+                    resultType = GenericsUtils.parameterizeType(lType, 
resultType.getPlainNodeReference());
+                } else if (lType.equals(OBJECT_TYPE) && 
GenericsUtils.hasUnresolvedGenerics(resultType)) { // def list = []
+                    Map<GenericsTypeName, GenericsType> placeholders = 
extractGenericsParameterMapOfThis(typeCheckingContext);
+                    resultType = fullyResolveType(resultType, 
Optional.ofNullable(placeholders).orElseGet(Collections::emptyMap));
+                }
             }
 
             // GROOVY-5874: if left expression is a closure shared variable, a 
second pass should be done
@@ -841,7 +846,7 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
                 // and we must update the result type
                 if (!implementsInterfaceOrIsSubclassOf(getWrapper(resultType), 
getWrapper(originType))) {
                     resultType = originType;
-                } else if (lType.isUsingGenerics() && !lType.isEnum() && 
hasRHSIncompleteGenericTypeInfo(resultType)) {
+                } else if (lType.isUsingGenerics() && !lType.isEnum() && 
GenericsUtils.hasUnresolvedGenerics(resultType)) {
                     // for example, LHS is List<ConcreteClass> and RHS is 
List<T> where T is a placeholder
                     resultType = lType;
                 } else {
@@ -1260,9 +1265,9 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
     private void checkTypeGenerics(final ClassNode leftExpressionType, final 
ClassNode wrappedRHS, final Expression rightExpression) {
         // last, check generic type information to ensure that inferred types 
are compatible
         if (!leftExpressionType.isUsingGenerics()) return;
-        // List<Foo> l = new List() is an example for incomplete generics type 
info
-        // we assume arity related errors are already handled here.
-        if (hasRHSIncompleteGenericTypeInfo(wrappedRHS)) return;
+        // example of incomplete type info: "List<Type> list = new 
LinkedList()"
+        // we assume arity related errors are already handled here
+        if (missesGenericsTypes(wrappedRHS)) return;
 
         GenericsType gt = GenericsUtils.buildWildcardType(leftExpressionType);
         if (UNKNOWN_PARAMETER_TYPE.equals(wrappedRHS) ||
@@ -1356,6 +1361,7 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
         typeCheckingContext.popEnclosingBinaryExpression();
     }
 
+    @Deprecated
     protected static boolean hasRHSIncompleteGenericTypeInfo(final ClassNode 
inferredRightExpressionType) {
         boolean replaceType = false;
         GenericsType[] genericsTypes = 
inferredRightExpressionType.getGenericsTypes();
@@ -5658,11 +5664,13 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
 
     protected boolean typeCheckMethodsWithGenericsOrFail(final ClassNode 
receiver, final ClassNode[] arguments, final MethodNode candidateMethod, final 
Expression location) {
         if (!typeCheckMethodsWithGenerics(receiver, arguments, 
candidateMethod)) {
-            Map<GenericsTypeName, GenericsType> classGTs = 
GenericsUtils.extractPlaceholders(receiver);
+            Map<GenericsTypeName, GenericsType> generics = 
GenericsUtils.extractPlaceholders(receiver);
+            
applyGenericsConnections(extractGenericsParameterMapOfThis(typeCheckingContext),
 generics);
+            addMethodLevelDeclaredGenerics(candidateMethod, generics);
             Parameter[] parameters = candidateMethod.getParameters();
             ClassNode[] paramTypes = new ClassNode[parameters.length];
             for (int i = 0, n = parameters.length; i < n; i += 1) {
-                paramTypes[i] = fullyResolveType(parameters[i].getType(), 
classGTs);
+                paramTypes[i] = fullyResolveType(parameters[i].getType(), 
generics);
                 // GROOVY-10010: check for List<String> parameter and 
["foo","$bar"] argument
                 if (i < arguments.length && 
hasGStringStringError(paramTypes[i], arguments[i], location)) {
                     return false;
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy 
b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 4d4ce4e..a542b59 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -146,7 +146,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testReturnTypeInference() {
+    void testReturnTypeInference1() {
         assertScript '''
             class Foo<U> {
                 U method() { }
@@ -156,13 +156,12 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testReturnTypeInferenceWithDiamond() {
+    void testReturnTypeInference2() {
         assertScript '''
-            class Foo<U> {
-                U method() { }
-            }
-            Foo<Integer> foo = new Foo<>()
-            Integer result = foo.method()
+        Object m() {
+          def s = '1234'
+          println 'Hello'
+        }
         '''
     }
 
@@ -217,20 +216,48 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-9033
+    void testReturnTypeInferenceWithMethodGenerics8() {
+        shouldFailWithMessages '''
+            List<String> test() {
+              def x = [].each { }
+              x.add(new Object())
+              return x // List<E>
+            }
+        ''', 'Incompatible generic argument types.' // Cannot assign 
java.util.List<java.lang.Object> to: java.util.List<java.lang.String>
+
+        assertScript '''
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type.genericsTypes[0].toString() == 'java.lang.String'
+                assert type.genericsTypes[1].toString() == 
'java.util.List<java.lang.Object>' // not List<E>
+            })
+            def map = [ key: [] ]
+        '''
+    }
+
     void testDiamondInferrenceFromConstructor1() {
         assertScript '''
-            Set<Long> set = new HashSet<>()
+            class Foo<U> {
+                U method() { }
+            }
+            Foo<Integer> foo = new Foo<>()
+            Integer result = foo.method()
         '''
     }
 
     void testDiamondInferrenceFromConstructor2() {
         assertScript '''
-            new HashSet<>(Arrays.asList(0L))
+            Set<Long> set = new HashSet<>()
         '''
     }
 
     void testDiamondInferrenceFromConstructor3() {
         assertScript '''
+            new HashSet<>(Arrays.asList(0L))
+        '''
+
+        assertScript '''
             Set<Number> set = new HashSet<>(Arrays.asList(0L))
         '''
 
@@ -726,15 +753,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         ''', 'Cannot find matching method 
FooWithGenerics#say(java.lang.Object)'
     }
 
-    void testVoidReturnTypeInferrence() {
-        assertScript '''
-        Object m() {
-          def s = '1234'
-          println 'Hello'
-        }
-        '''
-    }
-
     // GROOVY-5237
     void testGenericTypeArgumentAsField() {
         assertScript '''
diff --git 
a/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy 
b/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
index 1bcca69..e567be9 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
@@ -1157,9 +1157,9 @@ assert it.next() == 1G
             def map = new LinkedHashMap<>([a:1,b:2])
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 def ift = node.getNodeMetaData(INFERRED_TYPE)
-                assert ift == make(Set)
+                assert ift == SET_TYPE
                 assert ift.isUsingGenerics()
-                assert ift.genericsTypes[0].type==STRING_TYPE
+                assert ift.genericsTypes[0].type == STRING_TYPE
             })
             def set = map.keySet()
             def key = set[0]
@@ -1169,9 +1169,9 @@ assert it.next() == 1G
             def map = new LinkedHashMap([a:1,b:2])
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 def ift = node.getNodeMetaData(INFERRED_TYPE)
-                assert ift == make(Set)
+                assert ift == SET_TYPE
                 assert ift.isUsingGenerics()
-                assert ift.genericsTypes[0].name=='K'
+                assert ift.genericsTypes[0].name == 'java.lang.Object'
             })
             def set = map.keySet()
             def key = set[0]
diff --git 
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
 
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
index 15dffdc..785e8e2 100644
--- 
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
+++ 
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
@@ -820,7 +820,7 @@ class GinqAstWalker implements GinqAstVisitor<Expression>, 
SyntaxErrorReportable
             }
         })).getExpression(0)
 
-        def extra = []
+        List<Expression> extra = []
         if (enableCount || rowNumberUsed) {
             currentGinqExpression.putNodeMetaData(__RN_USED, true)
             extra << callX(varX(rowNumberName), 'getAndIncrement')
@@ -873,7 +873,7 @@ class GinqAstWalker implements GinqAstVisitor<Expression>, 
SyntaxErrorReportable
             validateWindowClause(classifierExpr, orderExpr, rowsExpr, 
rangeExpr, ignoredMethodCallExpressionList)
         }
 
-        def argumentExpressionList = []
+        List<Expression> argumentExpressionList = []
 
         if (classifierExpr) {
             List<Expression> expressionList = ((ArgumentListExpression) 
classifierExpr).getExpressions()

Reply via email to