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 9b3eeb9e22da47c5650a187fcb8e1b23936321e5 Author: Eric Milles <[email protected]> AuthorDate: Sat Dec 27 15:09:20 2025 -0600 GROOVY-10702: STC: multiple `instanceof` and sparse property access --- .../transform/stc/StaticTypeCheckingVisitor.java | 13 ++++- .../transform/stc/TypeInferenceSTCTest.groovy | 65 ++++++++++++++-------- 2 files changed, 53 insertions(+), 25 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 5b5e709a82..fec219cd57 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -1653,9 +1653,18 @@ out: if ((samParameterTypes.length == 1 && isOrImplements(samParameterTypes[0 List<Receiver<String>> receivers = new ArrayList<>(); addReceivers(receivers, makeOwnerList(objectExpression), pexp.isImplicitThis()); - for (Receiver<String> receiver : receivers) { + on: for (Receiver<String> receiver : receivers) { ClassNode receiverType = receiver.getType(); - + if (receiverType instanceof UnionTypeClassNode ut) { + for (ClassNode type : ut.getDelegates()) { // GROOVY-8965, GROOVY-10702 + var copy = (PropertyExpression) pexp.transformExpression(ex -> ex); + copy.setObjectExpression(varX("_", type)); + if (!existsProperty(copy, readMode, null)) + continue on; + } + pexp.putNodeMetaData(DYNAMIC_RESOLUTION, Boolean.TRUE); + return true; + } if (receiverType.isArray() && propertyName.equals("length")) { pexp.putNodeMetaData(READONLY_PROPERTY, Boolean.TRUE); storeType(pexp, int_TYPE); diff --git a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy index 94d38ad2c0..884423da79 100644 --- a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy +++ b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy @@ -403,17 +403,17 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase { int foo() { 1 } } class B { - int foo2() { 2 } + int bar() { 2 } } def o = new A() - int result = o instanceof A ? o.foo() : (o instanceof B ? o.foo2() : 3) + 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.foo2() : 3) + 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.foo2() : 3) + result = o instanceof A ? o.foo() : (o instanceof B ? o.bar() : 3) assert result == 3 ''' } @@ -421,22 +421,43 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase { void testMultipleInstanceOf1() { assertScript ''' class A { - void bar() { + void m() { } } class B { - void bar() { + void m() { } } void test(o) { if (o instanceof A || o instanceof B) { - o.bar() + o.m() } } test(new A()) test(new B()) ''' + + assertScript ''' + class A { + float getP() { + return 1.0 + } + } + class B { + short getP() { + return 2 + } + } + + def foo(o) { + if (o instanceof A || o instanceof B) { + return o.p + } + } + assert foo(new A()) == 1 + assert foo(new B()) == 2 + ''' } void testMultipleInstanceOf2() { @@ -465,19 +486,17 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase { // GROOVY-8965 void testMultipleInstanceOf4() { - for (o in ['o', '((Number) o)']) { - assertScript """ - def foo(o) { - if (o instanceof Integer || o instanceof Double) { - ${o}.floatValue() // ClassCastException - } + assertScript ''' + def foo(o) { + if (o instanceof Integer || o instanceof Double) { + o.floatValue() // ClassCastException } - def bar = foo(1.1d) - assert bar == 1.1f - def baz = foo(1) - assert baz == 1 - """ - } + } + def bar = foo(1.1d) + assert bar == 1.1f + def baz = foo(1) + assert baz == 1 + ''' } // GROOVY-11559 @@ -1312,7 +1331,7 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase { } ''', 'No such property: canonicalName for class: java.lang.Object' -/* + shouldFailWithMessages ''' void test(something) { switch (something) { @@ -1320,12 +1339,12 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase { case File: something.toString() default: - something.canonicalName // TODO: GROOVY-10702 + something.canonicalName // GROOVY-10702 } } ''', - 'No such property: canonicalName for class: java.lang.Object' -*/ + 'No such property: canonicalName for class: (java.lang.Class | java.io.File | java.lang.Object)' + shouldFailWithMessages ''' void test(something) { switch (something) {
