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 2026/03/01 00:14:53 UTC

(groovy) 01/03: STC: check compatibility of instanceof class types

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 109e9e8864abd0da0ed40501c1f96c25343d028b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Feb 28 14:06:56 2026 -0600

    STC: check compatibility of instanceof class types
---
 .../transform/stc/StaticTypeCheckingVisitor.java   |  7 +++-
 .../groovy/transform/stc/LoopsSTCTest.groovy       |  2 +-
 .../groovy/transform/stc/MethodCallsSTCTest.groovy | 18 ++-------
 .../transform/stc/TypeInferenceSTCTest.groovy      | 43 +++++++++++++++-------
 4 files changed, 41 insertions(+), 29 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 93a1976609..ac94f18e23 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -977,6 +977,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     }
                 }
             } else if (op == KEYWORD_INSTANCEOF) {
+                rType = rightExpression.getType();
+                if (lType instanceof WideningCategories.LowestUpperBoundClassNode) lType = lType.getUnresolvedSuperClass();
+                if (!lType.isInterface() && !rType.isInterface() && !(lType.isDerivedFrom(rType) || rType.isDerivedFrom(lType))) {
+                    addError("Incompatible instanceof types: " + prettyPrintTypeName(lType) + " and " + prettyPrintTypeName(rType), expression);
+                }
                 pushInstanceOfTypeInfo(leftExpression, rightExpression);
             } else if (op == COMPARE_NOT_INSTANCEOF) { // GROOVY-6429, GROOVY-8321, GROOVY-8412, GROOVY-8523, GROOVY-9931
                 putNotInstanceOfTypeInfo(extractTemporaryTypeInfoKey(leftExpression), Set.of(rightExpression.getType()));
@@ -987,7 +992,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             }
 
             // GROOVY-5874: if left expression is a closure shared variable, a second pass should be done
-            if (leftExpression instanceof VariableExpression && ((VariableExpression) leftExpression).isClosureSharedVariable()) {
+            if (leftExpression instanceof VariableExpression vexp && vexp.isClosureSharedVariable()) {
                 typeCheckingContext.secondPassExpressions.add(new SecondPassExpression<>(expression));
             }
         } finally {
diff --git a/src/test/groovy/groovy/transform/stc/LoopsSTCTest.groovy b/src/test/groovy/groovy/transform/stc/LoopsSTCTest.groovy
index fc36b5402d..059dfe0540 100644
--- a/src/test/groovy/groovy/transform/stc/LoopsSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/LoopsSTCTest.groovy
@@ -251,7 +251,7 @@ class LoopsSTCTest extends StaticTypeCheckingTestCase {
             for (String s in 'abc') assert s instanceof String
         '''
         assertScript '''
-            for (char c in 'abc') assert c instanceof Character
+            for (char c in 'abc') assert c !instanceof String
             for (Character c in 'abc') assert c instanceof Character
         '''
     }
diff --git a/src/test/groovy/groovy/transform/stc/MethodCallsSTCTest.groovy b/src/test/groovy/groovy/transform/stc/MethodCallsSTCTest.groovy
index 271cd8a70a..bc59ad8899 100644
--- a/src/test/groovy/groovy/transform/stc/MethodCallsSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/MethodCallsSTCTest.groovy
@@ -797,16 +797,6 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
         'Cannot find matching method','test(java.lang.Integer[])'
     }
 
-    void testInstanceOfOnExplicitParameter() {
-        assertScript '''
-            1.with { obj ->
-                if (obj instanceof String) {
-                    obj.toUpperCase()
-                }
-            }
-        '''
-    }
-
     void testSAMWithExplicitParameter1() {
         assertScript '''
             public interface SAM {
@@ -902,7 +892,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
             def it = ""
             if (it instanceof String) {
                 bar(it)
-                it = 123
+                it = 12
                 foo(it)
             }
         '''
@@ -913,7 +903,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
                 bar(it)
                 if (it instanceof String) {
                     bar(it)
-                    it = 123
+                    it = 12
                     foo(it)
                 } else {
                     bar(it)
@@ -929,9 +919,9 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
             def foo(Date d) { 'Date' }
             def it = ""
             if (it instanceof String) {
-                it = 123
+                it = 12
                 foo(it)
-                if (it instanceof Date) {
+                if (it !instanceof Date) {
                     foo(it)
                 }
             }
diff --git a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
index 5bd24a1e08..a080edb42f 100644
--- a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -298,8 +298,18 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
         'Cannot return value of type Foo for method returning Bar'
     }
 
-    // GROOVY-11007
     void testInstanceOf13() {
+        assertScript '''
+            "".with { x ->
+                if (x instanceof String) {
+                    x.toUpperCase()
+                }
+            }
+        '''
+    }
+
+    // GROOVY-11007
+    void testInstanceOf14() {
         assertScript '''
             interface I {
                 CharSequence getCharSequence()
@@ -319,7 +329,7 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-11290
-    void testInstanceOf14() {
+    void testInstanceOf15() {
         assertScript '''
             def test(List<String> list) {
                 if (list instanceof List) {
@@ -332,7 +342,7 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-11815
-    void testInstanceOf15() {
+    void testInstanceOf16() {
         assertScript '''
             def c = true ? new ArrayDeque() : new Stack()
             if (c instanceof Deque) {
@@ -351,7 +361,7 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-11864
-    void testInstanceOf16() {
+    void testInstanceOf17() {
         assertScript '''
             boolean enabled() {}
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
@@ -368,6 +378,13 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
             }
             assert test(42) == 42
         '''
+        shouldFailWithMessages '''
+            void test(Integer i) {
+                if (i instanceof Long) {
+                }
+            }
+        ''',
+        'Incompatible instanceof types: java.lang.Integer and java.lang.Long'
     }
 
     // GROOVY-5226
@@ -425,16 +442,16 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
             class B {
                int bar() { 2 }
             }
+            class C {
+            }
+
+            def test(o) {
+                o instanceof A ? o.foo() : (o instanceof B ? o.bar() : 3)
+            }
 
-            def o = new A()
-            int result = o instanceof A ? o.foo() : (o instanceof B ? o.bar() : 3)
-            assert result == 1
-            o = new B()
-            result = o instanceof A ? o.foo() : (o instanceof B ? o.bar() : 3)
-            assert result == 2
-            o = new Object()
-            result = o instanceof A ? o.foo() : (o instanceof B ? o.bar() : 3)
-            assert result == 3
+            assert test(new A()) == 1
+            assert test(new B()) == 2
+            assert test(new C()) == 3
         '''
     }