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

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

commit af72676fda22f601c7197260b8df9bd17ff301a3
Author: Eric Milles <[email protected]>
AuthorDate: Sat Dec 27 11:32:27 2025 -0600

    GROOVY-10702: STC: multiple `instanceof` and sparse method call
---
 .../transform/stc/StaticTypeCheckingSupport.java   | 22 ++++++--
 .../transform/stc/TypeInferenceSTCTest.groovy      | 59 +++++++++++++++-------
 2 files changed, 60 insertions(+), 21 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 9eb04af018..519cf395f9 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -129,6 +129,7 @@ import static 
org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory;
 import static 
org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asList;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.first;
 import static 
org.codehaus.groovy.runtime.DefaultGroovyMethodsSupport.closeQuietly;
 import static org.codehaus.groovy.syntax.Types.BITWISE_AND;
 import static org.codehaus.groovy.syntax.Types.BITWISE_AND_EQUAL;
@@ -870,6 +871,13 @@ public abstract class StaticTypeCheckingSupport {
         if (type.isArray()) {
             return prettyPrintTypeName(type.getComponentType()) + "[]";
         }
+        if (type instanceof UnionTypeClassNode union) {
+            var sj = new StringJoiner(" | ", "(", ")");
+            for (ClassNode cn : union.getDelegates()) {
+                sj.add(prettyPrintTypeName(cn));
+            }
+            return sj.toString();
+        }
         return type.isGenericsPlaceHolder() ? type.getUnresolvedName() : 
type.getText();
     }
 
@@ -1036,16 +1044,24 @@ public abstract class StaticTypeCheckingSupport {
 
         // GROOVY-8965: type disjunction
         boolean duckType = receiver instanceof UnionTypeClassNode;
-        if (methods.size() > 1 && !methods.iterator().next().isConstructor())
+        if (methods.size() > 1 && !first(methods).isConstructor())
             methods = removeCovariantsAndInterfaceEquivalents(methods, 
duckType);
 
-        if (argumentTypes == null) {
+        if (!duckType && argumentTypes == null) {
             return asList(methods); // GROOVY-11683: no covariants or 
equivalents
         }
 
         Set<MethodNode> bestMethods = new HashSet<>(); // choose best 
method(s) for each possible receiver
         for (ClassNode rcvr : duckType ? ((UnionTypeClassNode) 
receiver).getDelegates() : new ClassNode[]{receiver}) {
-            bestMethods.addAll(chooseBestMethods(rcvr, methods, 
argumentTypes));
+            var view = methods;
+            if (duckType) {
+                view = methods.stream().filter(m -> 
implementsInterfaceOrSubclassOf(rcvr, m.getDeclaringClass())).toList();
+            }
+            view = chooseBestMethods(rcvr, view, argumentTypes);
+            if (view.isEmpty()) {
+                return Collections.emptyList(); // GROOVY-10702
+            }
+            bestMethods.addAll(view);
         }
         return new LinkedList<>(bestMethods); // assumes caller wants remove 
to be inexpensive
     }
diff --git a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy 
b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
index 54f870a331..94d38ad2c0 100644
--- a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -542,25 +542,48 @@ class TypeInferenceSTCTest extends 
StaticTypeCheckingTestCase {
 
     // GROOVY-10668
     void testMultipleInstanceOf8() {
-        for (string in ['(value as String)', 'value.toString()', 'value']) {
+        for (string in ['(value as String)', 'value.toString()']) {
             assertScript """
                 def toArray(Object value) {
                     def array
-                    if (value instanceof List)
+                    if (value instanceof List) {
                         array = value.toArray()
-                    else if (value instanceof String || value instanceof 
GString)
+                    } else if (value instanceof String || value instanceof 
GString) {
                         array = ${string}.split(',')
-                    else
+                    } else {
                         throw new Exception('not supported')
-
+                    }
                     return array
                 }
-                toArray([1,2,3])
-                toArray('1,2,3')
+                assert toArray([1,2,3]) == new Object[]{1,2,3}
+                assert toArray('1,2,3') == new String[]{'1','2','3'}
+                assert toArray("\${1}") == new String[]{'1'}
             """
         }
     }
 
+    // GROOVY-10702
+    void testMultipleInstanceOf9() {
+        shouldFailWithMessages '''
+            def toArray(value) {
+                def array
+                if (value instanceof String || value instanceof GString) {
+                    array = value.split(',') // split(String) not declared for 
GString/CharSequence
+                }
+                array
+            }
+        ''',
+        'Cannot find matching method (java.lang.String | 
groovy.lang.GString)#split(java.lang.String)'
+
+        shouldFailWithMessages '''
+            def test(o) {
+                (o instanceof List || o instanceof Map) ? o.entrySet() : null
+            }
+            test([])
+        ''',
+        'Cannot find matching method (java.util.List | 
java.util.Map)#entrySet()'
+    }
+
     // GROOVY-6429
     void testNotInstanceof1() {
         String types = '''
@@ -1603,46 +1626,46 @@ class TypeInferenceSTCTest extends 
StaticTypeCheckingTestCase {
         '''
     }
 
-    // GROOVY-
+    // GROOVY-6207
     void testGetAnnotationFails() {
         assertScript '''
             import groovy.transform.*
             import java.lang.annotation.*
 
             @Retention(RetentionPolicy.RUNTIME)
-            @Target([ElementType.FIELD])
-            @interface Ann1 {}
+            @Target(ElementType.FIELD)
+            @interface Tag1 {}
 
             @Retention(RetentionPolicy.RUNTIME)
-            @Target([ElementType.FIELD])
-            @interface Ann2 {}
+            @Target(ElementType.FIELD)
+            @interface Tag2 {}
 
             class A {
-                @Ann2
+                @Tag2
                 String field
             }
 
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 lookup('second').each {
-                    assert it.expression.getNodeMetaData(INFERRED_TYPE).name 
== 'Ann2'
+                    assert it.expression.getNodeMetaData(INFERRED_TYPE).name 
== 'Tag2'
                 }
             })
             def doit(obj, String propName) {
                 def field = obj.getClass().getDeclaredField(propName)
                 if (field) {
                     @ASTTest(phase=INSTRUCTION_SELECTION, value={
-                        assert node.getNodeMetaData(INFERRED_TYPE).name == 
'Ann1'
+                        assert node.getNodeMetaData(INFERRED_TYPE).name == 
'Tag1'
                     })
-                    def annotation = field.getAnnotation Ann1
+                    def annotation = field.getAnnotation Tag1
                     if(true) {
-                        second: annotation = field.getAnnotation Ann2
+                        second: annotation = field.getAnnotation Tag2
                     }
                     return annotation
                 }
                 return null
             }
 
-            assert Ann2.isAssignableFrom(doit(new A(), "field").class)
+            assert Tag2.isAssignableFrom(doit(new A(), 'field').class)
         '''
     }
 

Reply via email to