This is an automated email from the ASF dual-hosted git repository.
paulk 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 022b14b GROOVY-10252: @Final should have a boolean attribute to
disable
022b14b is described below
commit 022b14bfc6683f4ad45a5bd49bc3112cf3a09d75
Author: Paul King <[email protected]>
AuthorDate: Thu Sep 23 00:02:50 2021 +1000
GROOVY-10252: @Final should have a boolean attribute to disable
---
src/main/java/groovy/transform/Final.java | 10 +++++++
.../groovy/transform/FinalASTTransformation.java | 1 +
.../groovy/transform/FinalTransformTest.groovy | 32 ++++++++++++++++++++++
3 files changed, 43 insertions(+)
diff --git a/src/main/java/groovy/transform/Final.java
b/src/main/java/groovy/transform/Final.java
index 577ac4f..911744d 100644
--- a/src/main/java/groovy/transform/Final.java
+++ b/src/main/java/groovy/transform/Final.java
@@ -60,6 +60,10 @@ import java.lang.annotation.Target;
* would have all parameters marked final (from @AutoFinal), and
* would have all parameters checked against {@code null} (from
@NullCheck).
* </p>
+ * In general, it would be bad style to have an explicit {@code final}
modifier and a {@code @Final} annotation
+ * (or more than one {@code @Final} annotation),
+ * but in that scenario if there is an explicit {@code final} or at least one
enabled {@code @Final},
+ * then the annotated class or member will be final.
*
* @since 4.0.0
*/
@@ -68,4 +72,10 @@ import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR,
ElementType.FIELD})
@GroovyASTTransformationClass("org.codehaus.groovy.transform.FinalASTTransformation")
public @interface Final {
+ /**
+ * When disabled, this annotation effectively becomes a no-op.
+ * Typically only used to override an annotation collector already
containing an enabled {@code @Final} annotation.
+ * Care must be taken when disabling final in this way since the
annotation collector probably had good reason for enabling final.
+ */
+ boolean enabled() default true;
}
diff --git
a/src/main/java/org/codehaus/groovy/transform/FinalASTTransformation.java
b/src/main/java/org/codehaus/groovy/transform/FinalASTTransformation.java
index 5f610b0..65f6ca2 100644
--- a/src/main/java/org/codehaus/groovy/transform/FinalASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/FinalASTTransformation.java
@@ -47,6 +47,7 @@ public class FinalASTTransformation extends
AbstractASTTransformation {
AnnotatedNode candidate = (AnnotatedNode) nodes[1];
AnnotationNode node = (AnnotationNode) nodes[0];
if (!MY_TYPE.equals(node.getClassNode())) return;
+ if (memberHasValue(node, "enabled", false)) return;
if (candidate instanceof ClassNode) {
ClassNode cNode = (ClassNode) candidate;
diff --git a/src/test/org/codehaus/groovy/transform/FinalTransformTest.groovy
b/src/test/org/codehaus/groovy/transform/FinalTransformTest.groovy
index 53db5c7..81ced8f 100644
--- a/src/test/org/codehaus/groovy/transform/FinalTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/FinalTransformTest.groovy
@@ -47,6 +47,38 @@ class FinalTransformTest {
}
@Test
+ void testUsageDirectDisabled() {
+ assertScript """
+ import groovy.transform.*
+ import static java.lang.reflect.Modifier.isFinal
+
+ @Final(enabled=false)
+ class Foo {}
+
+ assert !isFinal(Foo.modifiers)
+ """
+ }
+
+ @Test
+ void testUsageInAnnoationCollectorForClassDisabled() {
+ assertScript """
+ import groovy.transform.*
+ import static java.lang.reflect.Modifier.isFinal
+
+
@AnnotationCollector(mode=AnnotationCollectorMode.PREFER_EXPLICIT_MERGED)
+ @Canonical
+ @Final
+ @interface MyCanonical {}
+
+ @MyCanonical
+ @Final(enabled=false)
+ class Foo {}
+
+ assert !isFinal(Foo.modifiers)
+ """
+ }
+
+ @Test
void testUsageInAnnoationCollectorForMethod() {
assertScript """
import groovy.transform.*