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 2022/03/11 20:10:56 UTC

[groovy] 01/01: GROOVY-6919: STC: check bound types for fields, methods, properties, ...

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 911963a04a036fa03a453d041883107569301e98
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Mar 11 13:54:27 2022 -0600

    GROOVY-6919: STC: check bound types for fields, methods, properties, ...
    
    3_0_X backport
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 135 ++++++++-------------
 .../stc/ClosureParamTypeInferenceSTCTest.groovy    |  59 ++++-----
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 132 ++++++++++++++++----
 3 files changed, 182 insertions(+), 144 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 2c67eca..9194c46 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -3779,44 +3779,61 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     protected List<Receiver<String>> makeOwnerList(final Expression objectExpression) {
         ClassNode receiver = getType(objectExpression);
         List<Receiver<String>> owners = new ArrayList<>();
-        if (isClassClassNodeWrappingConcreteType(receiver)) {
-            ClassNode staticType = receiver.getGenericsTypes()[0].getType();
-            owners.add(Receiver.make(staticType)); // Type from Class<Type>
-            addTraitType(staticType, owners); // T in Class<T$Trait$Helper>
-            owners.add(Receiver.make(receiver)); // Class<Type>
-        } else {
-            owners.add(Receiver.make(receiver));
-            if (receiver.isInterface()) {
-                owners.add(Receiver.make(OBJECT_TYPE));
-            }
-            addSelfTypes(receiver, owners);
-            addTraitType(receiver, owners);
-        }
-        if (!typeCheckingContext.temporaryIfBranchTypeInformation.isEmpty()) {
-            List<ClassNode> potentialReceiverType = getTemporaryTypesForExpression(objectExpression);
-            if (potentialReceiverType != null && !potentialReceiverType.isEmpty()) {
-                for (ClassNode node : potentialReceiverType) {
-                    owners.add(Receiver.make(node));
-                }
-            }
-        }
-        if (typeCheckingContext.lastImplicitItType != null
-                && objectExpression instanceof VariableExpression
-                && ((VariableExpression) objectExpression).getName().equals("it")) {
-            owners.add(Receiver.make(typeCheckingContext.lastImplicitItType));
-        }
         if (typeCheckingContext.delegationMetadata != null
                 && objectExpression instanceof VariableExpression
                 && ((VariableExpression) objectExpression).getName().equals("owner")
                 && /*isNested:*/typeCheckingContext.delegationMetadata.getParent() != null) {
-            owners.clear();
             List<Receiver<String>> enclosingClass = Collections.singletonList(
                     Receiver.make(typeCheckingContext.getEnclosingClassNode()));
             addReceivers(owners, enclosingClass, typeCheckingContext.delegationMetadata.getParent(), "owner.");
+        } else {
+            if (isClassClassNodeWrappingConcreteType(receiver)) {
+                ClassNode staticType = receiver.getGenericsTypes()[0].getType();
+                owners.add(Receiver.make(staticType)); // Type from Class<Type>
+                addTraitType(staticType, owners); // T in Class<T$Trait$Helper>
+                owners.add(Receiver.make(receiver)); // Class<Type>
+            } else {
+                addBoundType(receiver, owners);
+                addSelfTypes(receiver, owners);
+                addTraitType(receiver, owners);
+                if (receiver.redirect().isInterface()) {
+                    owners.add(Receiver.make(OBJECT_TYPE));
+                }
+            }
+            if (!typeCheckingContext.temporaryIfBranchTypeInformation.isEmpty()) {
+                List<ClassNode> potentialReceiverType = getTemporaryTypesForExpression(objectExpression);
+                if (potentialReceiverType != null && !potentialReceiverType.isEmpty()) {
+                    for (ClassNode node : potentialReceiverType) {
+                        owners.add(Receiver.make(node));
+                    }
+                }
+            }
+            if (typeCheckingContext.lastImplicitItType != null
+                    && objectExpression instanceof VariableExpression
+                    && ((VariableExpression) objectExpression).getName().equals("it")) {
+                owners.add(Receiver.make(typeCheckingContext.lastImplicitItType));
+            }
         }
         return owners;
     }
 
+    private static void addBoundType(final ClassNode receiver, final List<Receiver<String>> owners) {
+        if (!receiver.isGenericsPlaceHolder() || receiver.getGenericsTypes() == null) {
+            owners.add(Receiver.make(receiver));
+            return;
+        }
+
+        GenericsType gt = receiver.getGenericsTypes()[0];
+        if (gt.getLowerBound() == null && gt.getUpperBounds() != null) {
+            for (ClassNode cn : gt.getUpperBounds()) { // T extends C & I
+                addBoundType(cn, owners);
+                addSelfTypes(cn, owners);
+            }
+        } else {
+            owners.add(Receiver.make(OBJECT_TYPE)); // T or T super Type
+        }
+    }
+
     private static void addSelfTypes(final ClassNode receiver, final List<Receiver<String>> owners) {
         for (ClassNode selfType : Traits.collectSelfTypes(receiver, new LinkedHashSet<>())) {
             owners.add(Receiver.make(selfType));
@@ -5845,71 +5862,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         ParameterVariableExpression(final Parameter parameter) {
             super(parameter);
             this.parameter = parameter;
-            ClassNode inferred = parameter.getNodeMetaData(INFERRED_TYPE);
-            if (inferred == null) {
-                inferred = infer(parameter);
-
-                parameter.setNodeMetaData(INFERRED_TYPE, inferred);
-            }
-        }
-
-        private static ClassNode infer(final Variable variable) {
-            ClassNode originType = variable.getOriginType();
-
-            if (originType.isGenericsPlaceHolder()) {
-                GenericsType[] genericsTypes = originType.getGenericsTypes();
-
-                if (genericsTypes != null && genericsTypes.length > 0) {
-                    GenericsType gt = genericsTypes[0];
-                    ClassNode[] upperBounds = gt.getUpperBounds();
-
-                    if (upperBounds != null && upperBounds.length > 0) {
-                        return upperBounds[0];
-                    }
-                }
-            }
-
-            return originType;
-        }
-
-        @Override
-        public void copyNodeMetaData(final ASTNode other) {
-            parameter.copyNodeMetaData(other);
-        }
-
-        @Override
-        public Object putNodeMetaData(final Object key, final Object value) {
-            return parameter.putNodeMetaData(key, value);
-        }
-
-        @Override
-        public void removeNodeMetaData(final Object key) {
-            parameter.removeNodeMetaData(key);
-        }
-
-        @Override
-        public Map<?, ?> getNodeMetaData() {
-            return parameter.getNodeMetaData();
-        }
-
-        @Override
-        public <T> T getNodeMetaData(final Object key) {
-            return parameter.getNodeMetaData(key);
-        }
-
-        @Override
-        public void setNodeMetaData(final Object key, final Object value) {
-            parameter.setNodeMetaData(key, value);
+            this.parameter.getNodeMetaData(INFERRED_TYPE, x -> parameter.getOriginType());
         }
 
         @Override
-        public int hashCode() {
-            return parameter.hashCode();
+        public Map<?, ?> getMetaDataMap() {
+            return parameter.getMetaDataMap();
         }
 
         @Override
-        public boolean equals(final Object other) {
-            return parameter.equals(other);
+        public void setMetaDataMap(final Map<?, ?> metaDataMap) {
+            parameter.setMetaDataMap(metaDataMap);
         }
     }
 
diff --git a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
index bc0ac8a..717a48d 100644
--- a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
@@ -22,22 +22,41 @@ package groovy.transform.stc
  * Unit tests for static type checking : closure parameter type inference.
  */
 class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
-    void testInferenceForDGM_CollectUsingExplicitIt() {
+
+    void testInferenceForDGM_collectUsingExplicitIt() {
         assertScript '''
             ['a','b'].collect { it -> it.toUpperCase() }
         '''
-    }
-
-    void testInferenceForDGM_CollectUsingExplicitItAndIncorrectType() {
         shouldFailWithMessages '''
             ['a','b'].collect { Date it -> it.toUpperCase() }
         ''', 'Expected parameter of type java.lang.String but got java.util.Date'
     }
 
-    void testInferenceForDGM_CollectUsingImplicitIt() {
+    void testInferenceForDGM_collectUsingImplicitIt() {
         assertScript '''
             ['a','b'].collect { it.toUpperCase() }
         '''
+        assertScript '''
+            def items = []
+            ['a','b','c'].collect(items) { it.toUpperCase() }
+        '''
+        assertScript '''
+            String[] array = ['foo', 'bar', 'baz']
+            assert array.collect { it.startsWith('ba') } == [false, true, true]
+        '''
+        assertScript '''
+            List<Boolean> answer = [true]
+            String[] array = ['foo', 'bar', 'baz']
+            array.collect(answer){it.startsWith('ba')}
+            assert answer == [true, false, true, true]
+        '''
+        assertScript '''
+            Iterator<String> iter = ['foo', 'bar', 'baz'].iterator()
+            assert iter.collect { it.startsWith('ba') } == [false, true, true]
+        '''
+        assertScript '''
+            assert [1234, 3.14].collect { it.intValue() } == [1234,3]
+        '''
     }
 
     void testInferenceForDGM_eachUsingExplicitIt() {
@@ -52,12 +71,6 @@ class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testInferenceForDGM_CollectUsingImplicitItAndLUB() {
-        assertScript '''
-            assert [1234, 3.14].collect { it.intValue() } == [1234,3]
-        '''
-    }
-
     void testInferenceForDGM_countUsingFirstSignature() {
         assertScript '''
             def src = [a: 1, b:2, c:3]
@@ -103,13 +116,6 @@ assert result == ['b', 'r', 'e', 'a', 'd', 'b', 'u', 't', 't', 'e', 'r']
 '''
     }
 
-    void testInferenceForDGM_Collect2() {
-        assertScript '''
-def items = []
-['a','b','c'].collect(items) { it.toUpperCase() }
-'''
-    }
-
     void testInferenceForDGM_CollectMap() {
         assertScript '''
         assert [a: 'foo',b:'bar'].collect { k,v -> k+v } == ['afoo','bbar']
@@ -221,23 +227,6 @@ def items = []
 '''
     }
 
-    void testDGM_collectOnArray() {
-        assertScript '''
-            String[] arr = ['foo', 'bar', 'baz']
-            assert arr.collect { it.startsWith('ba') } == [false, true, true]
-            List<Boolean> answer = [true]
-            arr.collect(answer) { it.startsWith('ba') }
-            assert answer == [true, false, true, true]
-        '''
-    }
-
-    void testDGM_collectOnIterator() {
-        assertScript '''
-            Iterator<String> itr = ['foo', 'bar', 'baz'].iterator()
-            assert itr.collect { it.startsWith('ba') } == [false, true, true]
-        '''
-    }
-
     void testInferenceOnNonExtensionMethod() {
         assertScript '''import groovy.transform.stc.ClosureParams
             import groovy.transform.stc.FirstParam
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index ae365ab..c16b875 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1726,29 +1726,23 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     void testOutOfBoundsByExtendsPlaceholderParameterType() {
         shouldFailWithMessages '''
             class Foo {
-                static <T extends List<? extends CharSequence>> void bar(T a) {}
+                static <T extends List<? extends CharSequence>> void bar(T list) {}
             }
-            class Baz {
-                static <T extends List<Object>> void qux(T a) {
-                    Foo.bar(a)
-                }
+            def <U extends List<Object>> void baz(U list) {
+                Foo.bar(list)
             }
-            Baz.qux([new Object()])
-        ''', 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.List <Object>]'
+        ''', 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
     }
 
     void testOutOfBoundsBySuperPlaceholderParameterType() {
         shouldFailWithMessages '''
             class Foo {
-                static <T extends List<? super CharSequence>> void bar(T a) {}
+                static <T extends List<? super CharSequence>> void bar(T list) {}
             }
-            class Baz {
-                static <T extends List<String>> void qux(T a) {
-                    Foo.bar(a)
-                }
+            def <U extends List<String>> void baz(U list) {
+                Foo.bar(list)
             }
-            Baz.qux(['abc'])
-        ''', 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.List <String>]'
+        ''', 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
     }
 
     // GROOVY-5721
@@ -2172,8 +2166,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '#foo(java.util.List <? extends A>) with arguments [java.util.ArrayList <java.lang.Object>]'
     }
 
+    // GROOVY-5891
     void testMethodLevelGenericsForMethodCall() {
-        // Groovy-5891
         assertScript '''
             public <T extends List<Integer>> T foo(Class<T> type, def x) {
                 return type.cast(x)
@@ -2187,24 +2181,116 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             }
             def cl = {1}
             assert foo(cl.class, cl) == cl
-         '''
-         assertScript '''
+        '''
+        assertScript '''
             public <T extends Runnable> T foo(Class<T> type, def x) {
                 return type.cast(x) as T
             }
             def cl = {1}
             assert foo(cl.class, cl) == cl
-         '''
-         // GROOVY-5885
-         assertScript '''
+        '''
+    }
+
+    // GROOVY-5885
+    void testMethodLevelGenericsForMethodCall2() {
+        assertScript '''
             class Test {
-                public <X extends Test> X castToMe(Class<X> type, Object o) {
+                public <T extends Test> T castToMe(Class<T> type, Object o) {
                     return type.cast(o);
                 }
             }
             def t = new Test()
-            assert t.castToMe(Test, t)  == t
-         '''
+            assert t == t.castToMe(Test, t)
+        '''
+    }
+
+    // GROOVY-6919
+    void testMethodLevelGenericsForMethodCall3() {
+        assertScript '''
+            interface I1 {
+                String getFoo()
+            }
+            interface I2 {
+                String getBar()
+            }
+            def <T extends I1 & I2> void test(T obj) {
+                obj?.getFoo()
+                obj?.getBar()
+            }
+            test(null)
+        '''
+    }
+
+    // GROOVY-6919
+    void testMethodLevelGenericsForPropertyRead() {
+        assertScript '''
+            interface I1 {
+                String getFoo()
+            }
+            interface I2 {
+                String getBar()
+            }
+            def <T extends I1 & I2> void test(T obj) {
+                obj?.foo
+                obj?.bar
+            }
+            test(null)
+        '''
+    }
+
+    @NotYetImplemented
+    void testMethodLevelGenericsForPropertyRead2() {
+        assertScript '''
+            interface I1 {
+                static String getFoo() { 'foo' }
+            }
+            interface I2 {
+                String bar = 'bar'
+            }
+            def <T extends I1 & I2> void test(Class<T> cls) {
+                cls?.foo
+                cls?.bar
+            }
+            test(null)
+        '''
+    }
+
+    void testMethodLevelGenericsForPropertyRead3() {
+        assertScript '''
+            interface I1 {
+                String getFoo()
+            }
+            interface I2 {
+                String getBar()
+            }
+            class C<T extends I1 & I2> {
+                def <U extends T> void test(U obj) {
+                    obj?.foo
+                    obj?.bar
+                }
+            }
+            new C().test(null)
+        '''
+    }
+
+    void testMethodLevelGenericsForPropertyRead4() {
+        assertScript '''
+            interface I1 {
+                String getFoo()
+            }
+            interface I2 {
+                String getBar()
+            }
+            @groovy.transform.SelfType(I2) trait T2 {
+                abstract String getBaz()
+            }
+            def <T extends I1 & T2> void test(T obj) {
+                obj?.foo
+                obj?.bar
+                obj?.baz
+            }
+            test(null)
+        '''
     }
 
     // GROOVY-5839