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 2018/03/09 06:39:46 UTC

groovy git commit: GROOVY-8501: Switch internal details of @ImmutableBase existing constructor validation (closes #673)

Repository: groovy
Updated Branches:
  refs/heads/master c8446c770 -> 7379d522b


GROOVY-8501: Switch internal details of @ImmutableBase existing constructor validation (closes #673)


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

Branch: refs/heads/master
Commit: 7379d522b3752a9459d8351fd3d8a970426047b4
Parents: c8446c7
Author: paulk <pa...@asert.com.au>
Authored: Thu Mar 8 23:25:24 2018 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Fri Mar 9 16:39:07 2018 +1000

----------------------------------------------------------------------
 .../groovy/transform/builder/Builder.java       | 11 +++++
 .../transform/builder/DefaultStrategy.java      |  1 +
 .../transform/builder/ExternalStrategy.java     |  1 +
 .../transform/builder/InitializerStrategy.java  | 20 ++++++----
 .../transform/builder/SimpleStrategy.java       |  1 +
 .../groovy/ast/tools/AnnotatedNodeUtils.java    | 42 ++++++++++++++++++++
 .../apache/groovy/ast/tools/ClassNodeUtils.java | 28 +++++++++++++
 .../transform/ImmutableASTTransformation.java   | 19 +--------
 .../MapConstructorASTTransformation.java        | 16 ++++++--
 .../TupleConstructorASTTransformation.java      | 13 ++++--
 .../transform/BuilderTransformTest.groovy       | 20 +++++++++-
 11 files changed, 138 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/main/groovy/groovy/transform/builder/Builder.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/Builder.java b/src/main/groovy/groovy/transform/builder/Builder.java
index 4389dc4..b72777a 100644
--- a/src/main/groovy/groovy/transform/builder/Builder.java
+++ b/src/main/groovy/groovy/transform/builder/Builder.java
@@ -161,4 +161,15 @@ public @interface Builder {
      * @since 2.5.0
      */
     boolean allProperties() default true;
+
+    /**
+     * Whether to always include helper constructors. Currently only supported by InitializerStrategy.
+     * By default, the InitializerStrategy only adds a needed helper tuple constructor if no {@code @TupleConstructor}
+     * annotations are present. If such annotations are present, it is assumed they will provide the helper constructor
+     * that this strategy needs. If made true, the helper constructor will be generated and it is up to you to make sure
+     * this doesn't conflict with any other generated constructors.
+     *
+     * @since 2.5.0
+     */
+    boolean force() default false;
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/main/groovy/groovy/transform/builder/DefaultStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/DefaultStrategy.java b/src/main/groovy/groovy/transform/builder/DefaultStrategy.java
index 65d90e3..b0dfae4 100644
--- a/src/main/groovy/groovy/transform/builder/DefaultStrategy.java
+++ b/src/main/groovy/groovy/transform/builder/DefaultStrategy.java
@@ -166,6 +166,7 @@ public class DefaultStrategy extends BuilderASTTransformation.AbstractBuilderStr
 
     public void build(BuilderASTTransformation transform, AnnotatedNode annotatedNode, AnnotationNode anno) {
         if (unsupportedAttribute(transform, anno, "forClass")) return;
+        if (unsupportedAttribute(transform, anno, "force")) return;
         if (annotatedNode instanceof ClassNode) {
             buildClass(transform, (ClassNode) annotatedNode, anno);
         } else if (annotatedNode instanceof MethodNode) {

http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/main/groovy/groovy/transform/builder/ExternalStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/ExternalStrategy.java b/src/main/groovy/groovy/transform/builder/ExternalStrategy.java
index c482bef..cba3b8b 100644
--- a/src/main/groovy/groovy/transform/builder/ExternalStrategy.java
+++ b/src/main/groovy/groovy/transform/builder/ExternalStrategy.java
@@ -109,6 +109,7 @@ public class ExternalStrategy extends BuilderASTTransformation.AbstractBuilderSt
         if (includes.size() == 1 && Undefined.isUndefined(includes.get(0))) includes = null;
         if (unsupportedAttribute(transform, anno, "builderClassName")) return;
         if (unsupportedAttribute(transform, anno, "builderMethodName")) return;
+        if (unsupportedAttribute(transform, anno, "force")) return;
         boolean allNames = transform.memberHasValue(anno, "allNames", true);
         boolean allProperties = !transform.memberHasValue(anno, "allProperties", false);
         List<PropertyInfo> props = getPropertyInfos(transform, anno, buildee, excludes, includes, allNames, allProperties);

http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/main/groovy/groovy/transform/builder/InitializerStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/InitializerStrategy.java b/src/main/groovy/groovy/transform/builder/InitializerStrategy.java
index e59dac5..1b7e0be 100644
--- a/src/main/groovy/groovy/transform/builder/InitializerStrategy.java
+++ b/src/main/groovy/groovy/transform/builder/InitializerStrategy.java
@@ -18,6 +18,7 @@
  */
 package groovy.transform.builder;
 
+import groovy.transform.TupleConstructor;
 import groovy.transform.Undefined;
 import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
@@ -34,12 +35,12 @@ import org.codehaus.groovy.ast.stmt.BlockStatement;
 import org.codehaus.groovy.classgen.Verifier;
 import org.codehaus.groovy.transform.AbstractASTTransformation;
 import org.codehaus.groovy.transform.BuilderASTTransformation;
-import org.codehaus.groovy.transform.ImmutableASTTransformation;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
+import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.markAsGenerated;
 import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
@@ -129,20 +130,22 @@ public class InitializerStrategy extends BuilderASTTransformation.AbstractBuilde
 
     private static final int PUBLIC_STATIC = ACC_PUBLIC | ACC_STATIC;
     private static final Expression DEFAULT_INITIAL_VALUE = null;
+    private static final ClassNode TUPLECONS_TYPE = ClassHelper.make(TupleConstructor.class);
 
     public void build(BuilderASTTransformation transform, AnnotatedNode annotatedNode, AnnotationNode anno) {
         if (unsupportedAttribute(transform, anno, "forClass")) return;
         if (unsupportedAttribute(transform, anno, "allProperties")) return;
         boolean useSetters = transform.memberHasValue(anno, "useSetters", true);
         boolean allNames = transform.memberHasValue(anno, "allNames", true);
+        boolean force = transform.memberHasValue(anno, "force", true);
         if (annotatedNode instanceof ClassNode) {
-            createBuilderForAnnotatedClass(transform, (ClassNode) annotatedNode, anno, useSetters, allNames);
+            createBuilderForAnnotatedClass(transform, (ClassNode) annotatedNode, anno, useSetters, allNames, force);
         } else if (annotatedNode instanceof MethodNode) {
             createBuilderForAnnotatedMethod(transform, (MethodNode) annotatedNode, anno, useSetters);
         }
     }
 
-    private void createBuilderForAnnotatedClass(BuilderASTTransformation transform, ClassNode buildee, AnnotationNode anno, boolean useSetters, boolean allNames) {
+    private void createBuilderForAnnotatedClass(BuilderASTTransformation transform, ClassNode buildee, AnnotationNode anno, boolean useSetters, boolean allNames, boolean force) {
         List<String> excludes = new ArrayList<String>();
         List<String> includes = new ArrayList<String>();
         includes.add(Undefined.STRING);
@@ -158,7 +161,8 @@ public class InitializerStrategy extends BuilderASTTransformation.AbstractBuilde
         addFields(buildee, filteredFields, builder);
 
         buildCommon(buildee, anno, filteredFields, builder);
-        createBuildeeConstructors(transform, buildee, builder, filteredFields, true, useSetters);
+        boolean needsConstructor = !transform.hasAnnotation(buildee, TUPLECONS_TYPE) || force;
+        createBuildeeConstructors(transform, buildee, builder, filteredFields, needsConstructor, useSetters);
     }
 
     private void createBuilderForAnnotatedMethod(BuilderASTTransformation transform, MethodNode mNode, AnnotationNode anno, boolean useSetters) {
@@ -271,13 +275,13 @@ public class InitializerStrategy extends BuilderASTTransformation.AbstractBuilde
 
     private static void createBuildeeConstructors(BuilderASTTransformation transform, ClassNode buildee, ClassNode builder, List<FieldNode> fields, boolean needsConstructor, boolean useSetters) {
         ConstructorNode initializer = createInitializerConstructor(buildee, builder, fields);
-        if (transform.hasAnnotation(buildee, ImmutableASTTransformation.MY_TYPE)) {
-            initializer.putNodeMetaData(ImmutableASTTransformation.IMMUTABLE_SAFE_FLAG, Boolean.TRUE);
-        } else if (needsConstructor) {
+        markAsGenerated(buildee, initializer);
+        if (needsConstructor) {
             final BlockStatement body = new BlockStatement();
             body.addStatement(ctorSuperS());
             initializeFields(fields, body, useSetters);
-            buildee.addConstructor(ACC_PRIVATE | ACC_SYNTHETIC, getParams(fields, buildee), NO_EXCEPTIONS, body);
+            ConstructorNode helperCons = buildee.addConstructor(ACC_PRIVATE | ACC_SYNTHETIC, getParams(fields, buildee), NO_EXCEPTIONS, body);
+            markAsGenerated(buildee, helperCons);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/main/groovy/groovy/transform/builder/SimpleStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/SimpleStrategy.java b/src/main/groovy/groovy/transform/builder/SimpleStrategy.java
index 7956ac6..fd96b7f 100644
--- a/src/main/groovy/groovy/transform/builder/SimpleStrategy.java
+++ b/src/main/groovy/groovy/transform/builder/SimpleStrategy.java
@@ -94,6 +94,7 @@ public class SimpleStrategy extends BuilderASTTransformation.AbstractBuilderStra
         if (unsupportedAttribute(transform, anno, "forClass")) return;
         if (unsupportedAttribute(transform, anno, "includeSuperProperties")) return;
         if (unsupportedAttribute(transform, anno, "allProperties")) return;
+        if (unsupportedAttribute(transform, anno, "force")) return;
         boolean useSetters = transform.memberHasValue(anno, "useSetters", true);
         boolean allNames = transform.memberHasValue(anno, "allNames", true);
 

http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/main/java/org/apache/groovy/ast/tools/AnnotatedNodeUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/groovy/ast/tools/AnnotatedNodeUtils.java b/src/main/java/org/apache/groovy/ast/tools/AnnotatedNodeUtils.java
new file mode 100644
index 0000000..e378f99
--- /dev/null
+++ b/src/main/java/org/apache/groovy/ast/tools/AnnotatedNodeUtils.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.ast.tools;
+
+import groovy.transform.Generated;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+
+/**
+ * Utility class for working with AnnotatedNodes
+ */
+public class AnnotatedNodeUtils {
+    private static final ClassNode GENERATED_TYPE = ClassHelper.make(Generated.class);
+
+    private AnnotatedNodeUtils() {
+    }
+
+    public static void markAsGenerated(ClassNode cNode, AnnotatedNode aNode) {
+        boolean shouldAnnotate = cNode.getModule() != null && cNode.getModule().getContext() != null;
+        if (shouldAnnotate) {
+            aNode.addAnnotation(new AnnotationNode(GENERATED_TYPE));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
index 6d28afc..f78e99d 100644
--- a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
+++ b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
@@ -18,6 +18,7 @@
  */
 package org.apache.groovy.ast.tools;
 
+import groovy.transform.Generated;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.ConstructorNode;
@@ -28,6 +29,7 @@ import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.MapExpression;
 import org.codehaus.groovy.ast.expr.SpreadExpression;
 import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.transform.AbstractASTTransformation;
 
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
@@ -41,6 +43,8 @@ import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
  * Utility class for working with ClassNodes
  */
 public class ClassNodeUtils {
+    private static final ClassNode GENERATED_TYPE = ClassHelper.make(Generated.class);
+
     /**
      * Formats a type name into a human readable version. For arrays, appends "[]" to the formatted
      * type name of the component. For unit class nodes, uses the class node name.
@@ -281,4 +285,28 @@ public class ClassNodeUtils {
         }
         return false;
     }
+
+    /**
+     * Determine if an explicit (non-generated) constructor is in the class.
+     *
+     * @param xform if non null, add an error if an explicit constructor is found
+     * @param cNode the type of the containing class
+     * @return true if an explicit (non-generated) constructor was found
+     */
+    public static boolean hasExplicitConstructor(AbstractASTTransformation xform, ClassNode cNode) {
+        List<ConstructorNode> declaredConstructors = cNode.getDeclaredConstructors();
+        for (ConstructorNode constructorNode : declaredConstructors) {
+            // allow constructors added by other transforms if flagged as Generated
+            if (AbstractASTTransformation.hasAnnotation(constructorNode, GENERATED_TYPE)) {
+                continue;
+            }
+            if (xform != null) {
+                xform.addError("Error during " + xform.getAnnotationName() +
+                        " processing. Explicit constructors not allowed for class: " +
+                        cNode.getNameWithoutPackage(), constructorNode);
+            }
+            return true;
+        }
+        return false;
+    }
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
index 1cd79c6..b8cc56c 100644
--- a/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
@@ -30,7 +30,6 @@ import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.ConstructorNode;
 import org.codehaus.groovy.ast.FieldNode;
 import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.PropertyNode;
@@ -52,6 +51,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static org.apache.groovy.ast.tools.ClassNodeUtils.hasExplicitConstructor;
 import static org.apache.groovy.ast.tools.ImmutablePropertyUtils.builtinOrMarkedImmutableClass;
 import static org.apache.groovy.ast.tools.ImmutablePropertyUtils.createErrorMessage;
 import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
@@ -94,7 +94,6 @@ public class ImmutableASTTransformation extends AbstractASTTransformation implem
     private static final String COPY_WITH_METHOD = "copyWith";
 
     private static final ClassNode HMAP_TYPE = makeWithoutCaching(HashMap.class, false);
-    public static final String IMMUTABLE_SAFE_FLAG = "Immutable.Safe";
 
     @Override
     public String getAnnotationName() {
@@ -147,7 +146,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation implem
 //            if (unsupportedTupleAttribute(tupleCons, "useSetters")) return;
             if (unsupportedTupleAttribute(tupleCons, "force")) return;
         }
-        if (!validateConstructors(cNode)) return;
+        if (hasExplicitConstructor(this, cNode)) return;
         if (memberHasValue(node, MEMBER_ADD_COPY_WITH, true) && !pList.isEmpty() &&
                 !hasDeclaredMethod(cNode, COPY_WITH_METHOD, 1)) {
             createCopyWith(cNode, pList);
@@ -210,20 +209,6 @@ public class ImmutableASTTransformation extends AbstractASTTransformation implem
         cNode.addField(fn);
     }
 
-    private boolean validateConstructors(ClassNode cNode) {
-        List<ConstructorNode> declaredConstructors = cNode.getDeclaredConstructors();
-        for (ConstructorNode constructorNode : declaredConstructors) {
-            // allow constructors added by other transforms if flagged as safe
-            Object nodeMetaData = constructorNode.getNodeMetaData(IMMUTABLE_SAFE_FLAG);
-            if (nodeMetaData != null && ((Boolean) nodeMetaData)) {
-                continue;
-            }
-            addError("Explicit constructors not allowed for " + MY_TYPE_NAME + " class: " + cNode.getNameWithoutPackage(), constructorNode);
-            return false;
-        }
-        return true;
-    }
-
     static boolean makeImmutable(ClassNode cNode) {
         List<AnnotationNode> annotations = cNode.getAnnotations(ImmutablePropertyUtils.IMMUTABLE_OPTIONS_TYPE);
         AnnotationNode annoImmutable = annotations.isEmpty() ? null : annotations.get(0);

http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
index 51de450..a1aa094 100644
--- a/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
@@ -45,11 +45,13 @@ import org.codehaus.groovy.control.SourceUnit;
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.markAsGenerated;
 import static org.apache.groovy.ast.tools.ClassNodeUtils.hasNoArgConstructor;
 import static org.apache.groovy.ast.tools.VisibilityUtils.getVisibility;
 import static org.codehaus.groovy.ast.ClassHelper.make;
@@ -138,10 +140,13 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation i
                                            boolean includeSuperProperties, boolean includeSuperFields, boolean noArg,
                                            boolean allNames, boolean allProperties, boolean specialNamedArgHandling, boolean includeStatic,
                                            List<String> excludes, List<String> includes, ClosureExpression pre, ClosureExpression post, SourceUnit source) {
-        List<ConstructorNode> constructors = cNode.getDeclaredConstructors();
-        boolean foundEmpty = constructors.size() == 1 && constructors.get(0).getFirstStatement() == null;
+
         // HACK: JavaStubGenerator could have snuck in a constructor we don't want
-        if (foundEmpty) constructors.remove(0);
+        Iterator<ConstructorNode> iterator = cNode.getDeclaredConstructors().iterator();
+        while (iterator.hasNext()) {
+            ConstructorNode next = iterator.next();
+            if (next.getFirstStatement() == null) iterator.remove();
+        }
 
         Set<String> names = new HashSet<String>();
         List<PropertyNode> superList;
@@ -182,6 +187,7 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation i
     }
 
     private static void doAddConstructor(final ClassNode cNode, final ConstructorNode constructorNode) {
+        markAsGenerated(cNode, constructorNode);
         cNode.addConstructor(constructorNode);
         // GROOVY-5814: Immutable is not compatible with @CompileStatic
         Parameter argsParam = null;
@@ -224,7 +230,9 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation i
 
     private static void createNoArgConstructor(ClassNode cNode, int modifiers) {
         Statement body = stmt(ctorX(ClassNode.THIS, args(new MapExpression())));
-        cNode.addConstructor(new ConstructorNode(modifiers, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body));
+        ConstructorNode consNode = new ConstructorNode(modifiers, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
+        markAsGenerated(cNode, consNode);
+        cNode.addConstructor(consNode);
     }
 
     private static ClassCodeExpressionTransformer makeMapTypedArgsTransformer() {

http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
index 9696d15..0d10cac 100644
--- a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
@@ -55,6 +55,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.markAsGenerated;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.hasExplicitConstructor;
 import static org.apache.groovy.ast.tools.VisibilityUtils.getVisibility;
 import static org.codehaus.groovy.ast.ClassHelper.make;
 import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
@@ -168,7 +170,6 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
         boolean callSuper = xform.memberHasValue(anno, "callSuper", true);
         boolean force = xform.memberHasValue(anno, "force", true);
         boolean defaults = !xform.memberHasValue(anno, "defaults", false);
-        boolean useSetters = xform.memberHasValue(anno, "useSetters", true);
         Set<String> names = new HashSet<String>();
         List<PropertyNode> superList;
         if (includeSuperProperties || includeSuperFields) {
@@ -177,14 +178,14 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
             superList = new ArrayList<PropertyNode>();
         }
 
-        List<PropertyNode> list = getAllProperties(names, cNode, true, includeFields, false, allProperties, false, true);
+        List<PropertyNode> list = getAllProperties(names, cNode, includeProperties, includeFields, false, allProperties, false, true);
 
         boolean makeImmutable = makeImmutable(cNode);
         boolean specialNamedArgCase = (ImmutableASTTransformation.isSpecialNamedArgCase(list, !defaults) && superList.isEmpty()) ||
                 (ImmutableASTTransformation.isSpecialNamedArgCase(superList, !defaults) && list.isEmpty());
 
         // no processing if existing constructors found unless forced or ImmutableBase in play
-        if (!cNode.getDeclaredConstructors().isEmpty() && !force && !makeImmutable) return;
+        if (hasExplicitConstructor(null, cNode) && !force && !makeImmutable) return;
 
         final List<Parameter> params = new ArrayList<Parameter>();
         final List<Expression> superParams = new ArrayList<Expression>();
@@ -254,7 +255,9 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
 
         boolean hasMapCons = hasAnnotation(cNode, MapConstructorASTTransformation.MY_TYPE);
         int modifiers = getVisibility(anno, cNode, ConstructorNode.class, ACC_PUBLIC);
-        cNode.addConstructor(new ConstructorNode(modifiers, params.toArray(new Parameter[params.size()]), ClassNode.EMPTY_ARRAY, body));
+        ConstructorNode consNode = new ConstructorNode(modifiers, params.toArray(new Parameter[params.size()]), ClassNode.EMPTY_ARRAY, body);
+        markAsGenerated(cNode, consNode);
+        cNode.addConstructor(consNode);
 
         if (sourceUnit != null && !body.isEmpty()) {
             VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(sourceUnit);
@@ -305,12 +308,14 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
                 illegalArgumentBlock(message),
                 processArgsBlock(cNode, namedArgs)));
         ConstructorNode init = new ConstructorNode(modifiers, parameters, ClassNode.EMPTY_ARRAY, code);
+        markAsGenerated(cNode, init);
         cNode.addConstructor(init);
         // potentially add a no-arg constructor too
         if (addNoArg) {
             code = new BlockStatement();
             code.addStatement(stmt(ctorX(ClassNode.THIS, ctorX(LHMAP_TYPE))));
             init = new ConstructorNode(modifiers, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, code);
+            markAsGenerated(cNode, init);
             cNode.addConstructor(init);
         }
     }

http://git-wip-us.apache.org/repos/asf/groovy/blob/7379d522/src/test/org/codehaus/groovy/transform/BuilderTransformTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/org/codehaus/groovy/transform/BuilderTransformTest.groovy b/src/test/org/codehaus/groovy/transform/BuilderTransformTest.groovy
index 1febfbc..9894752 100644
--- a/src/test/org/codehaus/groovy/transform/BuilderTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/BuilderTransformTest.groovy
@@ -603,8 +603,26 @@ class BuilderTransformTest extends CompilableTestSupport {
             import groovy.transform.builder.*
             import groovy.transform.*
 
+            @Canonical(useSetters=true)
+            @Builder(builderStrategy=InitializerStrategy)
+            class Person {
+                String name
+                void setName(String name) { this.name = name?.toUpperCase() }
+            }
+
+            @CompileStatic
+            def make() {
+                assert new Person(Person.createInitializer().name("John")).toString() == 'Person(JOHN)'
+            }
+            make()
+        '''
+        assertScript '''
+            import groovy.transform.builder.*
+            import groovy.transform.*
+
             @Canonical
-            @Builder(builderStrategy=InitializerStrategy, useSetters=true)
+            @TupleConstructor(includes='')
+            @Builder(builderStrategy=InitializerStrategy, useSetters=true, force=true)
             class Person {
                 String name
                 void setName(String name) { this.name = name?.toUpperCase() }