You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2017/10/08 13:43:51 UTC

groovy git commit: GROOVY-8347: @AutoFinal: Added support to auto-finalize closure parameters (support more targets and inner classes)

Repository: groovy
Updated Branches:
  refs/heads/master 8a7fe24f6 -> 0052a8102


GROOVY-8347: @AutoFinal: Added support to auto-finalize closure parameters (support more targets and inner classes)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/0052a810
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/0052a810
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/0052a810

Branch: refs/heads/master
Commit: 0052a81026ffd120fef92e755e8b874b2e22835a
Parents: 8a7fe24
Author: paulk <pa...@asert.com.au>
Authored: Sun Oct 8 23:43:39 2017 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Sun Oct 8 23:43:39 2017 +1000

----------------------------------------------------------------------
 src/main/groovy/transform/AutoFinal.java        |  18 +--
 .../transform/AutoFinalASTTransformation.java   | 140 ++++++++++++++-----
 2 files changed, 114 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/0052a810/src/main/groovy/transform/AutoFinal.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/AutoFinal.java b/src/main/groovy/transform/AutoFinal.java
index 8fb5310..1cc0ab6 100644
--- a/src/main/groovy/transform/AutoFinal.java
+++ b/src/main/groovy/transform/AutoFinal.java
@@ -30,14 +30,16 @@ import java.lang.annotation.Target;
  * Annotation to automatically add the final qualifier to method, constructor,
  * and closure parameters.
  * <p>The annotation can be placed at the class level in which case it applies to
- * all methods, constructors, and closures within the class, or on individual
- * methods or constructors.
- * <p>In general it will make the most sense to automatically apply the
- * annotation to all classes of a project
- * (groovyc --configscript; google "Customising The Groovy Compiler", or see {@link CompilerConfiguration}
- * so that one can be sure that all arguments will automatically be final,
+ * all methods, constructors, and closures within the class. It can also be applied
+ * to an individual method, constructor, field with a Closure initial value
+ * or a Closure assigned to a local variable.
+ * <p>If you wish to automatically apply the
+ * annotation to all classes of a project, consider using
+ * {@code groovyc --configscript}. Google "Customising The Groovy Compiler",
+ * or see {@link CompilerConfiguration} for further details.
+ * This will ensure that all arguments will automatically be final,
  * completely eliminating the need to clutter the code with final keywords
- * in any paramete list.
+ * in any parameter list.
  * <p>
  * <em>Example usage:</em>
  * <pre class="groovyTestCase">
@@ -80,7 +82,7 @@ import java.lang.annotation.Target;
  */
 @java.lang.annotation.Documented
 @Retention(RetentionPolicy.SOURCE)
-@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE})
 @GroovyASTTransformationClass("org.codehaus.groovy.transform.AutoFinalASTTransformation")
 public @interface AutoFinal {
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/0052a810/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java b/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
index 20e8d51..4055f72 100644
--- a/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
@@ -19,12 +19,23 @@
 package org.codehaus.groovy.transform;
 
 import groovy.transform.AutoFinal;
-import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
 import org.codehaus.groovy.control.CompilePhase;
 import org.codehaus.groovy.control.SourceUnit;
 
 import java.lang.reflect.Modifier;
+import java.util.Iterator;
 
 import static org.codehaus.groovy.ast.ClassHelper.make;
 
@@ -37,72 +48,129 @@ public class AutoFinalASTTransformation extends AbstractASTTransformation {
     private static final Class MY_CLASS = AutoFinal.class;
     private static final ClassNode MY_TYPE = make(MY_CLASS);
     private static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage();
+    private AnnotatedNode candidate;
+
 
     public void visit(ASTNode[] nodes, SourceUnit source) {
         init(nodes, source);
-        processClassesConstructorsMethods(nodes, source);
-        processClosures(nodes, source);
+        final ClassCodeVisitorSupport visitor = createVisitor();
+        process(nodes, visitor);
     }
 
-    private void processClassesConstructorsMethods(ASTNode[] nodes, final SourceUnit unit) {
-        AnnotatedNode candidate = (AnnotatedNode) nodes[1];
+    private ClassCodeVisitorSupport createVisitor() {
+        return new ClassCodeVisitorSupport() {
+            @Override
+            public void visitClosureExpression(ClosureExpression expression) {
+                if (expression.isSynthetic()) {
+                    return;
+                }
+                Parameter[] origParams = expression.getParameters();
+                for (Parameter p : origParams) {
+                    p.setModifiers(p.getModifiers() | Modifier.FINAL);
+                }
+                super.visitClosureExpression(expression);
+            }
+
+            @Override
+            protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+                if (hasNoExplicitAutoFinal(node) || candidate == node) {
+                    super.visitConstructorOrMethod(node, isConstructor);
+                }
+            }
+
+            @Override
+            public void visitField(FieldNode node) {
+                if (hasNoExplicitAutoFinal(node) || candidate == node) {
+                    super.visitField(node);
+                }
+            }
+
+            @Override
+            public void visitDeclarationExpression(DeclarationExpression expr) {
+                if (hasNoExplicitAutoFinal(expr) || candidate == expr) {
+                    super.visitDeclarationExpression(expr);
+                }
+            }
+
+            protected SourceUnit getSourceUnit() {
+                return sourceUnit;
+            }
+        };
+    }
+
+    private void process(ASTNode[] nodes, final ClassCodeVisitorSupport visitor) {
+        candidate = (AnnotatedNode) nodes[1];
         AnnotationNode node = (AnnotationNode) nodes[0];
         if (!MY_TYPE.equals(node.getClassNode())) return;
 
         if (candidate instanceof ClassNode) {
-            processClass((ClassNode) candidate);
+            ClassNode cNode = (ClassNode) candidate;
+            processClass(cNode, visitor);
         } else if (candidate instanceof MethodNode) {
             // handles constructors and methods
-            processConstructorOrMethod((MethodNode) candidate);
+            MethodNode mNode = (MethodNode) candidate;
+            processConstructorOrMethod(mNode, visitor);
+        } else if (candidate instanceof FieldNode) {
+            FieldNode fNode = (FieldNode) candidate;
+            processField(fNode, visitor);
+        } else if (candidate instanceof DeclarationExpression) {
+            DeclarationExpression de = (DeclarationExpression) candidate;
+            processLocalVariable(de, visitor);
         }
     }
 
-    private void processClosures(ASTNode[] nodes, final SourceUnit source) {
-        final ASTNode node = nodes[1];
-        if(node instanceof ClassNode) {
-            ClassNode annotatedClass = (ClassNode) node;
-
-            final ClassCodeVisitorSupport visitor = new ClassCodeVisitorSupport() {
-                @Override
-                public void visitClosureExpression(ClosureExpression expression) {
-                    if (expression.isSynthetic()) { return; }
-                    Parameter[] origParams = expression.getParameters();
-                    for (Parameter p : origParams) {
-                        p.setModifiers(p.getModifiers() | Modifier.FINAL);
-                    }
-                    super.visitClosureExpression(expression);
-                }
-
-                protected SourceUnit getSourceUnit() {
-                    return source;
-                }
-            };
-
-            visitor.visitClass(annotatedClass);
+    private void processLocalVariable(DeclarationExpression de, ClassCodeVisitorSupport visitor) {
+        if (de.getRightExpression() instanceof ClosureExpression) {
+            visitor.visitDeclarationExpression(de);
         }
     }
 
+    private void processField(FieldNode fNode, ClassCodeVisitorSupport visitor) {
+        if (fNode.hasInitialExpression() && fNode.getInitialExpression() instanceof ClosureExpression) {
+            visitor.visitField(fNode);
+        }
+    }
 
-
-    private void processClass(ClassNode cNode) {
+    private void processClass(ClassNode cNode, final ClassCodeVisitorSupport visitor) {
         if (cNode.isInterface()) {
             addError("Error processing interface '" + cNode.getName() +
                     "'. " + MY_TYPE_NAME + " only allowed for classes.", cNode);
             return;
         }
+
         for (ConstructorNode cn : cNode.getDeclaredConstructors()) {
-            processConstructorOrMethod(cn);
+            if (hasNoExplicitAutoFinal(cn)) {
+                processConstructorOrMethod(cn, visitor);
+            }
         }
+
         for (MethodNode mn : cNode.getAllDeclaredMethods()) {
-            processConstructorOrMethod(mn);
+            if (hasNoExplicitAutoFinal(mn)) {
+                processConstructorOrMethod(mn, visitor);
+            }
+        }
+
+        Iterator<InnerClassNode> it = cNode.getInnerClasses();
+        while (it.hasNext()) {
+            InnerClassNode in = it.next();
+            if (in.getAnnotations(MY_TYPE).isEmpty()) {
+                processClass(in, visitor);
+            }
         }
+
+        visitor.visitClass(cNode);
+    }
+
+    private boolean hasNoExplicitAutoFinal(AnnotatedNode node) {
+        return node.getAnnotations(MY_TYPE).isEmpty();
     }
 
-    private void processConstructorOrMethod(MethodNode node) {
-        if (node.isSynthetic()) return;
-        Parameter[] origParams = node.getParameters();
+    private void processConstructorOrMethod(MethodNode mNode, ClassCodeVisitorSupport visitor) {
+        if (mNode.isSynthetic()) return;
+        Parameter[] origParams = mNode.getParameters();
         for (Parameter p : origParams) {
             p.setModifiers(p.getModifiers() | Modifier.FINAL);
         }
+        visitor.visitMethod(mNode);
     }
 }