From 849aa4fddadb40439a30967131d2847b90fb139f Mon Sep 17 00:00:00 2001
From: Jan Rieke <it@janrieke.de>
Date: Sun, 9 Sep 2018 19:47:51 +0200
Subject: SuperBuilder toBuilder; test cases

---
 src/core/lombok/experimental/SuperBuilder.java | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/experimental/SuperBuilder.java b/src/core/lombok/experimental/SuperBuilder.java
index 26127a5f..be6ea304 100644
--- a/src/core/lombok/experimental/SuperBuilder.java
+++ b/src/core/lombok/experimental/SuperBuilder.java
@@ -1,16 +1,16 @@
 /*
  * Copyright (C) 2018 The Project Lombok Authors.
- * 
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -43,7 +43,7 @@ import lombok.Singular;
  * The builder also has a <code>build()</code> method which returns a completed instance of the original type.
  * <p>
  * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/SuperBuilder">the project lombok features page for &#64;SuperBuilder</a>.
- * 
+ *
  * @see Singular
  */
 @Target(TYPE)
@@ -51,16 +51,15 @@ import lombok.Singular;
 public @interface SuperBuilder {
 	/** @return Name of the method that creates a new builder instance. Default: {@code builder}. */
 	String builderMethodName() default "builder";
-	
+
 	/** @return Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
 	String buildMethodName() default "build";
-	
-	// toBuilder also requires a two-stage system where each class gets its own toBuilder but calls on a second method (and also calls parentclass's method)
-	// to fill the builder, as this class does not know what fields to pass on to the builder. Let's consider this, but only for milestone 2.
-	/*
-	 * If true, generate an instance method to obtain a builder that is initialized with the values of this instance.
-	 * 
+
+	/**
+	 * If <code>true</code>, generate an instance method to obtain a builder that is initialized with the values of this instance.
+	 * In this case, all superclasses must also have <code>toBuilder=true</code>.
+	 *
 	 * @return Whether to generate a {@code toBuilder()} method.
 	 */
-//	boolean toBuilder() default false;
+	boolean toBuilder() default false;
 }
-- 
cgit 


From b42ce5325afbe6202d5d5815a9c74dd64a0ce3de Mon Sep 17 00:00:00 2001
From: Jan Rieke <it@janrieke.de>
Date: Tue, 11 Sep 2018 10:32:50 +0200
Subject: SuperBuilder: generate toBuilder method (javac)

---
 src/core/lombok/javac/handlers/HandleBuilder.java  |  2 +-
 .../lombok/javac/handlers/HandleSuperBuilder.java  | 67 +++++++++++++++++-----
 .../after-delombok/SuperBuilderBasicToBuilder.java |  2 -
 .../after-ecj/SuperBuilderBasicToBuilder.java      |  4 --
 4 files changed, 53 insertions(+), 22 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index 63697691..91cd4abb 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -84,7 +84,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
 		return ((Boolean) expr).booleanValue();
 	}
 	
-	private static class BuilderFieldData {
+	public static class BuilderFieldData {
 		JCExpression type;
 		Name rawName;
 		Name name;
diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index b8f572d5..62dc93c1 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -68,6 +68,7 @@ import lombok.javac.Javac;
 import lombok.javac.JavacAnnotationHandler;
 import lombok.javac.JavacNode;
 import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.HandleBuilder.BuilderFieldData;
 import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
 import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker;
 import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker;
@@ -78,21 +79,9 @@ import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
 @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
 public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 	private static final String SELF_METHOD = "self";
-	
-	private static class BuilderFieldData {
-		JCExpression type;
-		Name rawName;
-		Name name;
-		Name nameOfDefaultProvider;
-		Name nameOfSetFlag;
-		SingularData singularData;
-		ObtainVia obtainVia;
-		JavacNode obtainViaNode;
-		JavacNode originalFieldNode;
-		
-		java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>();
-	}
-	
+	private static final String TO_BUILDER_METHOD_NAME = "toBuilder";
+	private static final String FILL_VALUES_METHOD_NAME = "$fillValuesFrom";
+
 	@Override
 	public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast, JavacNode annotationNode) {
 		handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder");
@@ -109,6 +98,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
 		if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
 		
+		boolean toBuilder = superbuilderAnnotation.toBuilder();
+
 		JavacNode tdParent = annotationNode.up();
 		
 		java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
@@ -307,6 +298,19 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 				if (builderMethod != null) injectMethod(tdParent, builderMethod);
 			}
 		}
+
+		if (toBuilder) {
+			switch (methodExists(TO_BUILDER_METHOD_NAME, tdParent, 0)) {
+			case EXISTS_BY_USER:
+				annotationNode.addWarning("Not generating toBuilder() as it already exists.");
+				return;
+			case NOT_EXISTS:
+				JCMethodDecl md = generateToBuilderMethod(builderClassName, builderImplClassName, annotationNode, tdParent, typeParams);
+				if (md != null) {
+					injectMethod(tdParent, md);
+				}
+			}
+		}
 	}
 	
 	/**
@@ -495,6 +499,39 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
 	}
 	
+	/**
+	 * Generates a toBuilder() method that looks like this:
+	 * <pre>
+	 * public ParentBuilder&lt;?, ?&gt; toBuilder() {
+	 *     return new <i>Foobar</i>BuilderImpl().$fillValuesFrom(this);
+	 * }
+	 * </pre>
+	 */
+	private JCMethodDecl generateToBuilderMethod(String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) {
+		JavacTreeMaker maker = type.getTreeMaker();
+		
+		ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
+		for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name));
+		
+		JCExpression newClass = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.<JCExpression>nil(), null);
+		JCMethodInvocation invokeFillMethod = maker.Apply(List.<JCExpression>nil(), maker.Select(newClass, type.toName(FILL_VALUES_METHOD_NAME)), List.<JCExpression>nil());
+		JCStatement statement = maker.Return(invokeFillMethod);
+		
+		JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+		int modifiers = Flags.PUBLIC;
+		
+		// Add any type params of the annotated class to the return type.
+		ListBuffer<JCExpression> typeParameterNames = new ListBuffer<JCExpression>();
+		typeParameterNames.addAll(typeParameterNames(maker, typeParams));
+		// Now add the <?, ?>.
+		JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+		typeParameterNames.add(wildcard);
+		typeParameterNames.add(wildcard);
+		JCTypeApply returnType = maker.TypeApply(maker.Ident(type.toName(builderClassName)), typeParameterNames.toList());
+		
+		return maker.MethodDef(maker.Modifiers(modifiers), type.toName(TO_BUILDER_METHOD_NAME), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+	}
+	
 	private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) {
 		JavacTreeMaker maker = type.getTreeMaker();
 		List<JCAnnotation> annotations = List.nil();
diff --git a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
index 2e097c1a..ae3f5f31 100644
--- a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
@@ -1,7 +1,5 @@
 import java.util.List;
 
-import manual.model.Superclass.SuperclassBuilder;
-import manual.model.Superclass.SuperclassBuilderImpl;
 public class SuperBuilderBasicToBuilder {
 	public static class Parent {
 		int field1;
diff --git a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
index 03fc86d1..a2299a9c 100644
--- a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
@@ -1,9 +1,5 @@
 import java.util.List;
 
-import SuperBuilderBasicToBuilder.Child.ChildBuilder;
-import SuperBuilderBasicToBuilder.Child.ChildBuilderImpl;
-import SuperBuilderBasicToBuilder.Parent.ParentBuilder;
-import SuperBuilderBasicToBuilder.Parent.ParentBuilderImpl;
 public class SuperBuilderBasic {
   public static @lombok.experimental.SuperBuilder class Parent {
     public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
-- 
cgit 


From abb0d2374e0ca6cda55c5ea63f51235111701081 Mon Sep 17 00:00:00 2001
From: Jan Rieke <it@janrieke.de>
Date: Tue, 11 Sep 2018 14:52:15 +0200
Subject: SuperBuilder: generate fillValuesFrom method (javac)

---
 .../lombok/javac/handlers/HandleSuperBuilder.java  | 95 +++++++++++++++++++++-
 .../after-delombok/SuperBuilderBasicToBuilder.java |  5 +-
 .../after-ecj/SuperBuilderBasicToBuilder.java      |  2 +-
 3 files changed, 95 insertions(+), 7 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index 62dc93c1..01ac2794 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -37,11 +37,13 @@ import com.sun.tools.javac.tree.JCTree.JCAssign;
 import com.sun.tools.javac.tree.JCTree.JCBlock;
 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
 import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
 import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
 import com.sun.tools.javac.tree.JCTree.JCIdent;
 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
 import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCReturn;
 import com.sun.tools.javac.tree.JCTree.JCStatement;
 import com.sun.tools.javac.tree.JCTree.JCTypeApply;
 import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
@@ -234,6 +236,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 			injectFieldAndMarkGenerated(builderType, uncleanField);
 		}
 		
+		// Generate $fillValuesFrom() method in the abstract builder.
+		injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderFields));
+
 		// Generate abstract self() and build() methods in the abstract builder.
 		injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName));
 		injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassExpression != null, classGenericName));
@@ -500,7 +505,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 	}
 	
 	/**
-	 * Generates a toBuilder() method that looks like this:
+	 * Generates a <code>toBuilder()</code> method in the annotated class that looks like this:
 	 * <pre>
 	 * public ParentBuilder&lt;?, ?&gt; toBuilder() {
 	 *     return new <i>Foobar</i>BuilderImpl().$fillValuesFrom(this);
@@ -514,7 +519,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name));
 		
 		JCExpression newClass = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.<JCExpression>nil(), null);
-		JCMethodInvocation invokeFillMethod = maker.Apply(List.<JCExpression>nil(), maker.Select(newClass, type.toName(FILL_VALUES_METHOD_NAME)), List.<JCExpression>nil());
+		List<JCExpression> methodArgs = List.<JCExpression>of(maker.Ident(type.toName("this")));
+		JCMethodInvocation invokeFillMethod = maker.Apply(List.<JCExpression>nil(), maker.Select(newClass, type.toName(FILL_VALUES_METHOD_NAME)), methodArgs);
+//		JCMethodInvocation invokeFillMethod = maker.Apply(List.<JCExpression>nil(), maker.Select(newClass, type.toName(FILL_VALUES_METHOD_NAME)), List.<JCExpression>nil());
 		JCStatement statement = maker.Return(invokeFillMethod);
 		
 		JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
@@ -531,6 +538,88 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		
 		return maker.MethodDef(maker.Modifiers(modifiers), type.toName(TO_BUILDER_METHOD_NAME), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
 	}
+
+	/**
+	 * Generates a <code>$fillValuesFrom()</code> method in the abstract builder class that looks
+	 * like this:
+	 * <pre>
+	 * protected B $fillValuesFrom(final C instance) {
+	 *     super.$fillValuesFrom(instance);
+	 *     this.field(instance.field);
+	 *     return self();
+	 * }
+	 * </pre>
+	 */
+	private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited, String builderGenericName, String classGenericName, java.util.List<BuilderFieldData> builderFields) {
+		JavacTreeMaker maker = type.getTreeMaker();
+		List<JCAnnotation> annotations = List.nil();
+		if (inherited) {
+			JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil());
+			annotations = List.of(overrideAnnotation);
+		}
+		JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED, annotations);
+		Name name = type.toName(FILL_VALUES_METHOD_NAME);
+		JCExpression returnType = maker.Ident(type.toName(builderGenericName));
+		
+		final String instanceVariableName = "instance";
+		JCExpression classGenericNameExpr = maker.Ident(type.toName(classGenericName));
+		JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(instanceVariableName), classGenericNameExpr, null);
+
+		ListBuffer<JCStatement> body = new ListBuffer<JCStatement>();
+		
+		if (inherited) {
+			// Call super.
+			JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
+					maker.Select(maker.Ident(type.toName("super")), name),
+					List.<JCExpression>of(maker.Ident(type.toName(instanceVariableName))));
+			body.append(maker.Exec(callToSuper));
+		}
+		
+		// Call the builder's setter methods to fill the values from the instance.
+		for (BuilderFieldData bfd : builderFields) {
+			JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, instanceVariableName, type, maker);
+			body.append(exec);
+		}
+		
+		JCReturn returnStatement = maker.Return(maker.Apply(List.<JCExpression>nil(), maker.Ident(type.toName(SELF_METHOD)), List.<JCExpression>nil()));
+		body.append(returnStatement);
+		JCBlock bodyBlock = maker.Block(0, body.toList());
+
+		return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.of(param), List.<JCExpression>nil(), bodyBlock, null);
+	}
+
+	private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData bfd, final String instanceVariableName, JavacNode type, JavacTreeMaker maker) {
+		JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2];
+		if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
+			for (int i = 0; i < tgt.length; i++) {
+				tgt[i] = maker.Select(maker.Ident(type.toName(instanceVariableName)), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field()));
+			}
+		} else {
+			if (bfd.obtainVia.isStatic()) {
+				for (int i = 0; i < tgt.length; i++) {
+					JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method()));
+					tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName(instanceVariableName))));
+				}
+			} else {
+				for (int i = 0; i < tgt.length; i++) {
+					JCExpression c = maker.Select(maker.Ident(type.toName(instanceVariableName)), type.toName(bfd.obtainVia.method()));
+					tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil());
+				}
+			}
+		}
+		
+		JCExpression arg;
+		if (bfd.singularData == null) {
+			arg = tgt[0];
+		} else {
+			JCExpression eqNull = maker.Binary(CTC_EQUAL, tgt[0], maker.Literal(CTC_BOT, null));
+			JCExpression emptyList = maker.Apply(List.<JCExpression>nil(), chainDots(type, "java", "util", "Collections", "emptyList"), List.<JCExpression>nil());
+			arg = maker.Conditional(eqNull, emptyList, tgt[1]);
+		}
+		JCMethodInvocation apply = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName("this")), bfd.name), List.of(arg));
+		JCExpressionStatement exec = maker.Exec(apply);
+		return exec;
+	}
 	
 	private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) {
 		JavacTreeMaker maker = type.getTreeMaker();
@@ -542,7 +631,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations);
 		Name name = type.toName(SELF_METHOD);
 		JCExpression returnType = maker.Ident(type.toName(builderGenericName));
-		
+
 		return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null);
 	}
 	
diff --git a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
index ae3f5f31..404984b9 100644
--- a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
@@ -1,5 +1,4 @@
 import java.util.List;
-
 public class SuperBuilderBasicToBuilder {
 	public static class Parent {
 		int field1;
@@ -13,7 +12,7 @@ public class SuperBuilderBasicToBuilder {
 			@java.lang.SuppressWarnings("all")
 			protected B $fillValuesFrom(final C instance) {
 				this.field1(instance.field1);
-				this.items(instance.items);
+				this.items(instance.items == null ? java.util.Collections.emptyList() : instance.items);
 				return self();
 			}
 			@java.lang.SuppressWarnings("all")
@@ -97,7 +96,7 @@ public class SuperBuilderBasicToBuilder {
 			private double field3;
 			@java.lang.Override
 			@java.lang.SuppressWarnings("all")
-			protected B $fillValuesFrom(C instance) {
+			protected B $fillValuesFrom(final C instance) {
 				super.$fillValuesFrom(instance);
 				this.field3(instance.field3);
 				return self();
diff --git a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
index a2299a9c..5e5f9760 100644
--- a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
@@ -10,7 +10,7 @@ public class SuperBuilderBasic {
       }
       protected @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
         this.field1(instance.field1);
-        this.items(instance.items);
+        this.items(instance.items == null ? java.util.Collections.emptyList() : instance.items);
         return self();
       }
       protected abstract @java.lang.SuppressWarnings("all") B self();
-- 
cgit 


From 1f3688fdba92508e091eab39b6131dbca2a8ef83 Mon Sep 17 00:00:00 2001
From: Jan Rieke <it@janrieke.de>
Date: Tue, 11 Sep 2018 16:36:25 +0200
Subject: SuperBuilder: generate fillValuesFrom method (ecj)

---
 .../lombok/eclipse/handlers/HandleBuilder.java     |   2 +-
 .../eclipse/handlers/HandleSuperBuilder.java       | 127 ++++++++++++++++++---
 .../lombok/javac/handlers/HandleSuperBuilder.java  |   6 +-
 .../after-ecj/SuperBuilderBasicToBuilder.java      |  16 +--
 4 files changed, 121 insertions(+), 30 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index 9a069c58..5d417bde 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -105,7 +105,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
 		return ((Boolean) expr).booleanValue();
 	}
 	
-	private static class BuilderFieldData {
+	public static class BuilderFieldData {
 		TypeReference type;
 		char[] rawName;
 		char[] name;
diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index 3c07ac55..6c1b2cac 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -39,7 +39,9 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation;
 import org.eclipse.jdt.internal.compiler.ast.Argument;
 import org.eclipse.jdt.internal.compiler.ast.Assignment;
 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
 import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
 import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
 import org.eclipse.jdt.internal.compiler.ast.Expression;
 import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
@@ -48,6 +50,8 @@ import org.eclipse.jdt.internal.compiler.ast.FieldReference;
 import org.eclipse.jdt.internal.compiler.ast.IfStatement;
 import org.eclipse.jdt.internal.compiler.ast.MessageSend;
 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
 import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
@@ -56,6 +60,7 @@ import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
 import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
 import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.SuperReference;
 import org.eclipse.jdt.internal.compiler.ast.ThisReference;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
@@ -87,6 +92,7 @@ import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
 import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
 import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker;
 import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker;
+import lombok.eclipse.handlers.HandleBuilder.BuilderFieldData;
 import lombok.experimental.NonFinal;
 import lombok.experimental.SuperBuilder;
 
@@ -98,40 +104,31 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 	private static final char[] DEFAULT_PREFIX = "$default$".toCharArray();
 	private static final char[] SET_PREFIX = "$set".toCharArray();
 	private static final char[] SELF_METHOD_NAME = "self".toCharArray();
+	private static final char[] TO_BUILDER_METHOD_NAME = "toBuilder".toCharArray();
+	private static final char[] FILL_VALUES_METHOD_NAME = "$fillValuesFrom".toCharArray();
+	private static final char[] EMPTY_LIST = "emptyList".toCharArray();
 	
 	private static final AbstractMethodDeclaration[] EMPTY_METHODS = {};
 	
-	private static class BuilderFieldData {
-		TypeReference type;
-		char[] rawName;
-		char[] name;
-		char[] nameOfDefaultProvider;
-		char[] nameOfSetFlag;
-		SingularData singularData;
-		ObtainVia obtainVia;
-		EclipseNode obtainViaNode;
-		EclipseNode originalFieldNode;
-		
-		List<EclipseNode> createdFields = new ArrayList<EclipseNode>();
-	}
-	
 	@Override
 	public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, EclipseNode annotationNode) {
 		handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder");
 		
 		long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
 		
-		SuperBuilder builderInstance = annotation.getInstance();
+		SuperBuilder superbuilderAnnotation = annotation.getInstance();
 		
-		String builderMethodName = builderInstance.builderMethodName();
-		String buildMethodName = builderInstance.buildMethodName();
+		String builderMethodName = superbuilderAnnotation.builderMethodName();
+		String buildMethodName = superbuilderAnnotation.buildMethodName();
 		
 		if (builderMethodName == null) builderMethodName = "builder";
 		if (buildMethodName == null) buildMethodName = "build";
 		
 		if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
 		if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
-		
+
+		boolean toBuilder = superbuilderAnnotation.toBuilder();
+
 		EclipseNode tdParent = annotationNode.up();
 		
 		java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
@@ -293,7 +290,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 			cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
 			injectFieldAndMarkGenerated(builderType, cleanDecl);
 		}
-		
+
+		if (toBuilder) {
+			// Generate $fillValuesFrom() method in the abstract builder.
+			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, typeParams, builderFields, ast));
+		}
+
 		// Generate abstract self() and build() methods in the abstract builder.
 		injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClass != null, builderGenericName));
 		injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClass != null, classGenericName, ast));
@@ -532,6 +534,93 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		return out;
 	}
 	
+	/**
+	 * Generates a <code>$fillValuesFrom()</code> method in the abstract builder class that looks
+	 * like this:
+	 * <pre>
+	 * protected B $fillValuesFrom(final C instance) {
+	 *     super.$fillValuesFrom(instance);
+	 *     this.field(instance.field);
+	 *     return self();
+	 * }
+	 * </pre>
+	 */
+	private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
+		MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
+		out.selector = FILL_VALUES_METHOD_NAME;
+		out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+		out.modifiers = ClassFileConstants.AccProtected;
+		if (inherited) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())};
+		out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0);
+		
+		final String instanceVariableName = "instance";
+		TypeReference builderType = createTypeReferenceWithTypeParameters(classGenericName, typeParams);
+		out.arguments = new Argument[] {new Argument(instanceVariableName.toCharArray(), 0, builderType, Modifier.FINAL)};
+
+		List<Statement> body = new ArrayList<Statement>();
+
+		if (inherited) {
+			// Call super.
+			MessageSend callToSuper = new MessageSend();
+			callToSuper.receiver = new SuperReference(0, 0);
+			callToSuper.selector = FILL_VALUES_METHOD_NAME;
+			callToSuper.arguments = new Expression[] {new SingleNameReference(instanceVariableName.toCharArray(), 0)};
+			body.add(callToSuper);
+		}
+
+		// Call the builder's setter methods to fill the values from the instance.
+		for (BuilderFieldData bfd : builderFields) {
+			MessageSend exec = createSetterCallWithInstanceValue(bfd, instanceVariableName, tdParent, source);
+			body.add(exec);
+		}
+
+		MessageSend returnCall = new MessageSend();
+		returnCall.receiver = ThisReference.implicitThis();
+		returnCall.selector = SELF_METHOD_NAME;
+		body.add(new ReturnStatement(returnCall, 0, 0));
+		
+		out.statements = body.isEmpty() ? null : body.toArray(new Statement[body.size()]);
+		
+		return out;
+	}
+
+	private MessageSend createSetterCallWithInstanceValue(BuilderFieldData bfd, final String instanceVariableName, EclipseNode type, ASTNode source) {
+			char[] setterName = bfd.name;
+			MessageSend ms = new MessageSend();
+			Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2];
+			
+			if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
+				char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray();
+				for (int i = 0; i < tgt.length; i++) {
+					FieldReference fr = new FieldReference(fieldName, 0);
+					fr.receiver = new SingleNameReference(instanceVariableName.toCharArray(), 0);
+					tgt[i] = fr;
+				}
+			} else {
+				String obtainName = bfd.obtainVia.method();
+				boolean obtainIsStatic = bfd.obtainVia.isStatic();
+				for (int i = 0; i < tgt.length; i++) {
+					MessageSend obtainExpr = new MessageSend();
+					obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0);
+					obtainExpr.selector = obtainName.toCharArray();
+					if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)};
+					tgt[i] = obtainExpr;
+				}
+			}
+			if (bfd.singularData == null) {
+				ms.arguments = tgt;
+			} else {
+				Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
+				MessageSend emptyList = new MessageSend();
+				emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray());
+				emptyList.selector = EMPTY_LIST;
+				ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])};
+			}
+			ms.receiver = ThisReference.implicitThis();
+			ms.selector = setterName;
+		return ms;
+	}
+	
 	private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) {
 		MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
 		out.selector = SELF_METHOD_NAME;
diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index 01ac2794..c8727f93 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -236,8 +236,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 			injectFieldAndMarkGenerated(builderType, uncleanField);
 		}
 		
-		// Generate $fillValuesFrom() method in the abstract builder.
-		injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderFields));
+		if (toBuilder) {
+			// Generate $fillValuesFrom() method in the abstract builder.
+			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderFields));
+		}
 
 		// Generate abstract self() and build() methods in the abstract builder.
 		injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName));
diff --git a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
index 5e5f9760..6dd06fb7 100644
--- a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
@@ -1,7 +1,7 @@
 import java.util.List;
 
-public class SuperBuilderBasic {
-  public static @lombok.experimental.SuperBuilder class Parent {
+public class SuperBuilderBasicToBuilder {
+  public static @lombok.experimental.SuperBuilder(toBuilder = true) class Parent {
     public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
       private @java.lang.SuppressWarnings("all") int field1;
       private @java.lang.SuppressWarnings("all") java.util.ArrayList<String> items;
@@ -9,8 +9,8 @@ public class SuperBuilderBasic {
         super();
       }
       protected @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
-        this.field1(instance.field1);
-        this.items(instance.items == null ? java.util.Collections.emptyList() : instance.items);
+        field1(instance.field1);
+        items(((instance.items == null) ? java.util.Collections.emptyList() : instance.items));
         return self();
       }
       protected abstract @java.lang.SuppressWarnings("all") B self();
@@ -37,7 +37,7 @@ public class SuperBuilderBasic {
         return self();
       }
       public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
-        return (((("SuperBuilderBasic.Parent.ParentBuilder(field1=" + this.field1) + ", items=") + this.items) + ")");
+        return (((("SuperBuilderBasicToBuilder.Parent.ParentBuilder(field1=" + this.field1) + ", items=") + this.items) + ")");
       }
     }
     private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
@@ -76,7 +76,7 @@ public class SuperBuilderBasic {
       return new ParentBuilderImpl().$fillValuesFrom(this);
     }
   }
-  public static @lombok.experimental.SuperBuilder class Child extends Parent {
+  public static @lombok.experimental.SuperBuilder(toBuilder = true) class Child extends Parent {
     public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
       private @java.lang.SuppressWarnings("all") double field3;
       public ChildBuilder() {
@@ -84,7 +84,7 @@ public class SuperBuilderBasic {
       }
       protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(C instance) {
         super.$fillValuesFrom(instance);
-        this.field3(instance.field3);
+        field3(instance.field3);
         return self();
       }
       protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
@@ -94,7 +94,7 @@ public class SuperBuilderBasic {
         return self();
       }
       public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
-        return (((("SuperBuilderBasic.Child.ChildBuilder(super=" + super.toString()) + ", field3=") + this.field3) + ")");
+        return (((("SuperBuilderBasicToBuilder.Child.ChildBuilder(super=" + super.toString()) + ", field3=") + this.field3) + ")");
       }
     }
     private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
-- 
cgit 


From da59d2d6375afcfcf6cb43288adba61cb99a49e8 Mon Sep 17 00:00:00 2001
From: Jan Rieke <it@janrieke.de>
Date: Tue, 11 Sep 2018 16:59:29 +0200
Subject: SuperBuilder: generate toBuilder method (ecj)

---
 .../eclipse/handlers/HandleSuperBuilder.java       | 47 +++++++++++++++++++++-
 .../lombok/javac/handlers/HandleSuperBuilder.java  |  1 -
 .../after-ecj/SuperBuilderBasicToBuilder.java      |  6 +--
 3 files changed, 49 insertions(+), 5 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index 6c1b2cac..1bbc514e 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -104,7 +104,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 	private static final char[] DEFAULT_PREFIX = "$default$".toCharArray();
 	private static final char[] SET_PREFIX = "$set".toCharArray();
 	private static final char[] SELF_METHOD_NAME = "self".toCharArray();
-	private static final char[] TO_BUILDER_METHOD_NAME = "toBuilder".toCharArray();
+	private static final String TO_BUILDER_METHOD_NAME_STRING = "toBuilder";
+	private static final char[] TO_BUILDER_METHOD_NAME = TO_BUILDER_METHOD_NAME_STRING.toCharArray();
 	private static final char[] FILL_VALUES_METHOD_NAME = "$fillValuesFrom".toCharArray();
 	private static final char[] EMPTY_LIST = "emptyList".toCharArray();
 	
@@ -345,6 +346,15 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 			MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, tdParent, typeParams, ast);
 			if (md != null) injectMethod(tdParent, md);
 		}
+
+		if (toBuilder) switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) {
+		case EXISTS_BY_USER:
+			annotationNode.addWarning("Not generating toBuilder() as it already exists.");
+			break;
+		case NOT_EXISTS:
+			MethodDeclaration md = generateToBuilderMethod(builderClassName, builderImplClassName, tdParent, typeParams, ast);
+			if (md != null) injectMethod(tdParent, md);
+		}
 	}
 	
 	private EclipseNode generateBuilderAbstractClass(EclipseNode tdParent, String builderClass,
@@ -534,6 +544,41 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		return out;
 	}
 	
+	/**
+	 * Generates a <code>toBuilder()</code> method in the annotated class that looks like this:
+	 * <pre>
+	 * public ParentBuilder&lt;?, ?&gt; toBuilder() {
+	 *     return new <i>Foobar</i>BuilderImpl().$fillValuesFrom(this);
+	 * }
+	 * </pre>
+	 */
+	private MethodDeclaration generateToBuilderMethod(String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
+		int pS = source.sourceStart, pE = source.sourceEnd;
+		long p = (long) pS << 32 | pE;
+		
+		MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
+		out.selector = TO_BUILDER_METHOD_NAME;
+		out.modifiers = ClassFileConstants.AccPublic;
+		out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+		
+		// Add type params if there are any.
+		if (typeParams != null && typeParams.length > 0) out.typeParameters = copyTypeParams(typeParams, source);
+		
+		TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND) };
+		out.returnType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p);
+		
+		AllocationExpression newClass = new AllocationExpression();
+		newClass.type = namePlusTypeParamsToTypeReference(builderImplClassName.toCharArray(), typeParams, p);
+		MessageSend invokeFillMethod = new MessageSend();
+		invokeFillMethod.receiver = newClass;
+		invokeFillMethod.selector = FILL_VALUES_METHOD_NAME;
+		invokeFillMethod.arguments = new Expression[] {new ThisReference(0, 0)};
+		out.statements = new Statement[] {new ReturnStatement(invokeFillMethod, pS, pE)};
+		
+		out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
+		return out;
+	}
+
 	/**
 	 * Generates a <code>$fillValuesFrom()</code> method in the abstract builder class that looks
 	 * like this:
diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index c8727f93..bdcd043a 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -523,7 +523,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		JCExpression newClass = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.<JCExpression>nil(), null);
 		List<JCExpression> methodArgs = List.<JCExpression>of(maker.Ident(type.toName("this")));
 		JCMethodInvocation invokeFillMethod = maker.Apply(List.<JCExpression>nil(), maker.Select(newClass, type.toName(FILL_VALUES_METHOD_NAME)), methodArgs);
-//		JCMethodInvocation invokeFillMethod = maker.Apply(List.<JCExpression>nil(), maker.Select(newClass, type.toName(FILL_VALUES_METHOD_NAME)), List.<JCExpression>nil());
 		JCStatement statement = maker.Return(invokeFillMethod);
 		
 		JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
diff --git a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
index 6dd06fb7..2b294d65 100644
--- a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
@@ -82,7 +82,7 @@ public class SuperBuilderBasicToBuilder {
       public ChildBuilder() {
         super();
       }
-      protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(C instance) {
+      protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
         super.$fillValuesFrom(instance);
         field3(instance.field3);
         return self();
@@ -120,10 +120,10 @@ public class SuperBuilderBasicToBuilder {
       return new ChildBuilderImpl().$fillValuesFrom(this);
     }
   }
-  public SuperBuilderBasic() {
+  public SuperBuilderBasicToBuilder() {
     super();
   }
   public static void test() {
-    Child x = Child.builder().field3(0.0).field1(5).item("").build();
+    Child x = Child.builder().field3(0.0).field1(5).item("").build().toBuilder().build();
   }
 }
-- 
cgit 


From a2b884c71194c780d37ccbd84561abd6a0f63741 Mon Sep 17 00:00:00 2001
From: Jan Rieke <it@janrieke.de>
Date: Tue, 11 Sep 2018 20:37:16 +0200
Subject: SuperBuilder: fixed ObtainVia

---
 .../eclipse/handlers/HandleSuperBuilder.java       |  4 +--
 .../after-delombok/SuperBuilderBasicToBuilder.java | 38 +++++++++++++++++++++-
 .../after-ecj/SuperBuilderBasicToBuilder.java      | 33 +++++++++++++++++--
 .../before/SuperBuilderBasicToBuilder.java         | 14 ++++++++
 4 files changed, 84 insertions(+), 5 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index 1bbc514e..bb78e82a 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -646,9 +646,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 				boolean obtainIsStatic = bfd.obtainVia.isStatic();
 				for (int i = 0; i < tgt.length; i++) {
 					MessageSend obtainExpr = new MessageSend();
-					obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0);
+					obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new SingleNameReference(instanceVariableName.toCharArray(), 0);
 					obtainExpr.selector = obtainName.toCharArray();
-					if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)};
+					if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new SingleNameReference(instanceVariableName.toCharArray(), 0)};
 					tgt[i] = obtainExpr;
 				}
 			}
diff --git a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
index 404984b9..7d34d28a 100644
--- a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
@@ -2,16 +2,34 @@ import java.util.List;
 public class SuperBuilderBasicToBuilder {
 	public static class Parent {
 		int field1;
+		int obtainViaField;
+		int obtainViaMethod;
+		String obtainViaStaticMethod;
 		List<String> items;
+		int method() {
+			return 2;
+		}
+		private static String staticMethod(Parent instance) {
+			return "staticMethod";
+		}
 		@java.lang.SuppressWarnings("all")
 		public static abstract class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
 			@java.lang.SuppressWarnings("all")
 			private int field1;
 			@java.lang.SuppressWarnings("all")
+			private int obtainViaField;
+			@java.lang.SuppressWarnings("all")
+			private int obtainViaMethod;
+			@java.lang.SuppressWarnings("all")
+			private String obtainViaStaticMethod;
+			@java.lang.SuppressWarnings("all")
 			private java.util.ArrayList<String> items;
 			@java.lang.SuppressWarnings("all")
 			protected B $fillValuesFrom(final C instance) {
 				this.field1(instance.field1);
+				this.obtainViaField(instance.field1);
+				this.obtainViaMethod(instance.method());
+				this.obtainViaStaticMethod(Parent.staticMethod(instance));
 				this.items(instance.items == null ? java.util.Collections.emptyList() : instance.items);
 				return self();
 			}
@@ -25,6 +43,21 @@ public class SuperBuilderBasicToBuilder {
 				return self();
 			}
 			@java.lang.SuppressWarnings("all")
+			public B obtainViaField(final int obtainViaField) {
+				this.obtainViaField = obtainViaField;
+				return self();
+			}
+			@java.lang.SuppressWarnings("all")
+			public B obtainViaMethod(final int obtainViaMethod) {
+				this.obtainViaMethod = obtainViaMethod;
+				return self();
+			}
+			@java.lang.SuppressWarnings("all")
+			public B obtainViaStaticMethod(final String obtainViaStaticMethod) {
+				this.obtainViaStaticMethod = obtainViaStaticMethod;
+				return self();
+			}
+			@java.lang.SuppressWarnings("all")
 			public B item(final String item) {
 				if (this.items == null) this.items = new java.util.ArrayList<String>();
 				this.items.add(item);
@@ -44,7 +77,7 @@ public class SuperBuilderBasicToBuilder {
 			@java.lang.Override
 			@java.lang.SuppressWarnings("all")
 			public java.lang.String toString() {
-				return "SuperBuilderBasicToBuilder.Parent.ParentBuilder(field1=" + this.field1 + ", items=" + this.items + ")";
+				return "SuperBuilderBasicToBuilder.Parent.ParentBuilder(field1=" + this.field1 + ", obtainViaField=" + this.obtainViaField + ", obtainViaMethod=" + this.obtainViaMethod + ", obtainViaStaticMethod=" + this.obtainViaStaticMethod + ", items=" + this.items + ")";
 			}
 		}
 		@java.lang.SuppressWarnings("all")
@@ -66,6 +99,9 @@ public class SuperBuilderBasicToBuilder {
 		@java.lang.SuppressWarnings("all")
 		protected Parent(final ParentBuilder<?, ?> b) {
 			this.field1 = b.field1;
+			this.obtainViaField = b.obtainViaField;
+			this.obtainViaMethod = b.obtainViaMethod;
+			this.obtainViaStaticMethod = b.obtainViaStaticMethod;
 			java.util.List<String> items;
 			switch (b.items == null ? 0 : b.items.size()) {
 			case 0: 
diff --git a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
index 2b294d65..974d933d 100644
--- a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
@@ -1,15 +1,20 @@
 import java.util.List;
-
 public class SuperBuilderBasicToBuilder {
   public static @lombok.experimental.SuperBuilder(toBuilder = true) class Parent {
     public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
       private @java.lang.SuppressWarnings("all") int field1;
+      private @java.lang.SuppressWarnings("all") int obtainViaField;
+      private @java.lang.SuppressWarnings("all") int obtainViaMethod;
+      private @java.lang.SuppressWarnings("all") String obtainViaStaticMethod;
       private @java.lang.SuppressWarnings("all") java.util.ArrayList<String> items;
       public ParentBuilder() {
         super();
       }
       protected @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
         field1(instance.field1);
+        obtainViaField(instance.field1);
+        obtainViaMethod(instance.method());
+        obtainViaStaticMethod(Parent.staticMethod(instance));
         items(((instance.items == null) ? java.util.Collections.emptyList() : instance.items));
         return self();
       }
@@ -19,6 +24,18 @@ public class SuperBuilderBasicToBuilder {
         this.field1 = field1;
         return self();
       }
+      public @java.lang.SuppressWarnings("all") B obtainViaField(final int obtainViaField) {
+        this.obtainViaField = obtainViaField;
+        return self();
+      }
+      public @java.lang.SuppressWarnings("all") B obtainViaMethod(final int obtainViaMethod) {
+        this.obtainViaMethod = obtainViaMethod;
+        return self();
+      }
+      public @java.lang.SuppressWarnings("all") B obtainViaStaticMethod(final String obtainViaStaticMethod) {
+        this.obtainViaStaticMethod = obtainViaStaticMethod;
+        return self();
+      }
       public @java.lang.SuppressWarnings("all") B item(String item) {
         if ((this.items == null))
             this.items = new java.util.ArrayList<String>();
@@ -37,7 +54,7 @@ public class SuperBuilderBasicToBuilder {
         return self();
       }
       public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
-        return (((("SuperBuilderBasicToBuilder.Parent.ParentBuilder(field1=" + this.field1) + ", items=") + this.items) + ")");
+        return (((((((((("SuperBuilderBasicToBuilder.Parent.ParentBuilder(field1=" + this.field1) + ", obtainViaField=") + this.obtainViaField) + ", obtainViaMethod=") + this.obtainViaMethod) + ", obtainViaStaticMethod=") + this.obtainViaStaticMethod) + ", items=") + this.items) + ")");
       }
     }
     private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
@@ -52,10 +69,22 @@ public class SuperBuilderBasicToBuilder {
       }
     }
     int field1;
+    @lombok.Builder.ObtainVia(field = "field1") int obtainViaField;
+    @lombok.Builder.ObtainVia(method = "method") int obtainViaMethod;
+    @lombok.Builder.ObtainVia(method = "staticMethod",isStatic = true) String obtainViaStaticMethod;
     @lombok.Singular List<String> items;
+    int method() {
+      return 2;
+    }
+    private static String staticMethod(Parent instance) {
+      return "staticMethod";
+    }
     protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder<?, ?> b) {
       super();
       this.field1 = b.field1;
+      this.obtainViaField = b.obtainViaField;
+      this.obtainViaMethod = b.obtainViaMethod;
+      this.obtainViaStaticMethod = b.obtainViaStaticMethod;
       java.util.List<String> items;
       switch (((b.items == null) ? 0 : b.items.size())) {
       case 0 :
diff --git a/test/transform/resource/before/SuperBuilderBasicToBuilder.java b/test/transform/resource/before/SuperBuilderBasicToBuilder.java
index bc3578f5..4dfac613 100644
--- a/test/transform/resource/before/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/before/SuperBuilderBasicToBuilder.java
@@ -4,7 +4,21 @@ public class SuperBuilderBasicToBuilder {
 	@lombok.experimental.SuperBuilder(toBuilder=true)
 	public static class Parent {
 		int field1;
+		@lombok.Builder.ObtainVia(field="field1")
+		int obtainViaField;
+		@lombok.Builder.ObtainVia(method="method")
+		int obtainViaMethod;
+		@lombok.Builder.ObtainVia(method = "staticMethod", isStatic = true)
+		String obtainViaStaticMethod;
 		@lombok.Singular List<String> items;
+		
+		int method() {
+			return 2;
+		}
+
+		private static String staticMethod(Parent instance) {
+			return "staticMethod";
+		}
 	}
 	
 	@lombok.experimental.SuperBuilder(toBuilder=true)
-- 
cgit 


From f5fff49f73ceabb776f470a9ed346ace0ce12be6 Mon Sep 17 00:00:00 2001
From: Jan Rieke <rieke@subshell.com>
Date: Fri, 14 Sep 2018 22:53:02 +0200
Subject: SuperBuilder toBuilder: generate static toBuilder helper method (ecj)

---
 .../eclipse/handlers/HandleSuperBuilder.java       | 168 +++++++++++++--------
 .../after-delombok/SuperBuilderBasicToBuilder.java |   4 +-
 .../after-ecj/SuperBuilderBasicToBuilder.java      |  16 +-
 3 files changed, 119 insertions(+), 69 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index bb78e82a..a68ede3c 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -107,7 +107,11 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 	private static final String TO_BUILDER_METHOD_NAME_STRING = "toBuilder";
 	private static final char[] TO_BUILDER_METHOD_NAME = TO_BUILDER_METHOD_NAME_STRING.toCharArray();
 	private static final char[] FILL_VALUES_METHOD_NAME = "$fillValuesFrom".toCharArray();
+	private static final char[] FILL_VALUES_STATIC_METHOD_NAME = "$fillValuesFromInstanceIntoBuilder".toCharArray();
 	private static final char[] EMPTY_LIST = "emptyList".toCharArray();
+	private static final char[] INSTANCE_VARIABLE_NAME = "instance".toCharArray();
+	private static final String BUILDER_VARIABLE_NAME_STRING = "b";
+	private static final char[] BUILDER_VARIABLE_NAME = BUILDER_VARIABLE_NAME_STRING.toCharArray();
 	
 	private static final AbstractMethodDeclaration[] EMPTY_METHODS = {};
 	
@@ -294,7 +298,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 
 		if (toBuilder) {
 			// Generate $fillValuesFrom() method in the abstract builder.
-			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, typeParams, builderFields, ast));
+			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, builderImplClassName, typeParams, builderFields, ast));
 		}
 
 		// Generate abstract self() and build() methods in the abstract builder.
@@ -323,8 +327,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		
 		if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
 		
-		if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) {
+		if ((td.modifiers & ClassFileConstants.AccAbstract) != 0 && !toBuilder) {
 			// Only non-abstract classes get the Builder implementation.
+			// However, if we want to generate a toBuilder, we need a helper function in the builder implementation class.
 			return;
 		}
 		
@@ -336,7 +341,24 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 			annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
 			return;
 		}
-		
+
+		if (toBuilder) {
+			// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
+			injectMethod(builderImplType, generateStaticFillValuesMethod(tdParent, superclassBuilderClass != null, builderClassName, classGenericName, typeParams, builderFields, ast));
+			
+			switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) {
+			case EXISTS_BY_USER:
+				annotationNode.addWarning("Not generating toBuilder() as it already exists.");
+				break;
+			case NOT_EXISTS:
+				injectMethod(tdParent, generateToBuilderMethod(builderClassName, builderImplClassName, tdParent, typeParams, ast));
+			}
+			if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) {
+				// The rest of the builder implementation class is not necessary for abstract classes.
+				return;
+			}
+		}
+
 		// Create the self() and build() methods in the BuilderImpl.
 		injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams, p));
 		injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast));
@@ -346,15 +368,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 			MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, tdParent, typeParams, ast);
 			if (md != null) injectMethod(tdParent, md);
 		}
-
-		if (toBuilder) switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) {
-		case EXISTS_BY_USER:
-			annotationNode.addWarning("Not generating toBuilder() as it already exists.");
-			break;
-		case NOT_EXISTS:
-			MethodDeclaration md = generateToBuilderMethod(builderClassName, builderImplClassName, tdParent, typeParams, ast);
-			if (md != null) injectMethod(tdParent, md);
-		}
 	}
 	
 	private EclipseNode generateBuilderAbstractClass(EclipseNode tdParent, String builderClass,
@@ -449,7 +462,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		constructor.selector = typeDeclaration.name;
 		if (callBuilderBasedSuperConstructor) {
 			constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super);
-			constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)};
+			constructor.constructorCall.arguments = new Expression[] {new SingleNameReference(BUILDER_VARIABLE_NAME, p)};
 		} else {
 			constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper);
 		}
@@ -463,7 +476,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		
 		TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)};
 		TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p);
-		constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, builderType, Modifier.FINAL)};
+		constructor.arguments = new Argument[] {new Argument(BUILDER_VARIABLE_NAME, p, builderType, Modifier.FINAL)};
 		
 		List<Statement> statements = new ArrayList<Statement>();
 		
@@ -476,10 +489,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 			
 			Expression assignmentExpr;
 			if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) {
-				fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, "b");
+				fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, BUILDER_VARIABLE_NAME_STRING);
 				assignmentExpr = new SingleNameReference(fieldNode.name, p);
 			} else {
-				char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName};
+				char[][] variableInBuilder = new char[][] {BUILDER_VARIABLE_NAME, fieldName};
 				long[] positions = new long[] {p, p};
 				assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e);
 			}
@@ -487,7 +500,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 			
 			// In case of @Builder.Default, set the value to the default if it was NOT set in the builder.
 			if (fieldNode.nameOfSetFlag != null) {
-				char[][] setVariableInBuilder = new char[][] {"b".toCharArray(), fieldNode.nameOfSetFlag};
+				char[][] setVariableInBuilder = new char[][] {BUILDER_VARIABLE_NAME, fieldNode.nameOfSetFlag};
 				long[] positions = new long[] {p, p};
 				QualifiedNameReference setVariableInBuilderRef = new QualifiedNameReference(setVariableInBuilder, positions, s, e);
 
@@ -585,12 +598,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 	 * <pre>
 	 * protected B $fillValuesFrom(final C instance) {
 	 *     super.$fillValuesFrom(instance);
-	 *     this.field(instance.field);
+	 *     FoobarBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this);
 	 *     return self();
 	 * }
 	 * </pre>
 	 */
-	private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
+	private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, String builderImplClassName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
 		MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
 		out.selector = FILL_VALUES_METHOD_NAME;
 		out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
@@ -598,9 +611,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		if (inherited) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())};
 		out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0);
 		
-		final String instanceVariableName = "instance";
 		TypeReference builderType = createTypeReferenceWithTypeParameters(classGenericName, typeParams);
-		out.arguments = new Argument[] {new Argument(instanceVariableName.toCharArray(), 0, builderType, Modifier.FINAL)};
+		out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, builderType, Modifier.FINAL)};
 
 		List<Statement> body = new ArrayList<Statement>();
 
@@ -609,16 +621,18 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 			MessageSend callToSuper = new MessageSend();
 			callToSuper.receiver = new SuperReference(0, 0);
 			callToSuper.selector = FILL_VALUES_METHOD_NAME;
-			callToSuper.arguments = new Expression[] {new SingleNameReference(instanceVariableName.toCharArray(), 0)};
+			callToSuper.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0)};
 			body.add(callToSuper);
 		}
 
-		// Call the builder's setter methods to fill the values from the instance.
-		for (BuilderFieldData bfd : builderFields) {
-			MessageSend exec = createSetterCallWithInstanceValue(bfd, instanceVariableName, tdParent, source);
-			body.add(exec);
-		}
+		// Call the builder implemention's helper method that actually fills the values from the instance.
+		MessageSend callStaticFillValuesMethod = new MessageSend();
+		callStaticFillValuesMethod.receiver = new SingleNameReference(builderImplClassName.toCharArray(), 0);
+		callStaticFillValuesMethod.selector = FILL_VALUES_STATIC_METHOD_NAME;
+		callStaticFillValuesMethod.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0), new ThisReference(0, 0)};
+		body.add(callStaticFillValuesMethod);
 
+		// Return self().
 		MessageSend returnCall = new MessageSend();
 		returnCall.receiver = ThisReference.implicitThis();
 		returnCall.selector = SELF_METHOD_NAME;
@@ -629,40 +643,76 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		return out;
 	}
 
-	private MessageSend createSetterCallWithInstanceValue(BuilderFieldData bfd, final String instanceVariableName, EclipseNode type, ASTNode source) {
-			char[] setterName = bfd.name;
-			MessageSend ms = new MessageSend();
-			Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2];
-			
-			if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
-				char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray();
-				for (int i = 0; i < tgt.length; i++) {
-					FieldReference fr = new FieldReference(fieldName, 0);
-					fr.receiver = new SingleNameReference(instanceVariableName.toCharArray(), 0);
-					tgt[i] = fr;
-				}
-			} else {
-				String obtainName = bfd.obtainVia.method();
-				boolean obtainIsStatic = bfd.obtainVia.isStatic();
-				for (int i = 0; i < tgt.length; i++) {
-					MessageSend obtainExpr = new MessageSend();
-					obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new SingleNameReference(instanceVariableName.toCharArray(), 0);
-					obtainExpr.selector = obtainName.toCharArray();
-					if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new SingleNameReference(instanceVariableName.toCharArray(), 0)};
-					tgt[i] = obtainExpr;
-				}
+	/**
+	 * Generates a <code>$fillValuesFromInstanceIntoBuilder()</code> method in
+	 * the builder implementation class that copies all fields from the instance
+	 * to the builder. It looks like this:
+	 * 
+	 * <pre>
+	 * protected B $fillValuesFromInstanceIntoBuilder(Foobar instance, FoobarBuilder&lt;?, ?&gt; b) {
+	 * 	b.field(instance.field);
+	 * }
+	 * </pre>
+	 */
+	private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderClassName, String classGenericName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
+		MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
+		out.selector = FILL_VALUES_STATIC_METHOD_NAME;
+		out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+		out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic;
+		out.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0);
+		
+		TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)};
+		TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, 0);
+		Argument builderArgument = new Argument(BUILDER_VARIABLE_NAME, 0, builderType, Modifier.FINAL);
+		out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, new SingleTypeReference(tdParent.getName().toCharArray(), 0), Modifier.FINAL), builderArgument};
+
+		List<Statement> body = new ArrayList<Statement>();
+
+		// Call the builder's setter methods to fill the values from the instance.
+		for (BuilderFieldData bfd : builderFields) {
+			MessageSend exec = createSetterCallWithInstanceValue(bfd, tdParent, source);
+			body.add(exec);
+		}
+		
+		out.statements = body.isEmpty() ? null : body.toArray(new Statement[body.size()]);
+		
+		return out;
+	}
+
+	private MessageSend createSetterCallWithInstanceValue(BuilderFieldData bfd, EclipseNode type, ASTNode source) {
+		char[] setterName = bfd.name;
+		MessageSend ms = new MessageSend();
+		Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2];
+		
+		if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
+			char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray();
+			for (int i = 0; i < tgt.length; i++) {
+				FieldReference fr = new FieldReference(fieldName, 0);
+				fr.receiver = new SingleNameReference(INSTANCE_VARIABLE_NAME, 0);
+				tgt[i] = fr;
 			}
-			if (bfd.singularData == null) {
-				ms.arguments = tgt;
-			} else {
-				Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
-				MessageSend emptyList = new MessageSend();
-				emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray());
-				emptyList.selector = EMPTY_LIST;
-				ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])};
+		} else {
+			String obtainName = bfd.obtainVia.method();
+			boolean obtainIsStatic = bfd.obtainVia.isStatic();
+			for (int i = 0; i < tgt.length; i++) {
+				MessageSend obtainExpr = new MessageSend();
+				obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new SingleNameReference(INSTANCE_VARIABLE_NAME, 0);
+				obtainExpr.selector = obtainName.toCharArray();
+				if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0)};
+				tgt[i] = obtainExpr;
 			}
-			ms.receiver = ThisReference.implicitThis();
-			ms.selector = setterName;
+		}
+		if (bfd.singularData == null) {
+			ms.arguments = tgt;
+		} else {
+			Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
+			MessageSend emptyList = new MessageSend();
+			emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray());
+			emptyList.selector = EMPTY_LIST;
+			ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])};
+		}
+		ms.receiver = new SingleNameReference(BUILDER_VARIABLE_NAME, 0);
+		ms.selector = setterName;
 		return ms;
 	}
 	
diff --git a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
index c5099480..dc0b6e0c 100644
--- a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
@@ -79,7 +79,7 @@ public class SuperBuilderBasicToBuilder {
 		@java.lang.SuppressWarnings("all")
 		private static final class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
 			@java.lang.SuppressWarnings("all")
-			private static void $fillValuesFromInstanceIntoBuilder(Parent instance, ParentBuilder<?,?> b) {
+			private static void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?,?> b) {
 				b.field1(instance.field1);
 				b.obtainViaField(instance.field1);
 				b.obtainViaMethod(instance.method());
@@ -161,7 +161,7 @@ public class SuperBuilderBasicToBuilder {
 		@java.lang.SuppressWarnings("all")
 		private static final class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
 			@java.lang.SuppressWarnings("all")
-			private static void $fillValuesFromInstanceIntoBuilder(Parent instance, ParentBuilder<?,?> b) {
+			private static void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?,?> b) {
 				b.field3(instance.field3);
 			}
 			@java.lang.SuppressWarnings("all")
diff --git a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
index fac9ab55..be819d05 100644
--- a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
@@ -57,7 +57,7 @@ public class SuperBuilderBasicToBuilder {
       private ParentBuilderImpl() {
         super();
       }
-      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(Parent instance, ParentBuilder<?,?> b) {
+      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
         b.field1(instance.field1);
         b.obtainViaField(instance.field1);
         b.obtainViaMethod(instance.method());
@@ -101,12 +101,12 @@ public class SuperBuilderBasicToBuilder {
       }
       this.items = items;
     }
-    public static @java.lang.SuppressWarnings("all") ParentBuilder<?, ?> builder() {
-      return new ParentBuilderImpl();
-    }
     public @java.lang.SuppressWarnings("all") ParentBuilder<?, ?> toBuilder() {
       return new ParentBuilderImpl().$fillValuesFrom(this);
     }
+    public static @java.lang.SuppressWarnings("all") ParentBuilder<?, ?> builder() {
+      return new ParentBuilderImpl();
+    }
   }
   public static @lombok.experimental.SuperBuilder(toBuilder = true) class Child extends Parent {
     public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
@@ -133,7 +133,7 @@ public class SuperBuilderBasicToBuilder {
       private ChildBuilderImpl() {
         super();
       }
-      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(Child instance, ChildBuilder<?,?> b) {
+      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
         b.field3(instance.field3);
       }
       protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() {
@@ -148,12 +148,12 @@ public class SuperBuilderBasicToBuilder {
       super(b);
       this.field3 = b.field3;
     }
-    public static @java.lang.SuppressWarnings("all") ChildBuilder<?, ?> builder() {
-      return new ChildBuilderImpl();
-    }
     public @java.lang.SuppressWarnings("all") ChildBuilder<?, ?> toBuilder() {
       return new ChildBuilderImpl().$fillValuesFrom(this);
     }
+    public static @java.lang.SuppressWarnings("all") ChildBuilder<?, ?> builder() {
+      return new ChildBuilderImpl();
+    }
   }
   public SuperBuilderBasicToBuilder() {
     super();
-- 
cgit 


From 7a287670f4bb6b22db920b980ce5dde4fcef5af5 Mon Sep 17 00:00:00 2001
From: Jan Rieke <it@janrieke.de>
Date: Sun, 16 Sep 2018 15:51:19 +0200
Subject: SuperBuilder toBuilder: generate static toBuilder helper method
 (javac)

---
 .../eclipse/handlers/HandleSuperBuilder.java       |   4 +-
 .../lombok/javac/handlers/HandleSuperBuilder.java  | 114 ++++++++++++++++-----
 .../after-delombok/SuperBuilderBasicToBuilder.java |   4 +-
 3 files changed, 90 insertions(+), 32 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index a68ede3c..fe53c7ed 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -344,7 +344,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 
 		if (toBuilder) {
 			// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
-			injectMethod(builderImplType, generateStaticFillValuesMethod(tdParent, superclassBuilderClass != null, builderClassName, classGenericName, typeParams, builderFields, ast));
+			injectMethod(builderImplType, generateStaticFillValuesMethod(tdParent, superclassBuilderClass != null, builderClassName, typeParams, builderFields, ast));
 			
 			switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) {
 			case EXISTS_BY_USER:
@@ -654,7 +654,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 	 * }
 	 * </pre>
 	 */
-	private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderClassName, String classGenericName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
+	private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderClassName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
 		MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
 		out.selector = FILL_VALUES_STATIC_METHOD_NAME;
 		out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index bdcd043a..84adc72e 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -63,6 +63,7 @@ import lombok.core.AST.Kind;
 import lombok.core.AnnotationValues;
 import lombok.core.HandlerPriority;
 import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.HandlerUtil.FieldAccess;
 import lombok.core.handlers.InclusionExclusionUtils.Included;
 import lombok.experimental.NonFinal;
 import lombok.experimental.SuperBuilder;
@@ -73,9 +74,9 @@ import lombok.javac.JavacTreeMaker;
 import lombok.javac.handlers.HandleBuilder.BuilderFieldData;
 import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
 import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker;
-import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker;
 import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
 import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker;
 
 @ProviderFor(JavacAnnotationHandler.class)
 @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
@@ -83,6 +84,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 	private static final String SELF_METHOD = "self";
 	private static final String TO_BUILDER_METHOD_NAME = "toBuilder";
 	private static final String FILL_VALUES_METHOD_NAME = "$fillValuesFrom";
+	private static final String STATIC_FILL_VALUES_METHOD_NAME = "$fillValuesFromInstanceIntoBuilder";
+	private static final String INSTANCE_VARIABLE_NAME = "instance";
+	private static final String BUILDER_VARIABLE_NAME = "b";
 
 	@Override
 	public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast, JavacNode annotationNode) {
@@ -238,7 +242,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		
 		if (toBuilder) {
 			// Generate $fillValuesFrom() method in the abstract builder.
-			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderFields));
+			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderImplClassName, builderFields));
 		}
 
 		// Generate abstract self() and build() methods in the abstract builder.
@@ -267,8 +271,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		
 		recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
 		
-		if ((td.mods.flags & Flags.ABSTRACT) == 0) {
+		boolean isAbstract = (td.mods.flags & Flags.ABSTRACT) != 0;
+		if (!isAbstract || toBuilder) {
 			// Only non-abstract classes get the Builder implementation.
+			// However, if we want to generate a toBuilder, we need a helper function in the builder implementation class.
 			
 			// Create the builder implementation class.
 			JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName);
@@ -278,14 +284,21 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 				annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
 				return;
 			}
+
+			if (toBuilder) {
+				// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
+				injectMethod(builderImplType, generateStaticFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderClassName, classGenericName, typeParams, builderFields, ast));
+			}
 			
-			// Create a simple constructor for the BuilderImpl class.
-			JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), builderImplType, List.<JavacNode>nil(), false, annotationNode);
-			if (cd != null) injectMethod(builderImplType, cd);
-			
-			// Create the self() and build() methods in the BuilderImpl.
-			injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams));
-			injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions));
+			if (!isAbstract) {
+				// Create a simple constructor for the BuilderImpl class.
+				JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), builderImplType, List.<JavacNode>nil(), false, annotationNode);
+				if (cd != null) injectMethod(builderImplType, cd);
+				
+				// Create the self() and build() methods in the BuilderImpl.
+				injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams));
+				injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions));
+			}
 			
 			recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext());
 		}
@@ -294,8 +307,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
 				superclassBuilderClassExpression != null);
 		
-		if ((td.mods.flags & Flags.ABSTRACT) == 0) {
-			// Only non-abstract classes get the Builder implementation and the builder() method.
+		if (!isAbstract) {
+			// Only non-abstract classes get the builder() method.
 			
 			// Add the builder() method to the annotated class.
 			// Allow users to specify their own builder() methods, e.g., to provide default values.
@@ -419,7 +432,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		
 		ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
 		
-		Name builderVariableName = typeNode.toName("b");
+		Name builderVariableName = typeNode.toName(BUILDER_VARIABLE_NAME);
 		for (BuilderFieldData bfd : builderFields) {
 			JCExpression rhs;
 			if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
@@ -546,12 +559,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 	 * <pre>
 	 * protected B $fillValuesFrom(final C instance) {
 	 *     super.$fillValuesFrom(instance);
-	 *     this.field(instance.field);
+	 *     FoobarBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this);
 	 *     return self();
 	 * }
 	 * </pre>
 	 */
-	private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited, String builderGenericName, String classGenericName, java.util.List<BuilderFieldData> builderFields) {
+	private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited, String builderGenericName, String classGenericName, String builderImplClassName, java.util.List<BuilderFieldData> builderFields) {
 		JavacTreeMaker maker = type.getTreeMaker();
 		List<JCAnnotation> annotations = List.nil();
 		if (inherited) {
@@ -562,9 +575,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		Name name = type.toName(FILL_VALUES_METHOD_NAME);
 		JCExpression returnType = maker.Ident(type.toName(builderGenericName));
 		
-		final String instanceVariableName = "instance";
 		JCExpression classGenericNameExpr = maker.Ident(type.toName(classGenericName));
-		JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(instanceVariableName), classGenericNameExpr, null);
+		JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(INSTANCE_VARIABLE_NAME), classGenericNameExpr, null);
 
 		ListBuffer<JCStatement> body = new ListBuffer<JCStatement>();
 		
@@ -572,15 +584,15 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 			// Call super.
 			JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
 					maker.Select(maker.Ident(type.toName("super")), name),
-					List.<JCExpression>of(maker.Ident(type.toName(instanceVariableName))));
+					List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME))));
 			body.append(maker.Exec(callToSuper));
 		}
 		
-		// Call the builder's setter methods to fill the values from the instance.
-		for (BuilderFieldData bfd : builderFields) {
-			JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, instanceVariableName, type, maker);
-			body.append(exec);
-		}
+		// Call the builder implemention's helper method that actually fills the values from the instance.
+		JCMethodInvocation callStaticFillValuesMethod = maker.Apply(List.<JCExpression>nil(),
+						maker.Select(maker.Ident(type.toName(builderImplClassName)), type.toName(STATIC_FILL_VALUES_METHOD_NAME)),
+						List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), maker.Ident(type.toName("this"))));
+		body.append(maker.Exec(callStaticFillValuesMethod));
 		
 		JCReturn returnStatement = maker.Return(maker.Apply(List.<JCExpression>nil(), maker.Ident(type.toName(SELF_METHOD)), List.<JCExpression>nil()));
 		body.append(returnStatement);
@@ -589,21 +601,67 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.of(param), List.<JCExpression>nil(), bodyBlock, null);
 	}
 
-	private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData bfd, final String instanceVariableName, JavacNode type, JavacTreeMaker maker) {
+	/**
+	 * Generates a <code>$fillValuesFromInstanceIntoBuilder()</code> method in
+	 * the builder implementation class that copies all fields from the instance
+	 * to the builder. It looks like this:
+	 * 
+	 * <pre>
+	 * protected B $fillValuesFromInstanceIntoBuilder(Foobar instance, FoobarBuilder&lt;?, ?&gt; b) {
+	 * 	b.field(instance.field);
+	 * }
+	 * </pre>
+	 */
+	private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, boolean inherited, String builderClassname, String classGenericName, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, JCAnnotation ast) {
+		JavacTreeMaker maker = type.getTreeMaker();
+		List<JCAnnotation> annotations = List.nil();
+		JCModifiers modifiers = maker.Modifiers(Flags.PRIVATE | Flags.STATIC, annotations);
+		Name name = type.toName(STATIC_FILL_VALUES_METHOD_NAME);
+		JCExpression returnType = maker.TypeIdent(CTC_VOID);
+		
+		// 1st parameter: "Foobar instance"
+		JCExpression classGenericNameExpr = maker.Ident(type.toName(type.getName()));
+		JCVariableDecl paramInstance = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(INSTANCE_VARIABLE_NAME), classGenericNameExpr, null);
+
+		// 2nd parameter: "FoobarBuilder<?, ?> b" (plus generics on the annotated type)
+		// First add all generics that are present on the parent type.
+		ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(typeParams, maker);
+		// Now add the <?, ?>.
+		JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+		typeParamsForBuilderParameter.add(wildcard);
+		wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+		typeParamsForBuilderParameter.add(wildcard);
+		JCTypeApply builderType = maker.TypeApply(maker.Ident(type.toName(builderClassname)), typeParamsForBuilderParameter.toList());
+		JCVariableDecl paramBuilder = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(BUILDER_VARIABLE_NAME), builderType, null);
+
+		ListBuffer<JCStatement> body = new ListBuffer<JCStatement>();
+		
+		// Call the builder's setter methods to fill the values from the instance.
+		for (BuilderFieldData bfd : builderFields) {
+			JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, type, maker);
+			body.append(exec);
+		}
+		
+		JCBlock bodyBlock = maker.Block(0, body.toList());
+
+		return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.of(paramInstance, paramBuilder), List.<JCExpression>nil(), bodyBlock, null);
+	}
+	
+	private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData bfd, JavacNode type, JavacTreeMaker maker) {
 		JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2];
 		if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
 			for (int i = 0; i < tgt.length; i++) {
-				tgt[i] = maker.Select(maker.Ident(type.toName(instanceVariableName)), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field()));
+				tgt[i] = maker.Select(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field()));
 			}
 		} else {
 			if (bfd.obtainVia.isStatic()) {
 				for (int i = 0; i < tgt.length; i++) {
 					JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method()));
-					tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName(instanceVariableName))));
+					tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME))));
 				}
 			} else {
 				for (int i = 0; i < tgt.length; i++) {
-					JCExpression c = maker.Select(maker.Ident(type.toName(instanceVariableName)), type.toName(bfd.obtainVia.method()));
+					JCExpression c = maker.Select(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), type.toName(bfd.obtainVia.method()));
 					tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil());
 				}
 			}
@@ -617,7 +675,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 			JCExpression emptyList = maker.Apply(List.<JCExpression>nil(), chainDots(type, "java", "util", "Collections", "emptyList"), List.<JCExpression>nil());
 			arg = maker.Conditional(eqNull, emptyList, tgt[1]);
 		}
-		JCMethodInvocation apply = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName("this")), bfd.name), List.of(arg));
+		JCMethodInvocation apply = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName(BUILDER_VARIABLE_NAME)), bfd.name), List.of(arg));
 		JCExpressionStatement exec = maker.Exec(apply);
 		return exec;
 	}
diff --git a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
index dc0b6e0c..7a80f019 100644
--- a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
@@ -79,7 +79,7 @@ public class SuperBuilderBasicToBuilder {
 		@java.lang.SuppressWarnings("all")
 		private static final class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
 			@java.lang.SuppressWarnings("all")
-			private static void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?,?> b) {
+			private static void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
 				b.field1(instance.field1);
 				b.obtainViaField(instance.field1);
 				b.obtainViaMethod(instance.method());
@@ -161,7 +161,7 @@ public class SuperBuilderBasicToBuilder {
 		@java.lang.SuppressWarnings("all")
 		private static final class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
 			@java.lang.SuppressWarnings("all")
-			private static void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?,?> b) {
+			private static void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
 				b.field3(instance.field3);
 			}
 			@java.lang.SuppressWarnings("all")
-- 
cgit 


From 41d8929630cedb1f310a7baebcfead616215093a Mon Sep 17 00:00:00 2001
From: Jan Rieke <it@janrieke.de>
Date: Sun, 16 Sep 2018 16:02:28 +0200
Subject: SuperBuilder: removed unnecessary method params

---
 .../eclipse/handlers/HandleSuperBuilder.java       | 10 ++++++----
 .../lombok/javac/handlers/HandleSuperBuilder.java  | 22 ++++++++++++----------
 2 files changed, 18 insertions(+), 14 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index fe53c7ed..fc532afb 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -298,7 +298,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 
 		if (toBuilder) {
 			// Generate $fillValuesFrom() method in the abstract builder.
-			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, builderImplClassName, typeParams, builderFields, ast));
+			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, builderImplClassName, typeParams));
 		}
 
 		// Generate abstract self() and build() methods in the abstract builder.
@@ -344,7 +344,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 
 		if (toBuilder) {
 			// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
-			injectMethod(builderImplType, generateStaticFillValuesMethod(tdParent, superclassBuilderClass != null, builderClassName, typeParams, builderFields, ast));
+			injectMethod(builderImplType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, ast));
 			
 			switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) {
 			case EXISTS_BY_USER:
@@ -352,6 +352,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 				break;
 			case NOT_EXISTS:
 				injectMethod(tdParent, generateToBuilderMethod(builderClassName, builderImplClassName, tdParent, typeParams, ast));
+			default:
+				// Should not happen.
 			}
 			if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) {
 				// The rest of the builder implementation class is not necessary for abstract classes.
@@ -603,7 +605,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 	 * }
 	 * </pre>
 	 */
-	private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, String builderImplClassName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
+	private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, String builderImplClassName, TypeParameter[] typeParams) {
 		MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
 		out.selector = FILL_VALUES_METHOD_NAME;
 		out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
@@ -654,7 +656,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 	 * }
 	 * </pre>
 	 */
-	private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderClassName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
+	private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
 		MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
 		out.selector = FILL_VALUES_STATIC_METHOD_NAME;
 		out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index 84adc72e..f858cd29 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -226,7 +226,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		JavacNode builderType = findInnerClass(tdParent, builderClassName);
 		if (builderType == null) {
 			builderType = generateBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression,
-					typeParams, superclassTypeParams, ast, classGenericName, builderGenericName);
+					typeParams, superclassTypeParams, classGenericName, builderGenericName);
 		} else {
 			annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
 			return;
@@ -242,7 +242,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		
 		if (toBuilder) {
 			// Generate $fillValuesFrom() method in the abstract builder.
-			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderImplClassName, builderFields));
+			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderImplClassName));
 		}
 
 		// Generate abstract self() and build() methods in the abstract builder.
@@ -279,7 +279,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 			// Create the builder implementation class.
 			JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName);
 			if (builderImplType == null) {
-				builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast);
+				builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams);
 			} else {
 				annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
 				return;
@@ -287,7 +287,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 
 			if (toBuilder) {
 				// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
-				injectMethod(builderImplType, generateStaticFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderClassName, classGenericName, typeParams, builderFields, ast));
+				injectMethod(builderImplType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields));
 			}
 			
 			if (!isAbstract) {
@@ -329,6 +329,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 				if (md != null) {
 					injectMethod(tdParent, md);
 				}
+			default:
+				// Should not happen.
 			}
 		}
 	}
@@ -338,7 +340,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 	 */
 	private JavacNode generateBuilderAbstractClass(JavacNode source, JavacNode tdParent, String builderClass,
 			JCExpression superclassBuilderClassExpression, List<JCTypeParameter> typeParams,
-			List<JCExpression> superclassTypeParams, JCAnnotation ast, String classGenericName, String builderGenericName) {
+			List<JCExpression> superclassTypeParams, String classGenericName, String builderGenericName) {
 		
 		JavacTreeMaker maker = tdParent.getTreeMaker();
 		JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC);
@@ -380,7 +382,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 	/**
 	 * Creates and returns the concrete builder implementation class and injects it into the annotated class.
 	 */
-	private JavacNode generateBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List<JCTypeParameter> typeParams, JCAnnotation ast) {
+	private JavacNode generateBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List<JCTypeParameter> typeParams) {
 		JavacTreeMaker maker = tdParent.getTreeMaker();
 		JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE | Flags.FINAL);
 		
@@ -564,7 +566,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 	 * }
 	 * </pre>
 	 */
-	private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited, String builderGenericName, String classGenericName, String builderImplClassName, java.util.List<BuilderFieldData> builderFields) {
+	private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited, String builderGenericName, String classGenericName, String builderImplClassName) {
 		JavacTreeMaker maker = type.getTreeMaker();
 		List<JCAnnotation> annotations = List.nil();
 		if (inherited) {
@@ -612,7 +614,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 	 * }
 	 * </pre>
 	 */
-	private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, boolean inherited, String builderClassname, String classGenericName, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, JCAnnotation ast) {
+	private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, String builderClassname, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields) {
 		JavacTreeMaker maker = type.getTreeMaker();
 		List<JCAnnotation> annotations = List.nil();
 		JCModifiers modifiers = maker.Modifiers(Flags.PRIVATE | Flags.STATIC, annotations);
@@ -802,13 +804,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		}};
 		
 		if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
-			generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnTypeMaker.make(), returnStatementMaker.make());
+			generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, returnTypeMaker.make(), returnStatementMaker.make());
 		} else {
 			fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker);
 		}
 	}
 	
-	private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCStatement returnStatement) {
+	private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, JCExpression returnType, JCStatement returnStatement) {
 		Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
 		
 		for (JavacNode child : builderType.down()) {
-- 
cgit 


From 790e060380f01ea113bfeee25b3138ea267ebcb4 Mon Sep 17 00:00:00 2001
From: Jan Rieke <it@janrieke.de>
Date: Sun, 16 Sep 2018 17:24:39 +0200
Subject: SuperBuilder toBuilder: moved static helper method to builder impl

---
 .../eclipse/handlers/HandleSuperBuilder.java       |  20 +--
 .../lombok/javac/handlers/HandleSuperBuilder.java  |  46 +++---
 .../SuperBuilderAbstractToBuilder.java             | 168 +++++++++++++++++++++
 .../after-delombok/SuperBuilderBasicToBuilder.java |  28 ++--
 .../after-ecj/SuperBuilderAbstractToBuilder.java   | 131 ++++++++++++++++
 .../after-ecj/SuperBuilderBasicToBuilder.java      |  24 +--
 .../before/SuperBuilderAbstractToBuilder.java      |  20 +++
 7 files changed, 374 insertions(+), 63 deletions(-)
 create mode 100644 test/transform/resource/after-delombok/SuperBuilderAbstractToBuilder.java
 create mode 100644 test/transform/resource/after-ecj/SuperBuilderAbstractToBuilder.java
 create mode 100644 test/transform/resource/before/SuperBuilderAbstractToBuilder.java

(limited to 'src')

diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index fc532afb..640e5a71 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -298,7 +298,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 
 		if (toBuilder) {
 			// Generate $fillValuesFrom() method in the abstract builder.
-			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, builderImplClassName, typeParams));
+			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, builderClassName, typeParams));
+			// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
+			injectMethod(builderType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, ast));
 		}
 
 		// Generate abstract self() and build() methods in the abstract builder.
@@ -327,9 +329,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		
 		if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
 		
-		if ((td.modifiers & ClassFileConstants.AccAbstract) != 0 && !toBuilder) {
+		boolean isAbstract = (td.modifiers & ClassFileConstants.AccAbstract) != 0;
+		if (isAbstract) {
 			// Only non-abstract classes get the Builder implementation.
-			// However, if we want to generate a toBuilder, we need a helper function in the builder implementation class.
 			return;
 		}
 		
@@ -343,9 +345,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		}
 
 		if (toBuilder) {
-			// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
-			injectMethod(builderImplType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, ast));
-			
+			// Add the toBuilder() method to the annotated class.
 			switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) {
 			case EXISTS_BY_USER:
 				annotationNode.addWarning("Not generating toBuilder() as it already exists.");
@@ -355,10 +355,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 			default:
 				// Should not happen.
 			}
-			if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) {
-				// The rest of the builder implementation class is not necessary for abstract classes.
-				return;
-			}
 		}
 
 		// Create the self() and build() methods in the BuilderImpl.
@@ -605,7 +601,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 	 * }
 	 * </pre>
 	 */
-	private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, String builderImplClassName, TypeParameter[] typeParams) {
+	private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, String builderClassName, TypeParameter[] typeParams) {
 		MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
 		out.selector = FILL_VALUES_METHOD_NAME;
 		out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
@@ -629,7 +625,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 
 		// Call the builder implemention's helper method that actually fills the values from the instance.
 		MessageSend callStaticFillValuesMethod = new MessageSend();
-		callStaticFillValuesMethod.receiver = new SingleNameReference(builderImplClassName.toCharArray(), 0);
+		callStaticFillValuesMethod.receiver = new SingleNameReference(builderClassName.toCharArray(), 0);
 		callStaticFillValuesMethod.selector = FILL_VALUES_STATIC_METHOD_NAME;
 		callStaticFillValuesMethod.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0), new ThisReference(0, 0)};
 		body.add(callStaticFillValuesMethod);
diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index f858cd29..f9686cba 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -242,7 +242,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		
 		if (toBuilder) {
 			// Generate $fillValuesFrom() method in the abstract builder.
-			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderImplClassName));
+			injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderClassName));
+			// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
+			injectMethod(builderType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields));
 		}
 
 		// Generate abstract self() and build() methods in the abstract builder.
@@ -272,9 +274,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
 		
 		boolean isAbstract = (td.mods.flags & Flags.ABSTRACT) != 0;
-		if (!isAbstract || toBuilder) {
+		if (!isAbstract) {
 			// Only non-abstract classes get the Builder implementation.
-			// However, if we want to generate a toBuilder, we need a helper function in the builder implementation class.
 			
 			// Create the builder implementation class.
 			JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName);
@@ -285,20 +286,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 				return;
 			}
 
-			if (toBuilder) {
-				// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
-				injectMethod(builderImplType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields));
-			}
+			// Create a simple constructor for the BuilderImpl class.
+			JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), builderImplType, List.<JavacNode>nil(), false, annotationNode);
+			if (cd != null) injectMethod(builderImplType, cd);
 			
-			if (!isAbstract) {
-				// Create a simple constructor for the BuilderImpl class.
-				JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), builderImplType, List.<JavacNode>nil(), false, annotationNode);
-				if (cd != null) injectMethod(builderImplType, cd);
-				
-				// Create the self() and build() methods in the BuilderImpl.
-				injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams));
-				injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions));
-			}
+			// Create the self() and build() methods in the BuilderImpl.
+			injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams));
+			injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions));
 			
 			recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext());
 		}
@@ -307,18 +301,20 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
 				superclassBuilderClassExpression != null);
 		
-		if (!isAbstract) {
-			// Only non-abstract classes get the builder() method.
+		if (isAbstract) {
+			// Only non-abstract classes get the builder() and toBuilder() methods.
+			return;
+		}
 			
-			// Add the builder() method to the annotated class.
-			// Allow users to specify their own builder() methods, e.g., to provide default values.
-			if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
-				JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams);
-				recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext());
-				if (builderMethod != null) injectMethod(tdParent, builderMethod);
-			}
+		// Add the builder() method to the annotated class.
+		// Allow users to specify their own builder() methods, e.g., to provide default values.
+		if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
+			JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams);
+			recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext());
+			if (builderMethod != null) injectMethod(tdParent, builderMethod);
 		}
 
+		// Add the toBuilder() method to the annotated class.
 		if (toBuilder) {
 			switch (methodExists(TO_BUILDER_METHOD_NAME, tdParent, 0)) {
 			case EXISTS_BY_USER:
diff --git a/test/transform/resource/after-delombok/SuperBuilderAbstractToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderAbstractToBuilder.java
new file mode 100644
index 00000000..096bd533
--- /dev/null
+++ b/test/transform/resource/after-delombok/SuperBuilderAbstractToBuilder.java
@@ -0,0 +1,168 @@
+public class SuperBuilderAbstractToBuilder {
+	public static class Parent {
+		int parentField;
+		@java.lang.SuppressWarnings("all")
+		public static abstract class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
+			@java.lang.SuppressWarnings("all")
+			private int parentField;
+			@java.lang.SuppressWarnings("all")
+			protected B $fillValuesFrom(final C instance) {
+				ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+				return self();
+			}
+			@java.lang.SuppressWarnings("all")
+			private static void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
+				b.parentField(instance.parentField);
+			}
+			@java.lang.SuppressWarnings("all")
+			protected abstract B self();
+			@java.lang.SuppressWarnings("all")
+			public abstract C build();
+			@java.lang.SuppressWarnings("all")
+			public B parentField(final int parentField) {
+				this.parentField = parentField;
+				return self();
+			}
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			public java.lang.String toString() {
+				return "SuperBuilderAbstractToBuilder.Parent.ParentBuilder(parentField=" + this.parentField + ")";
+			}
+		}
+		@java.lang.SuppressWarnings("all")
+		private static final class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
+			@java.lang.SuppressWarnings("all")
+			private ParentBuilderImpl() {
+			}
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			protected ParentBuilderImpl self() {
+				return this;
+			}
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			public Parent build() {
+				return new Parent(this);
+			}
+		}
+		@java.lang.SuppressWarnings("all")
+		protected Parent(final ParentBuilder<?, ?> b) {
+			this.parentField = b.parentField;
+		}
+		@java.lang.SuppressWarnings("all")
+		public static ParentBuilder<?, ?> builder() {
+			return new ParentBuilderImpl();
+		}
+		@java.lang.SuppressWarnings("all")
+		public ParentBuilder<?, ?> toBuilder() {
+			return new ParentBuilderImpl().$fillValuesFrom(this);
+		}
+	}
+	public static abstract class Child extends Parent {
+		double childField;
+		@java.lang.SuppressWarnings("all")
+		public static abstract class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
+			@java.lang.SuppressWarnings("all")
+			private double childField;
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			protected B $fillValuesFrom(final C instance) {
+				super.$fillValuesFrom(instance);
+				ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+				return self();
+			}
+			@java.lang.SuppressWarnings("all")
+			private static void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
+				b.childField(instance.childField);
+			}
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			protected abstract B self();
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			public abstract C build();
+			@java.lang.SuppressWarnings("all")
+			public B childField(final double childField) {
+				this.childField = childField;
+				return self();
+			}
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			public java.lang.String toString() {
+				return "SuperBuilderAbstractToBuilder.Child.ChildBuilder(super=" + super.toString() + ", childField=" + this.childField + ")";
+			}
+		}
+		@java.lang.SuppressWarnings("all")
+		protected Child(final ChildBuilder<?, ?> b) {
+			super(b);
+			this.childField = b.childField;
+		}
+	}
+	public static class GrandChild extends Child {
+		String grandChildField;
+		@java.lang.SuppressWarnings("all")
+		public static abstract class GrandChildBuilder<C extends GrandChild, B extends GrandChildBuilder<C, B>> extends Child.ChildBuilder<C, B> {
+			@java.lang.SuppressWarnings("all")
+			private String grandChildField;
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			protected B $fillValuesFrom(final C instance) {
+				super.$fillValuesFrom(instance);
+				GrandChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+				return self();
+			}
+			@java.lang.SuppressWarnings("all")
+			private static void $fillValuesFromInstanceIntoBuilder(final GrandChild instance, final GrandChildBuilder<?, ?> b) {
+				b.grandChildField(instance.grandChildField);
+			}
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			protected abstract B self();
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			public abstract C build();
+			@java.lang.SuppressWarnings("all")
+			public B grandChildField(final String grandChildField) {
+				this.grandChildField = grandChildField;
+				return self();
+			}
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			public java.lang.String toString() {
+				return "SuperBuilderAbstractToBuilder.GrandChild.GrandChildBuilder(super=" + super.toString() + ", grandChildField=" + this.grandChildField + ")";
+			}
+		}
+		@java.lang.SuppressWarnings("all")
+		private static final class GrandChildBuilderImpl extends GrandChildBuilder<GrandChild, GrandChildBuilderImpl> {
+			@java.lang.SuppressWarnings("all")
+			private GrandChildBuilderImpl() {
+			}
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			protected GrandChildBuilderImpl self() {
+				return this;
+			}
+			@java.lang.Override
+			@java.lang.SuppressWarnings("all")
+			public GrandChild build() {
+				return new GrandChild(this);
+			}
+		}
+		@java.lang.SuppressWarnings("all")
+		protected GrandChild(final GrandChildBuilder<?, ?> b) {
+			super(b);
+			this.grandChildField = b.grandChildField;
+		}
+		@java.lang.SuppressWarnings("all")
+		public static GrandChildBuilder<?, ?> builder() {
+			return new GrandChildBuilderImpl();
+		}
+		@java.lang.SuppressWarnings("all")
+		public GrandChildBuilder<?, ?> toBuilder() {
+			return new GrandChildBuilderImpl().$fillValuesFrom(this);
+		}
+	}
+	public static void test() {
+		GrandChild x = GrandChild.builder().grandChildField("").parentField(5).childField(2.5).build().toBuilder().build();
+	}
+}
diff --git a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
index 7a80f019..dc0b4e70 100644
--- a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
@@ -26,10 +26,18 @@ public class SuperBuilderBasicToBuilder {
 			private java.util.ArrayList<String> items;
 			@java.lang.SuppressWarnings("all")
 			protected B $fillValuesFrom(final C instance) {
-				ParentBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this);
+				ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
 				return self();
 			}
 			@java.lang.SuppressWarnings("all")
+			private static void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
+				b.field1(instance.field1);
+				b.obtainViaField(instance.field1);
+				b.obtainViaMethod(instance.method());
+				b.obtainViaStaticMethod(Parent.staticMethod(instance));
+				b.items(instance.items == null ? java.util.Collections.emptyList() : instance.items);
+			}
+			@java.lang.SuppressWarnings("all")
 			protected abstract B self();
 			@java.lang.SuppressWarnings("all")
 			public abstract C build();
@@ -78,14 +86,6 @@ public class SuperBuilderBasicToBuilder {
 		}
 		@java.lang.SuppressWarnings("all")
 		private static final class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
-			@java.lang.SuppressWarnings("all")
-			private static void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
-				b.field1(instance.field1);
-				b.obtainViaField(instance.field1);
-				b.obtainViaMethod(instance.method());
-				b.obtainViaStaticMethod(Parent.staticMethod(instance));
-				b.items(instance.items == null ? java.util.Collections.emptyList() : instance.items);
-			}
 			@java.lang.SuppressWarnings("all")
 			private ParentBuilderImpl() {
 			}
@@ -138,9 +138,13 @@ public class SuperBuilderBasicToBuilder {
 			@java.lang.SuppressWarnings("all")
 			protected B $fillValuesFrom(final C instance) {
 				super.$fillValuesFrom(instance);
-				ChildBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this);
+				ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
 				return self();
 			}
+			@java.lang.SuppressWarnings("all")
+			private static void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
+				b.field3(instance.field3);
+			}
 			@java.lang.Override
 			@java.lang.SuppressWarnings("all")
 			protected abstract B self();
@@ -160,10 +164,6 @@ public class SuperBuilderBasicToBuilder {
 		}
 		@java.lang.SuppressWarnings("all")
 		private static final class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
-			@java.lang.SuppressWarnings("all")
-			private static void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
-				b.field3(instance.field3);
-			}
 			@java.lang.SuppressWarnings("all")
 			private ChildBuilderImpl() {
 			}
diff --git a/test/transform/resource/after-ecj/SuperBuilderAbstractToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderAbstractToBuilder.java
new file mode 100644
index 00000000..668f6acf
--- /dev/null
+++ b/test/transform/resource/after-ecj/SuperBuilderAbstractToBuilder.java
@@ -0,0 +1,131 @@
+public class SuperBuilderAbstractToBuilder {
+  public static @lombok.experimental.SuperBuilder(toBuilder = true) class Parent {
+    public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
+      private @java.lang.SuppressWarnings("all") int parentField;
+      public ParentBuilder() {
+        super();
+      }
+      protected @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
+        ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+        return self();
+      }
+      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
+        b.parentField(instance.parentField);
+      }
+      protected abstract @java.lang.SuppressWarnings("all") B self();
+      public abstract @java.lang.SuppressWarnings("all") C build();
+      public @java.lang.SuppressWarnings("all") B parentField(final int parentField) {
+        this.parentField = parentField;
+        return self();
+      }
+      public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+        return (("SuperBuilderAbstractToBuilder.Parent.ParentBuilder(parentField=" + this.parentField) + ")");
+      }
+    }
+    private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
+      private ParentBuilderImpl() {
+        super();
+      }
+      protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() {
+        return this;
+      }
+      public @java.lang.Override @java.lang.SuppressWarnings("all") Parent build() {
+        return new Parent(this);
+      }
+    }
+    int parentField;
+    protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder<?, ?> b) {
+      super();
+      this.parentField = b.parentField;
+    }
+    public @java.lang.SuppressWarnings("all") ParentBuilder<?, ?> toBuilder() {
+      return new ParentBuilderImpl().$fillValuesFrom(this);
+    }
+    public static @java.lang.SuppressWarnings("all") ParentBuilder<?, ?> builder() {
+      return new ParentBuilderImpl();
+    }
+  }
+  public static abstract @lombok.experimental.SuperBuilder(toBuilder = true) class Child extends Parent {
+    public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
+      private @java.lang.SuppressWarnings("all") double childField;
+      public ChildBuilder() {
+        super();
+      }
+      protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
+        super.$fillValuesFrom(instance);
+        ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+        return self();
+      }
+      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
+        b.childField(instance.childField);
+      }
+      protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
+      public abstract @java.lang.Override @java.lang.SuppressWarnings("all") C build();
+      public @java.lang.SuppressWarnings("all") B childField(final double childField) {
+        this.childField = childField;
+        return self();
+      }
+      public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+        return (((("SuperBuilderAbstractToBuilder.Child.ChildBuilder(super=" + super.toString()) + ", childField=") + this.childField) + ")");
+      }
+    }
+    double childField;
+    protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder<?, ?> b) {
+      super(b);
+      this.childField = b.childField;
+    }
+  }
+  public static @lombok.experimental.SuperBuilder(toBuilder = true) class GrandChild extends Child {
+    public static abstract @java.lang.SuppressWarnings("all") class GrandChildBuilder<C extends GrandChild, B extends GrandChildBuilder<C, B>> extends Child.ChildBuilder<C, B> {
+      private @java.lang.SuppressWarnings("all") String grandChildField;
+      public GrandChildBuilder() {
+        super();
+      }
+      protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
+        super.$fillValuesFrom(instance);
+        GrandChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+        return self();
+      }
+      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final GrandChild instance, final GrandChildBuilder<?, ?> b) {
+        b.grandChildField(instance.grandChildField);
+      }
+      protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
+      public abstract @java.lang.Override @java.lang.SuppressWarnings("all") C build();
+      public @java.lang.SuppressWarnings("all") B grandChildField(final String grandChildField) {
+        this.grandChildField = grandChildField;
+        return self();
+      }
+      public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+        return (((("SuperBuilderAbstractToBuilder.GrandChild.GrandChildBuilder(super=" + super.toString()) + ", grandChildField=") + this.grandChildField) + ")");
+      }
+    }
+    private static final @java.lang.SuppressWarnings("all") class GrandChildBuilderImpl extends GrandChildBuilder<GrandChild, GrandChildBuilderImpl> {
+      private GrandChildBuilderImpl() {
+        super();
+      }
+      protected @java.lang.Override @java.lang.SuppressWarnings("all") GrandChildBuilderImpl self() {
+        return this;
+      }
+      public @java.lang.Override @java.lang.SuppressWarnings("all") GrandChild build() {
+        return new GrandChild(this);
+      }
+    }
+    String grandChildField;
+    protected @java.lang.SuppressWarnings("all") GrandChild(final GrandChildBuilder<?, ?> b) {
+      super(b);
+      this.grandChildField = b.grandChildField;
+    }
+    public @java.lang.SuppressWarnings("all") GrandChildBuilder<?, ?> toBuilder() {
+      return new GrandChildBuilderImpl().$fillValuesFrom(this);
+    }
+    public static @java.lang.SuppressWarnings("all") GrandChildBuilder<?, ?> builder() {
+      return new GrandChildBuilderImpl();
+    }
+  }
+  public SuperBuilderAbstractToBuilder() {
+    super();
+  }
+  public static void test() {
+    GrandChild x = GrandChild.builder().grandChildField("").parentField(5).childField(2.5).build().toBuilder().build();
+  }
+}
diff --git a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
index be819d05..a491430a 100644
--- a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
+++ b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
@@ -11,9 +11,16 @@ public class SuperBuilderBasicToBuilder {
         super();
       }
       protected @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
-        ParentBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this);
+        ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
         return self();
       }
+      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
+        b.field1(instance.field1);
+        b.obtainViaField(instance.field1);
+        b.obtainViaMethod(instance.method());
+        b.obtainViaStaticMethod(Parent.staticMethod(instance));
+        b.items(((instance.items == null) ? java.util.Collections.emptyList() : instance.items));
+      }
       protected abstract @java.lang.SuppressWarnings("all") B self();
       public abstract @java.lang.SuppressWarnings("all") C build();
       public @java.lang.SuppressWarnings("all") B field1(final int field1) {
@@ -57,13 +64,6 @@ public class SuperBuilderBasicToBuilder {
       private ParentBuilderImpl() {
         super();
       }
-      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
-        b.field1(instance.field1);
-        b.obtainViaField(instance.field1);
-        b.obtainViaMethod(instance.method());
-        b.obtainViaStaticMethod(Parent.staticMethod(instance));
-        b.items(((instance.items == null) ? java.util.Collections.emptyList() : instance.items));
-      }
       protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() {
         return this;
       }
@@ -116,9 +116,12 @@ public class SuperBuilderBasicToBuilder {
       }
       protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
         super.$fillValuesFrom(instance);
-        ChildBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this);
+        ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
         return self();
       }
+      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
+        b.field3(instance.field3);
+      }
       protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
       public abstract @java.lang.Override @java.lang.SuppressWarnings("all") C build();
       public @java.lang.SuppressWarnings("all") B field3(final double field3) {
@@ -133,9 +136,6 @@ public class SuperBuilderBasicToBuilder {
       private ChildBuilderImpl() {
         super();
       }
-      private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
-        b.field3(instance.field3);
-      }
       protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() {
         return this;
       }
diff --git a/test/transform/resource/before/SuperBuilderAbstractToBuilder.java b/test/transform/resource/before/SuperBuilderAbstractToBuilder.java
new file mode 100644
index 00000000..3359829c
--- /dev/null
+++ b/test/transform/resource/before/SuperBuilderAbstractToBuilder.java
@@ -0,0 +1,20 @@
+public class SuperBuilderAbstractToBuilder {
+	@lombok.experimental.SuperBuilder(toBuilder = true)
+	public static class Parent {
+		int parentField;
+	}
+	
+	@lombok.experimental.SuperBuilder(toBuilder = true)
+	public abstract static class Child extends Parent {
+		double childField;
+	}
+	
+	@lombok.experimental.SuperBuilder(toBuilder = true)
+	public static class GrandChild extends Child {
+		String grandChildField;
+	}
+	
+	public static void test() {
+		GrandChild x = GrandChild.builder().grandChildField("").parentField(5).childField(2.5).build().toBuilder().build();
+	}
+}
-- 
cgit 


From 68318742ef2e4f6b680ea90a279c2c96da7c2d74 Mon Sep 17 00:00:00 2001
From: Jan Rieke <rieke@subshell.com>
Date: Fri, 21 Sep 2018 11:25:55 +0200
Subject: SuperBuilder toBuilder with generics (javac)

---
 src/core/lombok/javac/handlers/HandleSuperBuilder.java  | 17 +++++++----------
 .../SuperBuilderWithGenericsAndToBuilder.java           |  4 ++--
 2 files changed, 9 insertions(+), 12 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index f9686cba..a440c9be 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -109,7 +109,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		JavacNode tdParent = annotationNode.up();
 		
 		java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
-		JCExpression returnType;
 		List<JCTypeParameter> typeParams = List.nil();
 		List<JCExpression> thrownExceptions = List.nil();
 		List<JCExpression> superclassTypeParams = List.nil();
@@ -187,7 +186,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		// If there is no superclass, superclassBuilderClassExpression is still == null at this point.
 		// You can use it to check whether to inherit or not.
 		
-		returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
 		typeParams = td.typarams;
 		
 		// <C, B> are the generics for our builder.
@@ -292,7 +290,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 			
 			// Create the self() and build() methods in the BuilderImpl.
 			injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams));
-			injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions));
+			injectMethod(builderImplType, generateBuildMethod(buildMethodName, tdParent, builderImplType, thrownExceptions));
 			
 			recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext());
 		}
@@ -548,7 +546,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		typeParameterNames.add(wildcard);
 		JCTypeApply returnType = maker.TypeApply(maker.Ident(type.toName(builderClassName)), typeParameterNames.toList());
 		
-		return maker.MethodDef(maker.Modifiers(modifiers), type.toName(TO_BUILDER_METHOD_NAME), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+		return maker.MethodDef(maker.Modifiers(modifiers), type.toName(TO_BUILDER_METHOD_NAME), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
 	}
 
 	/**
@@ -618,8 +616,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		JCExpression returnType = maker.TypeIdent(CTC_VOID);
 		
 		// 1st parameter: "Foobar instance"
-		JCExpression classGenericNameExpr = maker.Ident(type.toName(type.getName()));
-		JCVariableDecl paramInstance = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(INSTANCE_VARIABLE_NAME), classGenericNameExpr, null);
+		JCVariableDecl paramInstance = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(INSTANCE_VARIABLE_NAME), cloneSelfType(type), null);
 
 		// 2nd parameter: "FoobarBuilder<?, ?> b" (plus generics on the annotated type)
 		// First add all generics that are present on the parent type.
@@ -642,7 +639,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		
 		JCBlock bodyBlock = maker.Block(0, body.toList());
 
-		return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.of(paramInstance, paramBuilder), List.<JCExpression>nil(), bodyBlock, null);
+		return maker.MethodDef(modifiers, name, returnType, copyTypeParams(type, typeParams), List.of(paramInstance, paramBuilder), List.<JCExpression>nil(), bodyBlock, null);
 	}
 	
 	private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData bfd, JavacNode type, JavacTreeMaker maker) {
@@ -720,7 +717,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null);
 	}
 
-	private JCMethodDecl generateBuildMethod(String buildName, JCExpression returnType, JavacNode type, List<JCExpression> thrownExceptions) {
+	private JCMethodDecl generateBuildMethod(String buildName, JavacNode returnType, JavacNode type, List<JCExpression> thrownExceptions) {
 		JavacTreeMaker maker = type.getTreeMaker();
 		
 		JCExpression call;
@@ -728,7 +725,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		
 		// Use a constructor that only has this builder as parameter.
 		List<JCExpression> builderArg = List.<JCExpression>of(maker.Ident(type.toName("this")));
-		call = maker.NewClass(null, List.<JCExpression>nil(), returnType, builderArg, null);
+		call = maker.NewClass(null, List.<JCExpression>nil(), cloneSelfType(returnType), builderArg, null);
 		statements.append(maker.Return(call));
 		
 		JCBlock body = maker.Block(0, statements.toList());
@@ -736,7 +733,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
 		JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil());
 		JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation));
 		
-		return maker.MethodDef(modifiers, type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
+		return maker.MethodDef(modifiers, type.toName(buildName), cloneSelfType(returnType), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
 	}
 	
 	private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
diff --git a/test/transform/resource/after-delombok/SuperBuilderWithGenericsAndToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderWithGenericsAndToBuilder.java
index 93d0f9b5..deb5a223 100644
--- a/test/transform/resource/after-delombok/SuperBuilderWithGenericsAndToBuilder.java
+++ b/test/transform/resource/after-delombok/SuperBuilderWithGenericsAndToBuilder.java
@@ -15,7 +15,7 @@ public class SuperBuilderWithGenericsAndToBuilder {
 				return self();
 			}
 			@java.lang.SuppressWarnings("all")
-			private static void <A> $fillValuesFromInstanceIntoBuilder(final Parent<A> instance, final ParentBuilder<A, ?, ?> b) {
+			private static <A> void $fillValuesFromInstanceIntoBuilder(final Parent<A> instance, final ParentBuilder<A, ?, ?> b) {
 				b.field1(instance.field1);
 				b.items(instance.items == null ? java.util.Collections.emptyList() : instance.items);
 			}
@@ -106,7 +106,7 @@ public class SuperBuilderWithGenericsAndToBuilder {
 				return self();
 			}
 			@java.lang.SuppressWarnings("all")
-			private static void <A> $fillValuesFromInstanceIntoBuilder(final Child<A> instance, final ChildBuilder<A, ?, ?> b) {
+			private static <A> void $fillValuesFromInstanceIntoBuilder(final Child<A> instance, final ChildBuilder<A, ?, ?> b) {
 				b.field3(instance.field3);
 			}
 			@java.lang.Override
-- 
cgit 


From 279ceaa72795ea7b427ab37535892e3c5cbde958 Mon Sep 17 00:00:00 2001
From: Jan Rieke <rieke@subshell.com>
Date: Mon, 24 Sep 2018 09:26:37 +0200
Subject: SuperBuilder toBuilder with generics (ecj)

---
 .../eclipse/handlers/HandleSuperBuilder.java       | 11 +++++-----
 .../SuperBuilderWithGenericsAndToBuilder.java      | 24 ++++++++--------------
 2 files changed, 15 insertions(+), 20 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index 640e5a71..a2ea11c6 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -572,9 +572,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		out.modifiers = ClassFileConstants.AccPublic;
 		out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
 		
-		// Add type params if there are any.
-		if (typeParams != null && typeParams.length > 0) out.typeParameters = copyTypeParams(typeParams, source);
-		
 		TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND) };
 		out.returnType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p);
 		
@@ -609,7 +606,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		if (inherited) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())};
 		out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0);
 		
-		TypeReference builderType = createTypeReferenceWithTypeParameters(classGenericName, typeParams);
+		TypeReference builderType = new SingleTypeReference(classGenericName.toCharArray(), 0);
 		out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, builderType, Modifier.FINAL)};
 
 		List<Statement> body = new ArrayList<Statement>();
@@ -662,7 +659,11 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)};
 		TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, 0);
 		Argument builderArgument = new Argument(BUILDER_VARIABLE_NAME, 0, builderType, Modifier.FINAL);
-		out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, new SingleTypeReference(tdParent.getName().toCharArray(), 0), Modifier.FINAL), builderArgument};
+		TypeReference parentArgument = new ParameterizedSingleTypeReference(tdParent.getName().toCharArray(), mergeToTypeReferences(typeParams, new TypeReference[0]), 0, 0);
+		out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, parentArgument, Modifier.FINAL), builderArgument};
+
+		// Add type params if there are any.
+		if (typeParams.length > 0) out.typeParameters = copyTypeParams(typeParams, source);
 
 		List<Statement> body = new ArrayList<Statement>();
 
diff --git a/test/transform/resource/after-ecj/SuperBuilderWithGenericsAndToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderWithGenericsAndToBuilder.java
index fcdadb3b..40e3bdca 100644
--- a/test/transform/resource/after-ecj/SuperBuilderWithGenericsAndToBuilder.java
+++ b/test/transform/resource/after-ecj/SuperBuilderWithGenericsAndToBuilder.java
@@ -1,12 +1,6 @@
 import java.util.List;
-
-import SuperBuilderBasicToBuilder.Child;
-import SuperBuilderBasicToBuilder.Parent;
-import SuperBuilderBasicToBuilder.Child.ChildBuilder;
-import SuperBuilderBasicToBuilder.Child.ChildBuilderImpl;
-import SuperBuilderBasicToBuilder.Parent.ParentBuilder;
 public class SuperBuilderWithGenericsAndToBuilder {
-  public static @lombok.experimental.SuperBuilder class Parent<A> {
+  public static @lombok.experimental.SuperBuilder(toBuilder = true) class Parent<A> {
     public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder<A, C extends Parent<A>, B extends ParentBuilder<A, C, B>> {
       private @java.lang.SuppressWarnings("all") A field1;
       private @java.lang.SuppressWarnings("all") java.util.ArrayList<String> items;
@@ -14,10 +8,10 @@ public class SuperBuilderWithGenericsAndToBuilder {
         super();
       }
       protected @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
-          ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
-          return self();
-        }
-      private static @java.lang.SuppressWarnings("all") void <A>$fillValuesFromInstanceIntoBuilder(final Parent<A> instance, final ParentBuilder<A, ?, ?> b) {
+        ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+        return self();
+      }
+      private static @java.lang.SuppressWarnings("all") <A>void $fillValuesFromInstanceIntoBuilder(final Parent<A> instance, final ParentBuilder<A, ?, ?> b) {
         b.field1(instance.field1);
         b.items(((instance.items == null) ? java.util.Collections.emptyList() : instance.items));
       }
@@ -77,14 +71,14 @@ public class SuperBuilderWithGenericsAndToBuilder {
       }
       this.items = items;
     }
-    public @java.lang.SuppressWarnings("all") <A>ParentBuilder<A, ?, ?> toBuilder() {
+    public @java.lang.SuppressWarnings("all") ParentBuilder<A, ?, ?> toBuilder() {
       return new ParentBuilderImpl<A>().$fillValuesFrom(this);
     }
     public static @java.lang.SuppressWarnings("all") <A>ParentBuilder<A, ?, ?> builder() {
       return new ParentBuilderImpl<A>();
     }
   }
-  public static @lombok.experimental.SuperBuilder class Child<A> extends Parent<A> {
+  public static @lombok.experimental.SuperBuilder(toBuilder = true) class Child<A> extends Parent<A> {
     public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder<A, C extends Child<A>, B extends ChildBuilder<A, C, B>> extends Parent.ParentBuilder<A, C, B> {
       private @java.lang.SuppressWarnings("all") double field3;
       public ChildBuilder() {
@@ -95,7 +89,7 @@ public class SuperBuilderWithGenericsAndToBuilder {
         ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
         return self();
       }
-      private static @java.lang.SuppressWarnings("all") void <A>$fillValuesFromInstanceIntoBuilder(final Child<A> instance, final ChildBuilder<A, <d?, ?> b) {
+      private static @java.lang.SuppressWarnings("all") <A>void $fillValuesFromInstanceIntoBuilder(final Child<A> instance, final ChildBuilder<A, ?, ?> b) {
         b.field3(instance.field3);
       }
       protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
@@ -131,7 +125,7 @@ public class SuperBuilderWithGenericsAndToBuilder {
       return new ChildBuilderImpl<A>();
     }
   }
-  public SuperBuilderWithGenerics() {
+  public SuperBuilderWithGenericsAndToBuilder() {
     super();
   }
   public static void test() {
-- 
cgit 


From fd4c9d4bff6e75b30a3ee247edafaabc6888a691 Mon Sep 17 00:00:00 2001
From: Jan Rieke <rieke@subshell.com>
Date: Mon, 24 Sep 2018 09:32:07 +0200
Subject: SuperBuilder toBuilder fix with no generics (ecj)

---
 src/core/lombok/eclipse/handlers/HandleSuperBuilder.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index a2ea11c6..8f353cce 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -659,7 +659,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
 		TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)};
 		TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, 0);
 		Argument builderArgument = new Argument(BUILDER_VARIABLE_NAME, 0, builderType, Modifier.FINAL);
-		TypeReference parentArgument = new ParameterizedSingleTypeReference(tdParent.getName().toCharArray(), mergeToTypeReferences(typeParams, new TypeReference[0]), 0, 0);
+		TypeReference parentArgument = createTypeReferenceWithTypeParameters(tdParent.getName(), typeParams);
 		out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, parentArgument, Modifier.FINAL), builderArgument};
 
 		// Add type params if there are any.
-- 
cgit 


From afec417115abadd58c9d94a845b6c300e7e1b96f Mon Sep 17 00:00:00 2001
From: Jan Rieke <rieke@subshell.com>
Date: Mon, 24 Sep 2018 16:49:03 +0200
Subject: make BuilderFieldData package-private, so that SuperBuilder can use
 it

---
 src/core/lombok/eclipse/handlers/HandleBuilder.java | 2 +-
 src/core/lombok/javac/handlers/HandleBuilder.java   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index f6e0a175..09765efc 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -105,7 +105,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
 		return ((Boolean) expr).booleanValue();
 	}
 	
-	public static class BuilderFieldData {
+	static class BuilderFieldData {
 		Annotation[] annotations;
 		TypeReference type;
 		char[] rawName;
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index bf260644..bc1a8072 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -84,7 +84,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
 		return ((Boolean) expr).booleanValue();
 	}
 	
-	public static class BuilderFieldData {
+	static class BuilderFieldData {
 		List<JCAnnotation> annotations;
 		JCExpression type;
 		Name rawName;
-- 
cgit