[issue #1347] When lombok generates constructors, it should call the @Builder.Default static method instead of initializing to null/0/false. This does that, for ecj.

This commit is contained in:
Reinier Zwitserloot committed 2018-07-03 05:42:34 +02:00
1 parent 3987f54b83
commit 7472672f16
4 files changed
+221 -32

No files matched your search

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2017 The Project Lombok Authors.
* Copyright (C) 2010-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
@@ -64,6 +64,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.FloatLiteral;
import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
import org.eclipse.jdt.internal.compiler.ast.LongLiteral;
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.QualifiedTypeReference;
@@ -96,10 +97,9 @@ public class HandleConstructor {
boolean force = ann.force();
List<EclipseNode> fields = force ? findFinalFields(typeNode) : Collections.<EclipseNode>emptyList();
List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode);
handleConstructor.generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode);
handleConstructor.generateConstructor(typeNode, level, Collections.<EclipseNode>emptyList(), force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode);
}
}
@@ -132,10 +132,6 @@ public class HandleConstructor {
return findFields(typeNode, true);
}
private static List<EclipseNode> findFinalFields(EclipseNode typeNode) {
return findFields(typeNode, false);
}
private static List<EclipseNode> findFields(EclipseNode typeNode, boolean nullMarked) {
List<EclipseNode> fields = new ArrayList<EclipseNode>();
for (EclipseNode child : typeNode.down()) {
@@ -215,9 +211,8 @@ public class HandleConstructor {
Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.NO_ARGS_CONSTRUCTOR_EXTRA_PRIVATE);
if (v == null || !v) return;
List<EclipseNode> fields = findFinalFields(typeNode);
generate(typeNode, AccessLevel.PRIVATE, fields, true, null, SkipIfConstructorExists.NO, Collections.<Annotation>emptyList(), sourceNode, true);
generate(typeNode, AccessLevel.PRIVATE, Collections.<EclipseNode>emptyList(), true, null, SkipIfConstructorExists.NO, Collections.<Annotation>emptyList(), sourceNode, true);
}
public void generateRequiredArgsConstructor(
@@ -235,14 +230,14 @@ public class HandleConstructor {
}
public void generateConstructor(
EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists,
EclipseNode typeNode, AccessLevel level, List<EclipseNode> fieldsToParam, boolean forceDefaults, String staticName, SkipIfConstructorExists skipIfConstructorExists,
List<Annotation> onConstructor, EclipseNode sourceNode) {
generate(typeNode, level, fields, allToDefault, staticName, skipIfConstructorExists, onConstructor, sourceNode, false);
generate(typeNode, level, fieldsToParam, forceDefaults, staticName, skipIfConstructorExists, onConstructor, sourceNode, false);
}
public void generate(
EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists,
EclipseNode typeNode, AccessLevel level, List<EclipseNode> fieldsToParam, boolean forceDefaults, String staticName, SkipIfConstructorExists skipIfConstructorExists,
List<Annotation> onConstructor, EclipseNode sourceNode, boolean noArgs) {
ASTNode source = sourceNode.get();
@@ -279,11 +274,11 @@ public class HandleConstructor {
if (noArgs && noArgsConstructorExists(typeNode)) return;
ConstructorDeclaration constr = createConstructor(
staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault,
staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fieldsToParam, forceDefaults,
sourceNode, onConstructor);
injectMethod(typeNode, constr);
if (staticConstrRequired) {
MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.<EclipseNode>emptyList() : fields, source);
MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, fieldsToParam, source);
injectMethod(typeNode, staticConstr);
}
}
@@ -342,8 +337,16 @@ public class HandleConstructor {
return new Annotation[] { ann };
}
private static final char[] DEFAULT_PREFIX = {'$', 'd', 'e', 'f', 'a', 'u', 'l', 't', '$'};
private static final char[] prefixWith(char[] prefix, char[] name) {
char[] out = new char[prefix.length + name.length];
System.arraycopy(prefix, 0, out, 0, prefix.length);
System.arraycopy(name, 0, out, prefix.length, name.length);
return out;
}
@SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor(
AccessLevel level, EclipseNode type, Collection<EclipseNode> fields, boolean allToDefault,
AccessLevel level, EclipseNode type, Collection<EclipseNode> fieldsToParam, boolean forceDefaults,
EclipseNode sourceNode, List<Annotation> onConstructor) {
ASTNode source = sourceNode.get();
@@ -354,8 +357,11 @@ public class HandleConstructor {
if (isEnum) level = AccessLevel.PRIVATE;
List<EclipseNode> fieldsToDefault = fieldsNeedingBuilderDefaults(type, fieldsToParam);
List<EclipseNode> fieldsToExplicit = forceDefaults ? fieldsNeedingExplicitDefaults(type, fieldsToParam) : Collections.<EclipseNode>emptyList();
boolean addConstructorProperties;
if (fields.isEmpty()) {
if (fieldsToParam.isEmpty()) {
addConstructorProperties = false;
} else {
Boolean v = type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES);
@@ -381,7 +387,7 @@ public class HandleConstructor {
List<Statement> assigns = new ArrayList<Statement>();
List<Statement> nullChecks = new ArrayList<Statement>();
for (EclipseNode fieldNode : fields) {
for (EclipseNode fieldNode : fieldsToParam) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
char[] rawName = field.name;
char[] fieldName = removePrefixFromField(fieldNode);
@@ -390,23 +396,55 @@ public class HandleConstructor {
int e = (int) p;
thisX.receiver = new ThisReference(s, e);
Expression assignmentExpr = allToDefault ? getDefaultExpr(field.type, s, e) : new SingleNameReference(fieldName, p);
Expression assignmentExpr = new SingleNameReference(fieldName, p);
Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p);
assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32);
assigns.add(assignment);
if (!allToDefault) {
long fieldPos = (((long) field.sourceStart) << 32) | field.sourceEnd;
Argument parameter = new Argument(fieldName, fieldPos, copyType(field.type, source), Modifier.FINAL);
Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN);
Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN);
if (nonNulls.length != 0) {
Statement nullCheck = generateNullCheck(parameter, sourceNode);
if (nullCheck != null) nullChecks.add(nullCheck);
}
parameter.annotations = copyAnnotations(source, nonNulls, nullables);
params.add(parameter);
long fieldPos = (((long) field.sourceStart) << 32) | field.sourceEnd;
Argument parameter = new Argument(fieldName, fieldPos, copyType(field.type, source), Modifier.FINAL);
Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN);
Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN);
if (nonNulls.length != 0) {
Statement nullCheck = generateNullCheck(parameter, sourceNode);
if (nullCheck != null) nullChecks.add(nullCheck);
}
parameter.annotations = copyAnnotations(source, nonNulls, nullables);
params.add(parameter);
}
for (EclipseNode fieldNode : fieldsToExplicit) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
char[] rawName = field.name;
FieldReference thisX = new FieldReference(rawName, p);
int s = (int) (p >> 32);
int e = (int) p;
thisX.receiver = new ThisReference(s, e);
Expression assignmentExpr = getDefaultExpr(field.type, s, e);
Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p);
assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32);
assigns.add(assignment);
}
for (EclipseNode fieldNode : fieldsToDefault) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
char[] rawName = field.name;
FieldReference thisX = new FieldReference(rawName, p);
int s = (int) (p >> 32);
int e = (int) p;
thisX.receiver = new ThisReference(s, e);
MessageSend inv = new MessageSend();
inv.sourceStart = source.sourceStart;
inv.sourceEnd = source.sourceEnd;
inv.receiver = new SingleNameReference(((TypeDeclaration) type.get()).name, 0L);
inv.selector = prefixWith(DEFAULT_PREFIX, removePrefixFromField(fieldNode));
Assignment assignment = new Assignment(thisX, inv, (int) p);
assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32);
assigns.add(assignment);
}
nullChecks.addAll(assigns);
@@ -415,8 +453,8 @@ public class HandleConstructor {
/* Generate annotations that must be put on the generated method, and attach them. */ {
Annotation[] constructorProperties = null;
if (!allToDefault && addConstructorProperties && !isLocalType(type)) {
constructorProperties = createConstructorProperties(source, fields);
if (addConstructorProperties && !isLocalType(type)) {
constructorProperties = createConstructorProperties(source, fieldsToParam);
}
constructor.annotations = copyAnnotations(source,
@@ -428,6 +466,34 @@ public class HandleConstructor {
return constructor;
}
private static List<EclipseNode> fieldsNeedingBuilderDefaults(EclipseNode type, Collection<EclipseNode> fieldsToParam) {
List<EclipseNode> out = new ArrayList<EclipseNode>();
top:
for (EclipseNode node : type.down()) {
if (node.getKind() != Kind.FIELD) continue top;
FieldDeclaration fd = (FieldDeclaration) node.get();
if ((fd.modifiers & ClassFileConstants.AccStatic) != 0) continue top;
for (EclipseNode ftp : fieldsToParam) if (node == ftp) continue top;
if (EclipseHandlerUtil.hasAnnotation(Builder.Default.class, node)) out.add(node);
}
return out;
}
private static List<EclipseNode> fieldsNeedingExplicitDefaults(EclipseNode type, Collection<EclipseNode> fieldsToParam) {
List<EclipseNode> out = new ArrayList<EclipseNode>();
top:
for (EclipseNode node : type.down()) {
if (node.getKind() != Kind.FIELD) continue top;
FieldDeclaration fd = (FieldDeclaration) node.get();
if ((fd.modifiers & ClassFileConstants.AccFinal) == 0) continue top;
if ((fd.modifiers & ClassFileConstants.AccStatic) != 0) continue top;
for (EclipseNode ftp : fieldsToParam) if (node == ftp) continue top;
if (EclipseHandlerUtil.hasAnnotation(Builder.Default.class, node)) continue top;
out.add(node);
}
return out;
}
private static Expression getDefaultExpr(TypeReference type, int s, int e) {
boolean array = type instanceof ArrayTypeReference;
if (array) return new NullLiteral(s, e);
@@ -155,11 +155,13 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (bfd.singularData != null && isDefault != null) {
isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
findAnnotation(Builder.Default.class, fieldNode, true);
isDefault = null;
}
if (fd.init == null && isDefault != null) {
isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;').");
findAnnotation(Builder.Default.class, fieldNode, true);
isDefault = null;
}
@@ -0,0 +1,61 @@
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Value;
import lombok.Builder;
final @NoArgsConstructor @AllArgsConstructor @Builder @Value class ConstructorsWithBuilderDefaults {
public static @java.lang.SuppressWarnings("all") class ConstructorsWithBuilderDefaultsBuilder {
private @java.lang.SuppressWarnings("all") int x;
private @java.lang.SuppressWarnings("all") boolean x$set;
@java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder() {
super();
}
public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder x(final int x) {
this.x = x;
x$set = true;
return this;
}
public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults build() {
return new ConstructorsWithBuilderDefaults((x$set ? x : ConstructorsWithBuilderDefaults.$default$x()));
}
public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
return (("ConstructorsWithBuilderDefaults.ConstructorsWithBuilderDefaultsBuilder(x=" + this.x) + ")");
}
}
private final @Builder.Default int x;
private static @java.lang.SuppressWarnings("all") int $default$x() {
return 5;
}
public static @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder builder() {
return new ConstructorsWithBuilderDefaultsBuilder();
}
public @java.lang.SuppressWarnings("all") int getX() {
return this.x;
}
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
if ((o == this))
return true;
if ((! (o instanceof ConstructorsWithBuilderDefaults)))
return false;
final ConstructorsWithBuilderDefaults other = (ConstructorsWithBuilderDefaults) o;
if ((this.getX() != other.getX()))
return false;
return true;
}
public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
final int PRIME = 59;
int result = 1;
result = ((result * PRIME) + this.getX());
return result;
}
public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
return (("ConstructorsWithBuilderDefaults(x=" + this.getX()) + ")");
}
public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults() {
super();
this.x = ConstructorsWithBuilderDefaults.$default$x();
}
public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults(final int x) {
super();
this.x = x;
}
}
@@ -0,0 +1,60 @@
import lombok.NoArgsConstructor;
import lombok.Value;
import lombok.Builder;
final @Builder @Value class ConstructorsWithBuilderDefaults {
public static @java.lang.SuppressWarnings("all") class ConstructorsWithBuilderDefaultsBuilder {
private @java.lang.SuppressWarnings("all") int x;
private @java.lang.SuppressWarnings("all") boolean x$set;
@java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder() {
super();
}
public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder x(final int x) {
this.x = x;
x$set = true;
return this;
}
public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults build() {
return new ConstructorsWithBuilderDefaults((x$set ? x : ConstructorsWithBuilderDefaults.$default$x()));
}
public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
return (("ConstructorsWithBuilderDefaults.ConstructorsWithBuilderDefaultsBuilder(x=" + this.x) + ")");
}
}
private final @Builder.Default int x;
private static @java.lang.SuppressWarnings("all") int $default$x() {
return 5;
}
@java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults(final int x) {
super();
this.x = x;
}
public static @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder builder() {
return new ConstructorsWithBuilderDefaultsBuilder();
}
public @java.lang.SuppressWarnings("all") int getX() {
return this.x;
}
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
if ((o == this))
return true;
if ((! (o instanceof ConstructorsWithBuilderDefaults)))
return false;
final ConstructorsWithBuilderDefaults other = (ConstructorsWithBuilderDefaults) o;
if ((this.getX() != other.getX()))
return false;
return true;
}
public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
final int PRIME = 59;
int result = 1;
result = ((result * PRIME) + this.getX());
return result;
}
public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
return (("ConstructorsWithBuilderDefaults(x=" + this.getX()) + ")");
}
private @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults() {
super();
this.x = ConstructorsWithBuilderDefaults.$default$x();
}
}