Skip to content

Commit

Permalink
@SuperBuilder adapts @Builder.Default behavior from @builder as #1347 is
Browse files Browse the repository at this point in the history
fixed now (ecj)
  • Loading branch information
janrieke committed Aug 16, 2018
1 parent dbc173b commit 8439cb5
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 26 deletions.
53 changes: 36 additions & 17 deletions src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
Expand Up @@ -48,6 +48,7 @@
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.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;
Expand All @@ -60,6 +61,7 @@
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
Expand Down Expand Up @@ -95,6 +97,7 @@
public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray();
private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray();
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();

Expand All @@ -104,6 +107,7 @@ private static class BuilderFieldData {
TypeReference type;
char[] rawName;
char[] name;
char[] nameOfDefaultProvider;
char[] nameOfSetFlag;
SingularData singularData;
ObtainVia obtainVia;
Expand Down Expand Up @@ -177,16 +181,11 @@ public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, Ec
}

if (isDefault != null) {
bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name);
bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX);
// The @Builder annotation removes the initializing expression on the field and moves
// it to a method called "$default$FIELDNAME". This method is then called upon building.
// We do NOT do this, because this is unexpected and may lead to bugs when using other
// constructors (see, e.g., issue #1347).
// Instead, we keep the init expression and only set a new value in the builder-based
// constructor if it was set in the builder. Drawback is that the init expression is
// always executed, even if it was unnecessary because its value is overwritten by the
// builder.
// TODO: Once the issue is resolved in @Builder, we can adapt the solution here.

MethodDeclaration md = HandleBuilder.generateDefaultProvider(bfd.nameOfDefaultProvider, td.typeParameters, fieldNode, ast);
if (md != null) injectMethod(tdParent, md);
}
addObtainVia(bfd, fieldNode);
builderFields.add(bfd);
Expand Down Expand Up @@ -461,10 +460,10 @@ private void generateBuilderBasedConstructor(EclipseNode typeNode, TypeParameter

for (BuilderFieldData fieldNode : builderFields) {
char[] fieldName = removePrefixFromField(fieldNode.originalFieldNode);
FieldReference thisX = new FieldReference(fieldNode.rawName, p);
FieldReference fieldInThis = new FieldReference(fieldNode.rawName, p);
int s = (int) (p >> 32);
int e = (int) p;
thisX.receiver = new ThisReference(s, e);
fieldInThis.receiver = new ThisReference(s, e);

Expression assignmentExpr;
if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) {
Expand All @@ -475,16 +474,26 @@ private void generateBuilderBasedConstructor(EclipseNode typeNode, TypeParameter
long[] positions = new long[] {p, p};
assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e);
}
Statement assignment = new Assignment(thisX, assignmentExpr, (int) p);
Statement assignment = new Assignment(fieldInThis, assignmentExpr, (int) p);
statements.add(assignment);

// In case of @Builder.Default, only set the value if it really was set in the builder.
// In case of @Builder.Default, set the value to the default if it was NOT set in the builder.
if (fieldNode.nameOfSetFlag != null) {
char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldNode.nameOfSetFlag};
char[][] setVariableInBuilder = new char[][] {"b".toCharArray(), fieldNode.nameOfSetFlag};
long[] positions = new long[] {p, p};
QualifiedNameReference builderRef = new QualifiedNameReference(variableInBuilder, positions, s, e);
assignment = new IfStatement(builderRef, assignment, s, e);
QualifiedNameReference setVariableInBuilderRef = new QualifiedNameReference(setVariableInBuilder, positions, s, e);

MessageSend inv = new MessageSend();
inv.sourceStart = source.sourceStart;
inv.sourceEnd = source.sourceEnd;
inv.receiver = new SingleNameReference(((TypeDeclaration) typeNode.get()).name, 0L);
inv.selector = fieldNode.nameOfDefaultProvider;
inv.typeArguments = typeParameterNames(((TypeDeclaration) typeNode.get()).typeParameters);

assignment = new Assignment(fieldInThis, inv, (int) p);
IfStatement ifBlockForDefault = new IfStatement(new UnaryExpression(setVariableInBuilderRef, OperatorIds.NOT), assignment, s, e);
statements.add(ifBlockForDefault);
}
statements.add(assignment);

Annotation[] nonNulls = findAnnotations((FieldDeclaration)fieldNode.originalFieldNode.get(), NON_NULL_PATTERN);
if (nonNulls.length != 0) {
Expand Down Expand Up @@ -817,6 +826,16 @@ private TypeReference[] mergeTypeReferences(TypeReference[] refs1, TypeReference
return result;
}

private TypeReference[] typeParameterNames(TypeParameter[] typeParameters) {
if (typeParameters == null) return null;

TypeReference[] trs = new TypeReference[typeParameters.length];
for (int i = 0; i < trs.length; i++) {
trs[i] = new SingleTypeReference(typeParameters[i].name, 0);
}
return trs;
}

private EclipseNode findInnerClass(EclipseNode parent, String name) {
char[] c = name.toCharArray();
for (EclipseNode child : parent.down()) {
Expand Down
30 changes: 21 additions & 9 deletions test/transform/resource/after-ecj/SuperBuilderWithDefaults.java
Expand Up @@ -36,14 +36,22 @@ private ParentBuilderImpl() {
return new Parent<N>(this);
}
}
private @lombok.Builder.Default long millis = System.currentTimeMillis();
private @lombok.Builder.Default N numberField = null;
private @lombok.Builder.Default long millis;
private @lombok.Builder.Default N numberField;
private static @java.lang.SuppressWarnings("all") <N extends Number>long $default$millis() {
return System.currentTimeMillis();
}
private static @java.lang.SuppressWarnings("all") <N extends Number>N $default$numberField() {
return null;
}
protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder<N, ?, ?> b) {
super();
if (b.millis$set)
this.millis = b.millis;
if (b.numberField$set)
this.numberField = b.numberField;
this.millis = b.millis;
if ((! b.millis$set))
this.millis = Parent.<N>$default$millis();
this.numberField = b.numberField;
if ((! b.numberField$set))
this.numberField = Parent.<N>$default$numberField();
}
public static @java.lang.SuppressWarnings("all") <N extends Number>ParentBuilder<N, ?, ?> builder() {
return new ParentBuilderImpl<N>();
Expand Down Expand Up @@ -78,11 +86,15 @@ private ChildBuilderImpl() {
return new Child(this);
}
}
private @lombok.Builder.Default double doubleField = Math.PI;
private @lombok.Builder.Default double doubleField;
private static @java.lang.SuppressWarnings("all") double $default$doubleField() {
return Math.PI;
}
protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder<?, ?> b) {
super(b);
if (b.doubleField$set)
this.doubleField = b.doubleField;
this.doubleField = b.doubleField;
if ((! b.doubleField$set))
this.doubleField = Child.$default$doubleField();
}
public static @java.lang.SuppressWarnings("all") ChildBuilder<?, ?> builder() {
return new ChildBuilderImpl();
Expand Down

0 comments on commit 8439cb5

Please sign in to comment.