[fixes #2046] you can now suppress the builder() method, useful if you only want toBuilder(). Also suppresses the warnings about any missing Builder.Default annotations.

This commit is contained in:
Reinier Zwitserloot
2019-03-25 23:11:48 +01:00
parent 535e20972c
commit 228e99fe52
8 changed files with 118 additions and 7 deletions
+1
View File
@@ -3,6 +3,7 @@ Lombok Changelog
### v1.18.7 "Edgy Guinea Pig"
* BUGFIX: var/val on methods that return an intersection type would now work in Eclipse [Issue #1986](https://github.com/rzwitserloot/lombok/issues/1986)
* FEATURE: You can now suppress generation of the `builder` method when using `@Builder`; usually because you're only interested in the `toBuilder` method. As a convenience we won't emit warnings about missing `@Builder.Default` annotations when you do this. [Issue #2046](https://github.com/rzwitserloot/lombok/issues/2046)
### v1.18.6 (February 12th, 2019)
* FEATURE: Javadoc on fields will now also be copied to the Builders' setters. Thanks for the contribution, Emil Lundberg. [Issue #2008](https://github.com/rzwitserloot/lombok/issues/2008)
@@ -173,7 +173,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (buildMethodName == null) builderMethodName = "build";
if (builderClassName == null) builderClassName = "";
if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
boolean generateBuilderMethod;
if (builderMethodName.isEmpty()) {
generateBuilderMethod = false;
} else if (!checkName("builderMethodName", builderMethodName, annotationNode)) {
return;
} else {
generateBuilderMethod = true;
}
if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
if (!builderClassName.isEmpty()) {
if (!checkName("builderClassName", builderClassName, annotationNode)) return;
@@ -192,6 +200,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
boolean addCleaning = false;
boolean isStatic = true;
List<EclipseNode> nonFinalNonDefaultedFields = null;
if (parent.get() instanceof TypeDeclaration) {
tdParent = parent;
TypeDeclaration td = (TypeDeclaration) tdParent.get();
@@ -225,7 +235,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (fd.initialization != null && isDefault == null) {
if (isFinal) continue;
fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.");
if (nonFinalNonDefaultedFields == null) nonFinalNonDefaultedFields = new ArrayList<EclipseNode>();
nonFinalNonDefaultedFields.add(fieldNode);
}
if (isDefault != null) {
@@ -486,7 +497,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (cleanMethod != null) injectMethod(builderType, cleanMethod);
}
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false;
if (generateBuilderMethod) {
MethodDeclaration md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast);
if (md != null) injectMethod(tdParent, md);
}
@@ -508,6 +520,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (md != null) injectMethod(tdParent, md);
}
if (nonFinalNonDefaultedFields != null && generateBuilderMethod) {
for (EclipseNode fieldNode : nonFinalNonDefaultedFields) {
fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.");
}
}
}
private static final char[] BUILDER_TEMP_VAR = {'b', 'u', 'i', 'l', 'd', 'e', 'r'};
@@ -119,7 +119,15 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (buildMethodName == null) buildMethodName = "build";
if (builderClassName == null) builderClassName = "";
if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
boolean generateBuilderMethod;
if (builderMethodName.isEmpty()) {
generateBuilderMethod = false;
} else if (!checkName("builderMethodName", builderMethodName, annotationNode)) {
return;
} else {
generateBuilderMethod = true;
}
if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
if (!builderClassName.isEmpty()) {
if (!checkName("builderClassName", builderClassName, annotationNode)) return;
@@ -140,6 +148,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
boolean addCleaning = false;
boolean isStatic = true;
ArrayList<JavacNode> nonFinalNonDefaultedFields = null;
if (parent.get() instanceof JCClassDecl) {
tdParent = parent;
JCClassDecl td = (JCClassDecl) tdParent.get();
@@ -172,7 +182,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (fd.init != null && isDefault == null) {
if (isFinal) continue;
fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.");
if (nonFinalNonDefaultedFields == null) nonFinalNonDefaultedFields = new ArrayList<JavacNode>();
nonFinalNonDefaultedFields.add(fieldNode);
}
if (isDefault != null) {
@@ -431,7 +442,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false;
if (generateBuilderMethod) {
JCMethodDecl md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, annotationNode, tdParent, typeParams);
recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
if (md != null) injectMethod(tdParent, md);
@@ -459,6 +471,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
}
}
if (nonFinalNonDefaultedFields != null && generateBuilderMethod) {
for (JavacNode fieldNode : nonFinalNonDefaultedFields) {
fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.");
}
}
}
private static String unpack(JCExpression expr) {
@@ -0,0 +1,33 @@
class BuilderWithNoBuilderMethod {
private String a = "";
@java.lang.SuppressWarnings("all")
BuilderWithNoBuilderMethod(final String a) {
this.a = a;
}
@java.lang.SuppressWarnings("all")
public static class BuilderWithNoBuilderMethodBuilder {
@java.lang.SuppressWarnings("all")
private String a;
@java.lang.SuppressWarnings("all")
BuilderWithNoBuilderMethodBuilder() {
}
@java.lang.SuppressWarnings("all")
public BuilderWithNoBuilderMethodBuilder a(final String a) {
this.a = a;
return this;
}
@java.lang.SuppressWarnings("all")
public BuilderWithNoBuilderMethod build() {
return new BuilderWithNoBuilderMethod(a);
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
public java.lang.String toString() {
return "BuilderWithNoBuilderMethod.BuilderWithNoBuilderMethodBuilder(a=" + this.a + ")";
}
}
@java.lang.SuppressWarnings("all")
public BuilderWithNoBuilderMethodBuilder toBuilder() {
return new BuilderWithNoBuilderMethodBuilder().a(this.a);
}
}
@@ -0,0 +1,27 @@
import lombok.Builder;
@Builder(toBuilder = true,builderMethodName = "") class BuilderWithNoBuilderMethod {
public static @java.lang.SuppressWarnings("all") class BuilderWithNoBuilderMethodBuilder {
private @java.lang.SuppressWarnings("all") String a;
@java.lang.SuppressWarnings("all") BuilderWithNoBuilderMethodBuilder() {
super();
}
public @java.lang.SuppressWarnings("all") BuilderWithNoBuilderMethodBuilder a(final String a) {
this.a = a;
return this;
}
public @java.lang.SuppressWarnings("all") BuilderWithNoBuilderMethod build() {
return new BuilderWithNoBuilderMethod(a);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
return (("BuilderWithNoBuilderMethod.BuilderWithNoBuilderMethodBuilder(a=" + this.a) + ")");
}
}
private String a = "";
@java.lang.SuppressWarnings("all") BuilderWithNoBuilderMethod(final String a) {
super();
this.a = a;
}
public @java.lang.SuppressWarnings("all") BuilderWithNoBuilderMethodBuilder toBuilder() {
return new BuilderWithNoBuilderMethodBuilder().a(this.a);
}
}
@@ -0,0 +1,5 @@
import lombok.Builder;
@Builder(toBuilder = true, builderMethodName = "")
class BuilderWithNoBuilderMethod {
private String a = "";
}
@@ -1,4 +1,4 @@
13 @Builder.Default requires @Builder on the class for it to mean anything.
6 @Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.
8 @Builder.Default requires an initializing expression (' = something;').
9 @Builder.Default and @Singular cannot be mixed.
6 @Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.
+9
View File
@@ -10,6 +10,8 @@
<code>@Builder</code> with <code>@Singular</code> adds a clear method since lombok v1.16.8.
</p><p>
<code>@Builder.Default</code> functionality was added in lombok v1.16.16.
</p><p>
<code>@Builder(builderMethodName = "")</code> is legal (and will suppress generation of the builder method) starting with lombok v1.18.8.
</p>
</@f.history>
@@ -183,6 +185,13 @@ public class JacksonExample {
The initializer on a <code>@Builder.Default</code> field is removed and stored in a static method, in order to guarantee that this initializer won't be executed at all if a value is specified in the build. This does mean the initializer cannot refer to <code>this</code>, <code>super</code> or any non-static member. If lombok generates a constructor for you, it'll also initialize this field with the initializer.
</p><p>
Various well known annotations about nullity cause null checks to be inserted and will be copied to parameter of the builder's 'setter' method. See <a href="/features/GetterSetter">Getter/Setter</a> documentation's small print for more information.
</p><p>
You can suppress the generation of the <code>builder()</code> method, for example because you <em>just</em> want the <code>toBuilder()</code> functionality, by using:
<code>@Builder(builderMethodName = "")</code>. Any warnings about missing <code>@Builder.Default</code> annotations will disappear when you do this, as such warnings
are not relevant when only using <code>toBuilder()</code> to make builder instances.
</p><p>
You can use <code>@Builder</code> for copy constructors: <code>foo.toBuilder().build()</code> makes a shallow clone. Consider suppressing the generating of the
<code>builder</code> method if you just want this functionality, by using: <code>@Builder(toBuilder = true, builderMethodName = "")</code>.
</p><p>
Due to a peculiar way javac processes static imports, trying to do a non-star static import of the static <code>builder()</code> method won't work. Either use a star static import: `import static TypeThatHasABuilder.*;` or don't statically import the <code>builder</code> method.
</p>