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 18:19:33 UTC

[groovy] 02/02: 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 a38ccfc546db231843680e29751783562f7efccd
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Oct 6 14:26:25 2021 -0500

    GROOVY-6919: STC: check bound types for fields, methods, properties, ...
---
 .../transform/stc/StaticTypeCheckingVisitor.java   |  85 ++++---------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 132 +++++++++++++++++----
 .../console/ui/AstNodeToScriptAdapter.groovy       |  12 +-
 3 files changed, 138 insertions(+), 91 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 4e294c6..aeef6ca 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -3806,10 +3806,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 addTraitType(staticType, owners); // T in Class<T$Trait$Helper>
                 owners.add(Receiver.make(receiver)); // Class<Type>
             } else {
-                owners.add(Receiver.make(receiver));
+                addBoundType(receiver, owners);
                 addSelfTypes(receiver, owners);
                 addTraitType(receiver, owners);
-                if (receiver.isInterface()) {
+                if (receiver.redirect().isInterface()) {
                     owners.add(Receiver.make(OBJECT_TYPE));
                 }
             }
@@ -3817,6 +3817,23 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         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/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
diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/AstNodeToScriptAdapter.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/AstNodeToScriptAdapter.groovy
index fa78b38..dcd2ccd 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/AstNodeToScriptAdapter.groovy
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/AstNodeToScriptAdapter.groovy
@@ -710,14 +710,12 @@ class AstNodeToScriptVisitor implements CompilationUnit.IPrimaryClassNodeOperati
 
     @Override
     void visitStaticMethodCallExpression(StaticMethodCallExpression expression) {
+        boolean parens = expression?.arguments instanceof MethodCallExpression
+                || expression?.arguments instanceof VariableExpression
         print expression?.ownerType?.name + '.' + expression?.method
-        if (expression?.arguments instanceof VariableExpression || expression?.arguments instanceof MethodCallExpression) {
-            print '('
-            expression?.arguments?.visit this
-            print ')'
-        } else {
-            expression?.arguments?.visit this
-        }
+        if (parens) print '('
+        expression?.arguments?.visit(this)
+        if (parens) print ')'
     }
 
     @Override