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

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

commit 58c51ec097eec7da2ac7c5eeb80635aca1ec0fd3
Author: Eric Milles <eric.mil...@thomsonreuters.com>
AuthorDate: Sun Oct 10 19:32:43 2021 -0500

    GROOVY-5450: STC: cannot write final field after initializer
---
 .../groovy/ast/MethodCallTransformation.java       |  8 ++--
 .../transform/stc/StaticTypeCheckingVisitor.java   | 13 ++++++-
 .../stc/FieldsAndPropertiesSTCTest.groovy          | 45 ++++++++++++++++------
 3 files changed, 48 insertions(+), 18 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/ast/MethodCallTransformation.java 
b/src/main/java/org/codehaus/groovy/ast/MethodCallTransformation.java
index f07358f790..2535134b0d 100644
--- a/src/main/java/org/codehaus/groovy/ast/MethodCallTransformation.java
+++ b/src/main/java/org/codehaus/groovy/ast/MethodCallTransformation.java
@@ -73,11 +73,9 @@ public abstract class MethodCallTransformation implements 
ASTTransformation {
                     }
 
                     try {
-                        if (classNode.getObjectInitializerStatements() != 
null) {
-                            for (Statement node : 
classNode.getObjectInitializerStatements()) {
-                                if (node != null) {
-                                    node.visit(transformer);
-                                }
+                        for (Statement node : 
classNode.getObjectInitializerStatements()) {
+                            if (node != null) {
+                                node.visit(transformer);
                             }
                         }
                     } catch (MissingPropertyException ignored) {
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 aec63d1e65..f56e4f1bdb 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1877,7 +1877,9 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
             expressionToStoreOn.putNodeMetaData(IMPLICIT_RECEIVER, 
delegationData);
         }
         if (field.isFinal()) {
-          //expressionToStoreOn.putNodeMetaData(READONLY_PROPERTY, 
Boolean.TRUE); // GROOVY-5450
+            MethodNode enclosing = typeCheckingContext.getEnclosingMethod();
+            if (enclosing == null || !enclosing.getName().endsWith("init>"))
+                expressionToStoreOn.putNodeMetaData(READONLY_PROPERTY, 
Boolean.TRUE); // GROOVY-5450
         } else if (accessible) {
             expressionToStoreOn.removeNodeMetaData(READONLY_PROPERTY);
         }
@@ -2700,6 +2702,15 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
         }
     }
 
+    @Override
+    protected void visitObjectInitializerStatements(final ClassNode node) {
+        // GROOVY-5450: create fake constructor node so final field analysis 
can allow write within non-static initializer block(s)
+        ConstructorNode init = new ConstructorNode(0, null, null, new 
BlockStatement(node.getObjectInitializerStatements(), null));
+        typeCheckingContext.pushEnclosingMethod(init);
+        super.visitObjectInitializerStatements(node);
+        typeCheckingContext.popEnclosingMethod();
+    }
+
     protected void addTypeCheckingInfoAnnotation(final MethodNode node) {
         // TypeChecked$TypeCheckingInfo can not be applied on constructors
         if (node instanceof ConstructorNode) return;
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy 
b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index e6253ad0bd..ca4fc301cc 100644
--- a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -57,6 +57,15 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
             }
             new C().x
         '''
+        assertScript '''
+            class C {
+                final x
+                C(def x) {
+                    this.x = x
+                }
+            }
+            new C(null).x
+        '''
         assertScript '''
             class C {
                 final x;
@@ -68,21 +77,21 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
         '''
         assertScript '''
             class C {
-                static final x;
-                static {
-                    x = null
+                final x;
+                {
+                    this.x = x
                 }
             }
             new C().x
         '''
         assertScript '''
             class C {
-                public final x
-                C(Object x) {
-                    this.x = x
+                static final x
+                static {
+                    this.x = null
                 }
             }
-            new C(null).x
+            new C().x
         '''
     }
 
@@ -90,27 +99,39 @@ class FieldsAndPropertiesSTCTest extends 
StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             int[] array = []
             array.length = 1
-        ''', 'Cannot set read-only property: length'
+        ''',
+        'Cannot set read-only property: length'
 
         shouldFailWithMessages '''
             class C { final x }
             new C().x = null
-        ''', 'Cannot set read-only property: x'
+        ''',
+        'Cannot set read-only property: x'
+
+        // GROOVY-5450
+        shouldFailWithMessages '''
+            class C { final x }
+            new C().@x = null
+        ''',
+        'Cannot set read-only property: x'
 
         shouldFailWithMessages '''
             class C { final x }
             new C().with { x = null }
-        ''', 'Cannot set read-only property: x'
+        ''',
+        'Cannot set read-only property: x'
 
         shouldFailWithMessages '''
             class C { final x }
             new C().with { delegate.x = null }
-        ''', 'Cannot set read-only property: x'
+        ''',
+        'Cannot set read-only property: x'
 
         shouldFailWithMessages '''
             class C { final x }
             new C().setX(null)
-        ''', 'Cannot find matching method C#setX(<unknown parameter type>).'
+        ''',
+        'Cannot find matching method C#setX(<unknown parameter type>).'
     }
 
     void testInferenceFromFieldType() {

Reply via email to