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


The following commit(s) were added to refs/heads/master by this push:
     new 93f0d096c5 GROOVY-11403: STC: map property precedence: entry before 
super field
93f0d096c5 is described below

commit 93f0d096c5c23256add266ff8f0f7d6a953a65ee
Author: Eric Milles <eric.mil...@thomsonreuters.com>
AuthorDate: Tue Jun 18 14:58:33 2024 -0500

    GROOVY-11403: STC: map property precedence: entry before super field
---
 .../classgen/asm/sc/StaticTypesCallSiteWriter.java | 10 ++--
 .../transform/stc/StaticTypeCheckingVisitor.java   |  2 +-
 src/test/groovy/bugs/Groovy9007.groovy             | 12 +++--
 .../stc/FieldsAndPropertiesSTCTest.groovy          | 58 ++++++++++++++++++++--
 4 files changed, 66 insertions(+), 16 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
index 874723724b..51b7f7d358 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
@@ -53,7 +53,6 @@ import org.objectweb.asm.MethodVisitor;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.function.BiPredicate;
 import java.util.function.Predicate;
 
@@ -190,7 +189,7 @@ public class StaticTypesCallSiteWriter extends 
CallSiteWriter {
         // GROOVY-5001, GROOVY-5491, GROOVY-5517, GROOVY-6144, GROOVY-8788: 
for map types,
         // replace "map.foo" with "map.get('foo')" -- if no public field "foo" 
is declared
         if (!isStaticProperty && isOrImplements(receiverType, MAP_TYPE)
-                && Optional.ofNullable(getField(receiverType, 
propertyName)).filter(FieldNode::isPublic).isEmpty()) {
+                && getField(receiverType, propertyName, FieldNode::isPublic) 
== null) {
             writeMapDotProperty(receiver, propertyName, safe);
             return;
         }
@@ -416,8 +415,9 @@ public class StaticTypesCallSiteWriter extends 
CallSiteWriter {
         // GROOVY-5001, GROOVY-5491, GROOVY-5517, GROOVY-6144, GROOVY-8788: 
for map types,
         // replace "map.foo" with "map.get('foo')" -- if no public field "foo" 
is declared
         if (isMapDotProperty
-                && (!isThisExpression(receiver) || 
controller.isInGeneratedFunction()) // GROOVY-8978, GROOVY-11402
-                && Optional.ofNullable(getField(receiverType, 
propertyName)).filter(FieldNode::isPublic).isEmpty()) {
+                && getField(receiverType, propertyName, FieldNode::isPublic) 
== null
+                // GROOVY-11367, GROOVY-11402, GROOVY-11403: "this.name" 
outside closure includes non-public fields of lexical scope
+                && (!isThisExpression(receiver) || 
controller.isInGeneratedFunction() || 
receiverType.getDeclaredField(propertyName) == null)) {
             writeMapDotProperty(receiver, propertyName, safe);
             return;
         }
@@ -800,7 +800,7 @@ public class StaticTypesCallSiteWriter extends 
CallSiteWriter {
             // GROOVY-6954, GROOVY-11376: for map types, replace "map.foo = 
..."
             // with "map.put('foo', ...)" if no public field exists
             if (!isClassReceiver[0] && isOrImplements(receiverType, MAP_TYPE)
-                    && Optional.ofNullable(getField(receiverType, 
name)).filter(FieldNode::isPublic).isEmpty()) {
+                    && getField(receiverType, name, FieldNode::isPublic) == 
null) {
                 MethodVisitor mv = controller.getMethodVisitor();
 
                 // store value in temporary variable
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 f3195562bb..1990049d73 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1658,7 +1658,7 @@ out:    if ((samParameterTypes.length == 1 && 
isOrImplements(samParameterTypes[0
 
                 if (field != null && publicOnly && !field.isPublic()
                         // GROOVY-11367, GROOVY-11402, GROOVY-11403:
-                        && !(isThisExpression(objectExpression) && 
typeCheckingContext.getEnclosingClosure() == null)) {
+                        && (!receiverType.equals(current) || 
!isThisExpression(objectExpression) || 
typeCheckingContext.getEnclosingClosure() != null)) {
                     field = null;
                 }
                 // skip property/accessor checks for "field", "this.field", 
"this.with { field }", etc. within the declaring class of the field
diff --git a/src/test/groovy/bugs/Groovy9007.groovy 
b/src/test/groovy/bugs/Groovy9007.groovy
index 20b5e169e4..e92a7a3563 100644
--- a/src/test/groovy/bugs/Groovy9007.groovy
+++ b/src/test/groovy/bugs/Groovy9007.groovy
@@ -70,19 +70,21 @@ final class Groovy9007 {
 
             @CompileStatic
             class TaskConfig extends LazyMap implements Cloneable {
-                TaskConfig() {
-                }
                 @Override
                 TaskConfig clone() {
                     def copy = (TaskConfig) super.clone()
-                    copy.target = new HashMap<>(this.target)
+                    copy.@target = new HashMap<>(this)
                     return copy
                 }
             }
 
-            def t1 = new TaskConfig()
-            t1.a = 'b'
+            def t1 = new TaskConfig().tap{ a = 'b' }
             def t2 = t1.clone()
+            assert t2 !== t1
+            assert t2.a == 'b'
+
+            t1.remove('a') // ensure independence
+            assert !t1.containsKey('a')
             assert t2.a == 'b'
         '''
     }
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy 
b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index bcf07630f1..a0ec654c90 100644
--- a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -979,6 +979,54 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
 
     // GROOVY-11403
     void testMapPropertyAccess12() {
+        for (mode in ['','static']) {
+            assertScript """
+                class M {
+                    @Delegate final Map m = [:].withDefault{ 'entry' }
+                    def           $mode v = 'field'
+                    public        $mode w = 'field'
+                    protected     $mode x = 'field'
+                    @PackageScope $mode y = 'field'
+                    private       $mode z = 'field'
+                    void test1() {
+                        assert this.m    === this.@m
+                        assert this.v     == 'field'
+                        assert this.w     == 'field'
+                        assert this.x     == 'field'
+                        assert this.y     == 'field'
+                        assert this.z     == 'field';
+                        {->
+                            assert this.v == 'field'
+                            assert this.w == 'field'
+                            assert this.x == 'entry'
+                            assert this.y == 'entry'
+                            assert this.z == 'entry'
+                        }()
+                    }
+                }
+                class MM extends M {
+                    void test2() {
+                        assert this.v     == 'field'
+                        assert this.w     == 'field'
+                        assert this.x     == 'entry'
+                        assert this.y     == 'entry'
+                        assert this.z     == 'entry';
+                        {->
+                            assert this.v == 'field'
+                            assert this.w == 'field'
+                            assert this.x == 'entry'
+                            assert this.y == 'entry'
+                            assert this.z == 'entry'
+                        }()
+                    }
+                }
+                new MM().with { test1(); test2() }
+            """
+        }
+    }
+
+    // GROOVY-11403
+    void testMapPropertyAccess13() {
         for (mode in ['','static']) {
             assertScript """
                 class M {
@@ -1109,7 +1157,7 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
     }
 
     // GROOVY-11368
-    void testMapPropertyAccess13() {
+    void testMapPropertyAccess14() {
         String type = '''
             class M implements Map<String,String> {
                 @Delegate Map<String,String> impl = [:]
@@ -1134,7 +1182,7 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
     }
 
     // GROOVY-11367
-    void testMapPropertyAccess14() {
+    void testMapPropertyAccess15() {
         String map = "def map = new ${MapType.name}()"
         assertScript map + '''
             map.foo = 11 // public setter
@@ -1155,7 +1203,7 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
     }
 
     // GROOVY-11387
-    void testMapPropertyAccess15() {
+    void testMapPropertyAccess16() {
         assertScript '''
             def map = new HashMap<String,String>()
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
@@ -1177,7 +1225,7 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
     }
 
     // GROOVY-11387
-    void testMapPropertyAccess16() {
+    void testMapPropertyAccess17() {
         assertScript '''
             class HttpHeaders extends HashMap<String,List<String>> {
                 protected static final String ACCEPT = 'Accept'
@@ -1191,7 +1239,7 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
     }
 
     // GROOVY-11401
-    void testMapPropertyAccess17() {
+    void testMapPropertyAccess18() {
         assertScript '''
             class C {
                 private Object obj = 'field'

Reply via email to