You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2019/01/21 04:43:50 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-8873: Fails at runtime with @CompileStatic and two nested with statements with ClassCastException (closes #854)

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new f71ddf6  GROOVY-8873: Fails at runtime with @CompileStatic and two nested with statements with ClassCastException (closes #854)
f71ddf6 is described below

commit f71ddf6b39002bdcc6e62a17c8e04cb4f82b445b
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 627e7d0..497f9a2 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1725,6 +1725,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;
@@ -3403,6 +3404,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);
@@ -3461,6 +3463,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()
+        '''
+    }
+}