mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-14 12:16:49 +00:00
[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:
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();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user