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() {