Skip to content

Commit 5b62bf5

Browse files
committed
GROOVY-11889: STC: instanceof check rejects UnionTypeClassNode members as incompatible types
1 parent bdfb672 commit 5b62bf5

2 files changed

Lines changed: 58 additions & 3 deletions

File tree

src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,16 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
982982
} else if (op == KEYWORD_INSTANCEOF) {
983983
rType = rightExpression.getType();
984984
if (lType instanceof WideningCategories.LowestUpperBoundClassNode) lType = lType.getUnresolvedSuperClass();
985-
if (!lType.isInterface() && !rType.isInterface() && !(lType.isDerivedFrom(rType) || rType.isDerivedFrom(lType))) {
985+
if (lType instanceof UnionTypeClassNode union) {
986+
boolean compatible = rType.isInterface();
987+
for (int i = 0; !compatible && i < union.getDelegates().length; i += 1) {
988+
ClassNode delegate = union.getDelegates()[i];
989+
compatible = delegate.isInterface() || delegate.isDerivedFrom(rType) || rType.isDerivedFrom(delegate);
990+
}
991+
if (!compatible) {
992+
addError("Incompatible instanceof types: " + prettyPrintTypeName(lType) + " and " + prettyPrintTypeName(rType), expression);
993+
}
994+
} else if (!lType.isInterface() && !rType.isInterface() && !(lType.isDerivedFrom(rType) || rType.isDerivedFrom(lType))) {
986995
addError("Incompatible instanceof types: " + prettyPrintTypeName(lType) + " and " + prettyPrintTypeName(rType), expression);
987996
}
988997
pushInstanceOfTypeInfo(leftExpression, rightExpression);

src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -537,8 +537,8 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
537537
'''
538538
}
539539

540-
// GROOVY-7971: negated || instanceof — re-check instanceof in else branch
541-
@Test @org.junit.jupiter.api.Disabled('requires instanceof compatibility fix for UnionTypeClassNode')
540+
// GROOVY-11889: negated || instanceof — re-check instanceof in else branch
541+
@Test
542542
void testInstanceOf25() {
543543
assertScript '''
544544
void test(Object x) {
@@ -569,6 +569,52 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
569569
'''
570570
}
571571

572+
// GROOVY-11889: instanceof interface check on union type
573+
@Test
574+
void testInstanceOf27() {
575+
assertScript '''
576+
void test(Object x) {
577+
if (x instanceof String || x instanceof Integer) {
578+
if (x instanceof Serializable) {
579+
assert true
580+
}
581+
}
582+
}
583+
test('hello')
584+
test(42)
585+
'''
586+
}
587+
588+
// GROOVY-11889: union containing interface delegate
589+
@Test
590+
void testInstanceOf28() {
591+
assertScript '''
592+
void test(Object x) {
593+
if (x instanceof Serializable || x instanceof String) {
594+
if (x instanceof String) {
595+
assert true
596+
}
597+
}
598+
}
599+
test('hello')
600+
'''
601+
}
602+
603+
// GROOVY-11889: incompatible instanceof on union type should produce error
604+
@Test
605+
void testInstanceOf29() {
606+
shouldFailWithMessages '''
607+
void test(Object x) {
608+
if (x instanceof String || x instanceof Integer) {
609+
if (x instanceof Long) {
610+
// unreachable - neither String nor Integer is related to Long
611+
}
612+
}
613+
}
614+
''',
615+
'Incompatible instanceof types'
616+
}
617+
572618
// GROOVY-5226
573619
@Test
574620
void testNestedInstanceOf1() {

0 commit comments

Comments
 (0)