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
'''
}