@@ -431,6 +431,144 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
431431 ' Incompatible instanceof types: java.lang.Integer and java.lang.Long'
432432 }
433433
434+ // GROOVY-7971: nested && within || — method calls on narrowed types within
435+ // each && branch should work; the || produces a union for the body
436+ @Test
437+ void testInstanceOf18 () {
438+ assertScript '''
439+ int test(Object x) {
440+ if (x instanceof String && x.length() > 0 || x instanceof List && x.size() > 0) {
441+ return 1
442+ }
443+ return 0
444+ }
445+ assert test('hello') == 1
446+ assert test([1, 2, 3]) == 1
447+ assert test('') == 0
448+ assert test([]) == 0
449+ assert test(42) == 0
450+ '''
451+ }
452+
453+ // GROOVY-7971: ternary with || instanceof in condition
454+ @Test
455+ void testInstanceOf19 () {
456+ assertScript '''
457+ String test(Object x) {
458+ (x instanceof String || x instanceof Integer) ? x.toString() : 'other'
459+ }
460+ assert test('hi') == 'hi'
461+ assert test(42) == '42'
462+ assert test(3.14) == 'other'
463+ '''
464+ }
465+
466+ // GROOVY-7971: negated || instanceof
467+ @Test
468+ void testInstanceOf20 () {
469+ assertScript '''
470+ void test(Object x) {
471+ if (!(x instanceof String || x instanceof Integer)) {
472+ assert x != null
473+ }
474+ }
475+ test('hello')
476+ test(42)
477+ test(3.14)
478+ '''
479+ }
480+
481+ // GROOVY-7971: chained || with 3+ instanceof checks
482+ @Test
483+ void testInstanceOf21 () {
484+ assertScript '''
485+ void test(Object x) {
486+ if (x instanceof String || x instanceof Integer || x instanceof List) {
487+ assert "$x" != null // should be String|Integer|List
488+ }
489+ }
490+ test('hello')
491+ test(42)
492+ test([1, 2])
493+ '''
494+ }
495+
496+ // GROOVY-7971: RHS of || should not see LHS instanceof narrowing
497+ @Test
498+ void testInstanceOf22 () {
499+ assertScript '''
500+ void test(Number n) {
501+ if (n instanceof Integer || n.doubleValue() > 0) {
502+ assert "$n" != null // n should be Integer|Number
503+ }
504+ }
505+ test(42)
506+ test(1.5)
507+ '''
508+ }
509+
510+ // GROOVY-7971: closure shared variable with || instanceof
511+ @Test
512+ void testInstanceOf23 () {
513+ assertScript '''
514+ void test(Object x) {
515+ if (x instanceof String || x instanceof Integer) {
516+ def c = { -> x.toString() }
517+ assert c() != null
518+ }
519+ }
520+ test('hello')
521+ test(42)
522+ '''
523+ }
524+
525+ // GROOVY-11888: method resolution on union type — toString() is on Object
526+ // and should be found via (String|List) union
527+ @Test
528+ void testInstanceOf24 () {
529+ assertScript '''
530+ void test(Object x) {
531+ if (x instanceof String || x instanceof List) {
532+ assert x.toString() != null
533+ }
534+ }
535+ test('hello')
536+ test([1, 2])
537+ '''
538+ }
539+
540+ // GROOVY-7971: negated || instanceof — re-check instanceof in else branch
541+ @Test @org.junit.jupiter.api.Disabled (' requires instanceof compatibility fix for UnionTypeClassNode' )
542+ void testInstanceOf25 () {
543+ assertScript '''
544+ void test(Object x) {
545+ if (!(x instanceof String || x instanceof Integer)) {
546+ assert x != null
547+ } else {
548+ assert x instanceof String || x instanceof Integer
549+ }
550+ }
551+ test('hello')
552+ test(42)
553+ test(3.14)
554+ '''
555+ }
556+
557+ // GROOVY-11888: method resolution on union type — intValue() is on Number
558+ // and should be found via (Integer|Number) union
559+ @Test
560+ void testInstanceOf26 () {
561+ assertScript '''
562+ void test(Number n) {
563+ if (n instanceof Integer || n.intValue() > 0) {
564+ assert n.intValue() >= 0
565+ }
566+ }
567+ test(42)
568+ test(1.5)
569+ '''
570+ }
571+
434572 // GROOVY-5226
435573 @Test
436574 void testNestedInstanceOf1 () {
0 commit comments