aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/changelog.markdown1
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleBuilder.java24
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java24
-rw-r--r--test/transform/resource/after-delombok/BuilderWithNoBuilderMethod.java33
-rw-r--r--test/transform/resource/after-ecj/BuilderWithNoBuilderMethod.java27
-rw-r--r--test/transform/resource/before/BuilderWithNoBuilderMethod.java5
-rw-r--r--test/transform/resource/messages-delombok/BuilderDefaultsWarnings.java.messages2
-rw-r--r--website/templates/features/Builder.html9
8 files changed, 118 insertions, 7 deletions
diff --git a/doc/changelog.markdown b/doc/changelog.markdown
index 17326a58..4f8d203e 100644
--- a/doc/changelog.markdown
+++ b/doc/changelog.markdown
@@ -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)
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index 3391b99d..3adbc27c 100755
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -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'};
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index 609adbd6..2ef2fac2 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -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) {
diff --git a/test/transform/resource/after-delombok/BuilderWithNoBuilderMethod.java b/test/transform/resource/after-delombok/BuilderWithNoBuilderMethod.java
new file mode 100644
index 00000000..35e2c79e
--- /dev/null
+++ b/test/transform/resource/after-delombok/BuilderWithNoBuilderMethod.java
@@ -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);
+ }
+}
diff --git a/test/transform/resource/after-ecj/BuilderWithNoBuilderMethod.java b/test/transform/resource/after-ecj/BuilderWithNoBuilderMethod.java
new file mode 100644
index 00000000..624b14b9
--- /dev/null
+++ b/test/transform/resource/after-ecj/BuilderWithNoBuilderMethod.java
@@ -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);
+ }
+}
diff --git a/test/transform/resource/before/BuilderWithNoBuilderMethod.java b/test/transform/resource/before/BuilderWithNoBuilderMethod.java
new file mode 100644
index 00000000..3f2b21ac
--- /dev/null
+++ b/test/transform/resource/before/BuilderWithNoBuilderMethod.java
@@ -0,0 +1,5 @@
+import lombok.Builder;
+@Builder(toBuilder = true, builderMethodName = "")
+class BuilderWithNoBuilderMethod {
+ private String a = "";
+}
diff --git a/test/transform/resource/messages-delombok/BuilderDefaultsWarnings.java.messages b/test/transform/resource/messages-delombok/BuilderDefaultsWarnings.java.messages
index 694511f8..c6590132 100644
--- a/test/transform/resource/messages-delombok/BuilderDefaultsWarnings.java.messages
+++ b/test/transform/resource/messages-delombok/BuilderDefaultsWarnings.java.messages
@@ -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.
diff --git a/website/templates/features/Builder.html b/website/templates/features/Builder.html
index 082b97ed..0818f9d2 100644
--- a/website/templates/features/Builder.html
+++ b/website/templates/features/Builder.html
@@ -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>
@@ -184,6 +186,13 @@ public class JacksonExample {
</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>
</@f.smallPrint>