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()
+        '''
+    }
+}

Reply via email to