You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2019/11/05 22:33:22 UTC

[groovy] 01/01: GROOVY-7996: check for get(String)/set(String, Object) or propertyMissing

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

emilles pushed a commit to branch GROOVY-7996
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 3a9b12ad1f3f2307936eb2b3a014a9195d655ff8
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Oct 29 14:37:21 2019 -0500

    GROOVY-7996: check for get(String)/set(String,Object) or propertyMissing
---
 .../transform/stc/StaticTypeCheckingVisitor.java      | 19 +++++++++++++++++++
 src/test/groovy/bugs/Groovy7996.groovy                | 14 +++++++++-----
 .../src/spec/test/MarkupTemplateEngineSpecTest.groovy |  7 ++++---
 3 files changed, 32 insertions(+), 8 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 f38d0d7..3e562e2 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1716,6 +1716,25 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     }
                 }
             }
+
+            // GROOVY-7996: check if receiver implements get(String)/set(String,Object) or propertyMissing(String)
+            if (!testClass.isArray() && !isPrimitiveType(getUnwrapper(testClass))
+                    && objectExpression instanceof VariableExpression && typeCheckingContext.getEnclosingClosure() != null) {
+                MethodNode mopMethod;
+                if (readMode) {
+                    mopMethod = testClass.getMethod("get", new Parameter[]{new Parameter(STRING_TYPE, "name")});
+                } else {
+                    mopMethod = testClass.getMethod("set", new Parameter[]{new Parameter(STRING_TYPE, "name"), new Parameter(OBJECT_TYPE, "value")});
+                }
+                if (mopMethod == null) mopMethod = testClass.getMethod("propertyMissing", new Parameter[]{new Parameter(STRING_TYPE, "propertyName")});
+
+                if (mopMethod != null) {
+                    pexp.putNodeMetaData(DYNAMIC_RESOLUTION, Boolean.TRUE);
+                    pexp.removeNodeMetaData(DECLARATION_INFERRED_TYPE);
+                    pexp.removeNodeMetaData(INFERRED_TYPE);
+                    return true;
+                }
+            }
         }
 
         for (Receiver<String> receiver : receivers) {
diff --git a/src/test/groovy/bugs/Groovy7996.groovy b/src/test/groovy/bugs/Groovy7996.groovy
index fc0872f..39de261 100644
--- a/src/test/groovy/bugs/Groovy7996.groovy
+++ b/src/test/groovy/bugs/Groovy7996.groovy
@@ -18,16 +18,18 @@
  */
 package groovy.bugs
 
-import groovy.test.NotYetImplemented
+import groovy.transform.CompileStatic
 import org.junit.Test
 
 import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
 
+@CompileStatic
 final class Groovy7996 {
 
-    @Test @NotYetImplemented
+    @Test
     void testFieldAccessFromClosure1() {
-        assertScript '''
+        def err = shouldFail '''
             class Foo {
                 def build(@DelegatesTo(value=Foo, strategy=Closure.DELEGATE_FIRST) Closure block) {
                     this.with(block)
@@ -45,13 +47,15 @@ final class Groovy7996 {
                 boolean doStuff() {
                     Foo foo = new Foo()
                     foo.build {
-                        bar.isEmpty() // "ClassCastException: java.lang.String cannot be cast to java.util.List"
+                        bar.isEmpty() // ClassCastException: java.lang.String cannot be cast to java.util.List
                     }
                 }
             }
 
-            assert new Bar().doStuff()
+            new Bar().doStuff()
         '''
+
+        assert err =~ /Cannot find matching method java.lang.Object#isEmpty\(\)/
     }
 
     @Test
diff --git a/subprojects/groovy-templates/src/spec/test/MarkupTemplateEngineSpecTest.groovy b/subprojects/groovy-templates/src/spec/test/MarkupTemplateEngineSpecTest.groovy
index c94f449..da68093 100644
--- a/subprojects/groovy-templates/src/spec/test/MarkupTemplateEngineSpecTest.groovy
+++ b/subprojects/groovy-templates/src/spec/test/MarkupTemplateEngineSpecTest.groovy
@@ -22,7 +22,8 @@ import groovy.text.Template
 import groovy.text.markup.MarkupTemplateEngine
 import groovy.text.markup.TemplateConfiguration
 
-class MarkupTemplateEngineSpecTest extends GroovyTestCase {
+final class MarkupTemplateEngineSpecTest extends GroovyTestCase {
+
     private MarkupTemplateEngine engine
     private TemplateConfiguration config
     private Map model
@@ -621,7 +622,7 @@ pages.each { page ->                    // <1>
         // end::typechecked_setup_fixed[]
          */
 
-        assertError 'No such property: text for class: MarkupTemplateEngineSpecTest$Page'
+        assertError 'No such property: text for class: MarkupTemplateEngineSpec'
     }
 
     void testInlinedTypeCheckedModel() {
@@ -654,7 +655,7 @@ pages.each { page ->
 // end::typechecked_inlined_template_expected[]
 '''
 
-        assertError 'No such property: text for class: MarkupTemplateEngineSpecTest$Page'
+        assertError 'No such property: text for class: MarkupTemplateEngineSpec'
     }
 
     void testFragment() {