aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@zwitserloot.com>2018-07-03 05:00:28 +0200
committerReinier Zwitserloot <reinier@zwitserloot.com>2018-07-03 05:42:34 +0200
commit7472672f164460cb8fb45ce941b685f358435374 (patch)
treeb95c8230d88861376059d20eecc3c53ea53f9f4f
parent3987f54b8321ae666cb1c774aef5986df05bf4ad (diff)
downloadlombok-7472672f164460cb8fb45ce941b685f358435374.tar.gz
lombok-7472672f164460cb8fb45ce941b685f358435374.tar.bz2
lombok-7472672f164460cb8fb45ce941b685f358435374.zip
[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.
-rw-r--r--src/core/lombok/eclipse/handlers/HandleConstructor.java130
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java2
-rw-r--r--test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults.java61
-rw-r--r--test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults2.java60
4 files changed, 221 insertions, 32 deletions
diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java
index cab847e6..6c7b4caf 100644
--- a/src/core/lombok/eclipse/handlers/HandleConstructor.java
+++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java
@@ -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);
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index fa887974..bb495fbc 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -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;
}
diff --git a/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults.java b/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults.java
new file mode 100644
index 00000000..81d59a37
--- /dev/null
+++ b/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults.java
@@ -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;
+ }
+}
diff --git a/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults2.java b/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults2.java
new file mode 100644
index 00000000..6094e2f4
--- /dev/null
+++ b/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults2.java
@@ -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();
+ }
+} \ No newline at end of file