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

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


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

commit 99ddcf17bed599ecb13b7ddec6227a330e3a53d5
Author: Eric Milles <eric.mil...@thomsonreuters.com>
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         | 55 +++++++++++++++++++++-
 2 files changed, 64 insertions(+), 6 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 2a9db0a400..761fcde041 100644
--- a/src/test/groovy/groovy/PropertyTest.groovy
+++ b/src/test/groovy/groovy/PropertyTest.groovy
@@ -132,6 +132,57 @@ final class PropertyTest {
         assert foo.body == "James"
     }
 
+    // GROOVY-11675
+    @Test
+    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)
+        '''
+    }
+
     @Test
     void testFinalProperty() {
         assertScript '''
@@ -821,7 +872,7 @@ final class PropertyTest {
     static class Child extends Base {
         protected String field = 'foo' + super.field
 
-            def getField() { field }
+        def getField() { field }
 
         void setSuperField(value) { super.field = value }
 
@@ -829,7 +880,7 @@ final class PropertyTest {
 
         def thing = 'bar thing'
 
-            def superthing() {
+        def superthing() {
             'bar1' + super.thing
         }
 

Reply via email to