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

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


The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
     new c9ea8e9026 GROOVY-11675: split property may declare final modifier for 
accessors
c9ea8e9026 is described below

commit c9ea8e902699982dc66c17a489065f0495821822
Author: Eric Milles <[email protected]>
AuthorDate: Wed Sep 17 08:44:56 2025 -0500

    GROOVY-11675: split property may declare final modifier for accessors
---
 .../groovy/ast/tools/PropertyNodeUtils.java        |  15 ++-
 src/test/groovy/groovy/PropertyTest.groovy         | 101 +++++++++++++++------
 2 files changed, 85 insertions(+), 31 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/PropertyNodeUtils.java 
b/src/main/java/org/codehaus/groovy/ast/tools/PropertyNodeUtils.java
index 1f282023d8..a0abc685a6 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/PropertyNodeUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/PropertyNodeUtils.java
@@ -23,6 +23,7 @@ import org.codehaus.groovy.ast.PropertyNode;
 import java.lang.reflect.Modifier;
 
 public class PropertyNodeUtils {
+
     /**
      * Fields within the AST that have no explicit visibility are deemed to be 
properties
      * and represented by a PropertyNode. The Groovy compiler creates accessor 
methods and
@@ -33,11 +34,17 @@ public class PropertyNodeUtils {
      * methods (such as {@code volatile} and {@code transient}) but other 
modifiers are carried over,
      * for example {@code static}.
      *
-     * @param propNode the original property node
+     * @since 2.4.8
+     * @param node the original property node
      * @return the modifiers which make sense for an accessor method
      */
-    public static int adjustPropertyModifiersForMethod(PropertyNode propNode) {
-        // GROOVY-3726: clear some modifiers so that they do not get applied 
to methods
-        return propNode.getModifiers() & ~(Modifier.FINAL | Modifier.TRANSIENT 
| Modifier.VOLATILE);
+    public static int adjustPropertyModifiersForMethod(final PropertyNode 
node) {
+        // GROOVY-3726, GROOVY-7969: clear modifiers that do not apply to 
methods
+        int mods = node.getModifiers() & 
~(Modifier.TRANSIENT|Modifier.VOLATILE);
+        // GROOVY-11675: split property case may declare final modifier
+        if (node.getField() == null || node.getField().isSynthetic()) {
+            mods &= ~Modifier.FINAL;
+        }
+        return mods;
     }
 }
diff --git a/src/test/groovy/groovy/PropertyTest.groovy 
b/src/test/groovy/groovy/PropertyTest.groovy
index fdbd437b56..6d7564965c 100644
--- a/src/test/groovy/groovy/PropertyTest.groovy
+++ b/src/test/groovy/groovy/PropertyTest.groovy
@@ -117,38 +117,85 @@ class PropertyTest extends GroovyTestCase {
         assert foo.body == "James"
     }
 
+    // GROOVY-11675
+    void testSplitProperty() {
+        assertScript '''import java.lang.reflect.*
+            class C {
+                @Deprecated private final Integer one
+                final Integer one
+
+                protected synchronized Integer two
+                synchronized Integer two
+
+                public Integer three
+                @Deprecated Integer three
+            }
+
+            Member m = C.getDeclaredField('one')
+            assert m.isAnnotationPresent(Deprecated)
+            assert m.modifiers == Modifier.PRIVATE + Modifier.FINAL
+
+            m = C.getDeclaredMethod('getOne')
+            assert !m.isAnnotationPresent(Deprecated)
+            assert m.modifiers == Modifier.PUBLIC + Modifier.FINAL
+
+            groovy.test.GroovyAssert.shouldFail(NoSuchMethodException) {
+                m = C.getDeclaredMethod('setOne', Integer)
+            }
+
+            m = C.getDeclaredField('two')
+            assert m.modifiers == Modifier.PROTECTED
+            // field cannot carry modifier SYNCHRONIZED
+
+            m = C.getDeclaredMethod('getTwo')
+            assert m.modifiers == Modifier.PUBLIC + Modifier.SYNCHRONIZED
+
+            m = C.getDeclaredMethod('setTwo', Integer)
+            assert m.modifiers == Modifier.PUBLIC + Modifier.SYNCHRONIZED
+
+            m = C.getDeclaredField('three')
+            assert m.modifiers == Modifier.PUBLIC
+            assert !m.isAnnotationPresent(Deprecated)
+
+            m = C.getDeclaredMethod('getThree')
+            assert m.modifiers == Modifier.PUBLIC
+            assert m.isAnnotationPresent(Deprecated)
+
+            m = C.getDeclaredMethod('setThree', Integer)
+            assert m.modifiers == Modifier.PUBLIC
+            assert m.isAnnotationPresent(Deprecated)
+        '''
+    }
+
     void testFinalProperty() {
-        def shell = new GroovyShell();
-        assertScript """
-        class A {
-           final foo = 1
-        }
-        A.class.declaredMethods.each {
-          assert it.name!="setFoo"
+        assertScript '''
+            class C {
+               final foo = 1
+            }
+            C.declaredMethods.each {
+                assert it.name != "setFoo"
+            }
 
-        }
-        assert new A().foo==1
-      """
-        shouldFail {
-            shell.execute """
-          class A {
-            final foo = 1
-          }
-          new A().foo = 2
-        """
-        }
+            assert new C().foo == 1
+        '''
+
+        shouldFail '''
+            class C {
+                final foo = 1
+            }
+
+            new C().foo = 2
+        '''
     }
 
     void testFinalField() {
-        def shell = new GroovyShell();
-        shouldFail {
-            shell.execute """
-          class A {
-            public final foo = 1
-          }
-          new A().foo = 2
-        """
-        }
+        shouldFail '''
+            class C {
+                public final foo = 1
+            }
+
+            new C().foo = 2
+        '''
     }
 
     void testFinalPropertyWithInheritance() {

Reply via email to