This is an automated email from the ASF dual-hosted git repository. paulk pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push: new 4a60ce2 GROOVY-8873: Fails at runtime with @CompileStatic and two nested with statements with ClassCastException (closes #854) 4a60ce2 is described below commit 4a60ce2a69f552ba32b1175e96e6de4b051d60c4 Author: Paul King <pa...@asert.com.au> AuthorDate: Thu Jan 17 22:32:28 2019 +1000 GROOVY-8873: Fails at runtime with @CompileStatic and two nested with statements with ClassCastException (closes #854) --- .../transform/stc/StaticTypeCheckingVisitor.java | 35 ++++++++++ .../transform/stc/WithSTCStandaloneTest.groovy | 75 ++++++++++++++++++++++ 2 files changed, 110 insertions(+) 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 e33d38e..fdae2cc 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -1807,6 +1807,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { if (visitor != null) visitor.visitProperty(propertyNode); storeWithResolve(propertyNode.getOriginType(), receiver, propertyNode.getDeclaringClass(), propertyNode.isStatic(), expressionToStoreOn); if (delegationData != null) { + delegationData = adjustData(delegationData, receiver, typeCheckingContext.delegationMetadata); expressionToStoreOn.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData); } return true; @@ -3526,6 +3527,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { } String data = chosenReceiver.getData(); if (data != null) { + data = adjustData(data, chosenReceiver.getType(), typeCheckingContext.delegationMetadata); // the method which has been chosen is supposed to be a call on delegate or owner // so we store the information so that the static compiler may reuse it call.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, data); @@ -3584,6 +3586,39 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { } } + // adjust data to handle cases like nested .with since we didn't have enough information earlier + // TODO see if we can make the earlier detection smarter and then remove this adjustment + private static String adjustData(String data, ClassNode type, DelegationMetadata dmd) { + StringBuilder path = new StringBuilder(); + int i = 0; + String[] propertyPath = data.split("\\."); + while (dmd != null) { + int strategy = dmd.getStrategy(); + ClassNode delegate = dmd.getType(); + dmd = dmd.getParent(); + switch (strategy) { + case Closure.DELEGATE_FIRST: + if (!delegate.isDerivedFrom(CLOSURE_TYPE) && !delegate.isDerivedFrom(type)) { + path.append("owner"); // must be non-delegate case + } else { + path.append("delegate"); + } + break; + default: + if (i >= propertyPath.length) return data; + path.append(propertyPath[i]); + } + if (type.equals(delegate)) break; + i++; + if (dmd != null) path.append('.'); + } + String result = path.toString(); + if (!result.isEmpty()) { + return result; + } + return data; + } + /** * e.g. c(b(a())), a() and b() are nested method call, but c() is not * new C(b(a())) a() and b() are nested method call diff --git a/src/test/groovy/transform/stc/WithSTCStandaloneTest.groovy b/src/test/groovy/transform/stc/WithSTCStandaloneTest.groovy new file mode 100644 index 0000000..c36dd94 --- /dev/null +++ b/src/test/groovy/transform/stc/WithSTCStandaloneTest.groovy @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package groovy.transform.stc + +/** + * Unit tests for static type checking : with method. + */ +class WithSTCStandaloneTest extends GroovyTestCase { + void testMethodAndPropertyAccessWithinNestedWithStatements() { + assertScript ''' + import groovy.transform.CompileStatic + + class Foo { + String foo = 'foo' + String foom() { 'foom' } + } + + class Bar { + String bar = 'bar' + String barm() { 'barm' } + } + + class Baz { + String baz = 'baz' + String bazm() { 'bazm' } + } + + def other() { 'other' } + + @CompileStatic + def main() { + new Foo().with { + assert other() == 'other' + assert foom() == 'foom' + assert foo == 'foo' + new Bar().with { + assert foo == 'foo' + assert bar == 'bar' + assert barm() == 'barm' + assert other() == 'other' + assert foom() == 'foom' + new Baz().with { + assert foo == 'foo' + assert bar == 'bar' + assert baz == 'baz' + assert barm() == 'barm' + assert other() == 'other' + assert foom() == 'foom' + assert bazm() == 'bazm' + } + } + } + } + + main() + ''' + } +}