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/29 20:42:53 UTC

[groovy] branch GROOVY_4_0_X updated: GROOVY-10528: STC: fix NPE for raw type inference

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

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


The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
     new 685c801  GROOVY-10528: STC: fix NPE for raw type inference
685c801 is described below

commit 685c8013aa2f405961a21bee90381f2dfc9ba2ce
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Mar 29 12:51:59 2022 -0500

    GROOVY-10528: STC: fix NPE for raw type inference
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 74 +++++++++++-----------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 14 ++++
 2 files changed, 50 insertions(+), 38 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 3b05d2d..e006ad1 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -3047,17 +3047,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         List<ClassNode[]> closureSignatures = getSignaturesFromHint(selectedMethod, hintClass, options, expression);
         List<ClassNode[]> candidates = new LinkedList<>();
         for (ClassNode[] signature : closureSignatures) {
-            // in order to compute the inferred types of the closure parameters, we're using the following trick:
-            // 1. create a dummy MethodNode for which the return type is a class node for which the generic types are the types returned by the hint
-            // 2. call inferReturnTypeGenerics
-            // 3. fetch inferred types from the result of inferReturnTypeGenerics
-            // In practice, it could be done differently but it has the main advantage of reusing
-            // existing code, hence reducing the amount of code to debug in case of failure.
-            ClassNode[] inferred = resolveGenericsFromTypeHint(receiver, arguments, selectedMethod, signature);
+            resolveGenericsFromTypeHint(receiver, arguments, selectedMethod, signature);
             if (signature.length == closureParams.length // same number of arguments
                     || (signature.length == 1 && closureParams.length == 0) // implicit it
-                    || (closureParams.length > signature.length && inferred[inferred.length - 1].isArray())) { // vargs
-                candidates.add(inferred);
+                    || (closureParams.length > signature.length && last(signature).isArray())) { // vargs
+                candidates.add(signature);
             }
         }
         if (candidates.size() > 1) {
@@ -3116,47 +3110,51 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    private ClassNode[] resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
-        ClassNode dummyResultNode = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
-        GenericsType[] genericTypes = new GenericsType[signature.length];
-        for (int i = 0, n = signature.length; i < n; i += 1) {
-            genericTypes[i] = new GenericsType(signature[i]);
-        }
-        dummyResultNode.setGenericsTypes(genericTypes);
+    /**
+     * Computes the inferred types of the closure parameters using the following trick:
+     * <ol>
+     * <li> creates a dummy MethodNode for which the return type is a class node
+     *      for which the generic types are the types returned by the hint
+     * <li> calls {@link #inferReturnTypeGenerics}
+     * <li> returns inferred types from the result
+     * </ol>
+     * In practice it could be done differently but it has the main advantage of
+     * reusing existing code, hence reducing the amount of code to debug in case
+     * of failure.
+     */
+    private void resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
+        ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
+        returnType.setGenericsTypes(Arrays.stream(signature).map(ClassNode::asGenericsType).toArray(GenericsType[]::new));
+
         MethodNode dummyMN = selectedMethod instanceof ExtensionMethodNode ? ((ExtensionMethodNode) selectedMethod).getExtensionMethodNode() : selectedMethod;
-        dummyMN = new MethodNode(
-                dummyMN.getName(),
-                dummyMN.getModifiers(),
-                dummyResultNode,
-                dummyMN.getParameters(),
-                dummyMN.getExceptions(),
-                EmptyStatement.INSTANCE
-        );
+        dummyMN = new MethodNode(dummyMN.getName(), dummyMN.getModifiers(), returnType, dummyMN.getParameters(), dummyMN.getExceptions(), null);
         dummyMN.setDeclaringClass(selectedMethod.getDeclaringClass());
         dummyMN.setGenericsTypes(selectedMethod.getGenericsTypes());
         if (selectedMethod instanceof ExtensionMethodNode) {
-            ExtensionMethodNode orig = (ExtensionMethodNode) selectedMethod;
             dummyMN = new ExtensionMethodNode(
                     dummyMN,
                     dummyMN.getName(),
                     dummyMN.getModifiers(),
-                    dummyResultNode,
-                    orig.getParameters(),
-                    orig.getExceptions(),
-                    EmptyStatement.INSTANCE,
-                    orig.isStaticExtension()
+                    returnType,
+                    selectedMethod.getParameters(),
+                    selectedMethod.getExceptions(),
+                    null,
+                    ((ExtensionMethodNode) selectedMethod).isStaticExtension()
             );
-            dummyMN.setDeclaringClass(orig.getDeclaringClass());
-            dummyMN.setGenericsTypes(orig.getGenericsTypes());
+            dummyMN.setDeclaringClass(selectedMethod.getDeclaringClass());
+            dummyMN.setGenericsTypes(selectedMethod.getGenericsTypes());
         }
-        ClassNode returnType = inferReturnTypeGenerics(receiver, dummyMN, arguments);
+
+        returnType = inferReturnTypeGenerics(receiver, dummyMN, arguments);
         GenericsType[] returnTypeGenerics = returnType.getGenericsTypes();
-        ClassNode[] inferred = new ClassNode[returnTypeGenerics.length];
         for (int i = 0, n = returnTypeGenerics.length; i < n; i += 1) {
-            GenericsType genericsType = returnTypeGenerics[i];
-            inferred[i] = getCombinedBoundType(genericsType);
+            GenericsType gt = returnTypeGenerics[i];
+            if (gt.isPlaceholder()) { // GROOVY-9968, GROOVY-10528, et al.
+                signature[i] = gt.getUpperBounds() != null ? gt.getUpperBounds()[0] : gt.getType().redirect();
+            } else {
+                signature[i] = getCombinedBoundType(gt);
+            }
         }
-        return inferred;
     }
 
     private static String[] convertToStringArray(final Expression options) {
@@ -3203,7 +3201,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     );
                     if (resolved != null) {
                         if (resolved.length == 1) {
-                            resolved = resolveGenericsFromTypeHint(receiver, arguments, mn, resolved);
+                            resolveGenericsFromTypeHint(receiver, arguments, mn, resolved);
                             expression.putNodeMetaData(DELEGATION_METADATA, newDelegationMetadata(resolved[0], stInt));
                         } else {
                             addStaticTypeError("Incorrect type hint found in method " + (mn), type);
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index c301b93..be1528d 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -4390,6 +4390,20 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-10528
+    void testRawTypeGuard() {
+        assertScript '''
+            void test(... args) {
+                args.each { object ->
+                    if (object instanceof Iterable) {
+                        object.each { test(it) }
+                    }
+                }
+            }
+            test(1,[2,3])
+        '''
+    }
+
     //--------------------------------------------------------------------------
 
     static class MyList