From 1b06f4aa2960ba18d26a4d0218dd486872176971 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Thu, 26 Mar 2020 00:32:51 +0100 Subject: [PATCH 01/87] [SuperBuilder] fix IndexOutOfBounds (fixes #2407) --- src/core/lombok/eclipse/handlers/HandleSuperBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 82af39fa..436cc6a0 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -242,7 +242,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { String superclassBuilderClassName = builderClassNameTemplate.replace("*", superclassClassName); char[][] tokens = Arrays.copyOf(qualifiedTypeReference.tokens, qualifiedTypeReference.tokens.length + 1); - tokens[tokens.length] = superclassBuilderClassName.toCharArray(); + tokens[tokens.length-1] = superclassBuilderClassName.toCharArray(); long[] poss = new long[tokens.length]; Arrays.fill(poss, p); From 736904298202bb75c27243f930c9e48bf2139be9 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Thu, 26 Mar 2020 23:21:44 +0100 Subject: [PATCH 02/87] [SuperBuilder] test qualified type references in extends clause --- test/transform/resource/after-delombok/SuperBuilderBasic.java | 4 ++-- test/transform/resource/after-ecj/SuperBuilderBasic.java | 4 ++-- test/transform/resource/before/SuperBuilderBasic.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/transform/resource/after-delombok/SuperBuilderBasic.java b/test/transform/resource/after-delombok/SuperBuilderBasic.java index c347e41c..c3fa86f2 100644 --- a/test/transform/resource/after-delombok/SuperBuilderBasic.java +++ b/test/transform/resource/after-delombok/SuperBuilderBasic.java @@ -81,10 +81,10 @@ public class SuperBuilderBasic { return new SuperBuilderBasic.Parent.ParentBuilderImpl(); } } - public static class Child extends Parent { + public static class Child extends SuperBuilderBasic.Parent { double field3; @java.lang.SuppressWarnings("all") - public static abstract class ChildBuilder> extends Parent.ParentBuilder { + public static abstract class ChildBuilder> extends SuperBuilderBasic.Parent.ParentBuilder { @java.lang.SuppressWarnings("all") private double field3; @java.lang.Override diff --git a/test/transform/resource/after-ecj/SuperBuilderBasic.java b/test/transform/resource/after-ecj/SuperBuilderBasic.java index b47f318d..7902c95f 100644 --- a/test/transform/resource/after-ecj/SuperBuilderBasic.java +++ b/test/transform/resource/after-ecj/SuperBuilderBasic.java @@ -71,8 +71,8 @@ public class SuperBuilderBasic { return new SuperBuilderBasic.Parent.ParentBuilderImpl(); } } - public static @lombok.experimental.SuperBuilder class Child extends Parent { - public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder> extends Parent.ParentBuilder { + public static @lombok.experimental.SuperBuilder class Child extends SuperBuilderBasic.Parent { + public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder> extends SuperBuilderBasic.Parent.ParentBuilder { private @java.lang.SuppressWarnings("all") double field3; public ChildBuilder() { super(); diff --git a/test/transform/resource/before/SuperBuilderBasic.java b/test/transform/resource/before/SuperBuilderBasic.java index f4e8c670..99d7284e 100644 --- a/test/transform/resource/before/SuperBuilderBasic.java +++ b/test/transform/resource/before/SuperBuilderBasic.java @@ -8,7 +8,7 @@ public class SuperBuilderBasic { } @lombok.experimental.SuperBuilder - public static class Child extends Parent { + public static class Child extends SuperBuilderBasic.Parent { double field3; } From 2566345e6108037c63383ec0287b773989d9ed33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Kalm=C3=A1r?= Date: Sat, 11 Apr 2020 14:42:11 +0200 Subject: [PATCH 03/87] Encode + sign properly. Fixes #1539 --- src/launch/lombok/launch/ShadowClassLoader.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java index 30ca6e97..da377ae4 100644 --- a/src/launch/lombok/launch/ShadowClassLoader.java +++ b/src/launch/lombok/launch/ShadowClassLoader.java @@ -321,8 +321,9 @@ class ShadowClassLoader extends ClassLoader { } private static String urlDecode(String in) { + final String plusFixed = in.replaceAll("\\+", "%2B"); try { - return URLDecoder.decode(in, "UTF-8"); + return URLDecoder.decode(plusFixed, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new InternalError("UTF-8 not supported"); } From 91cd859b72c89a47bbe145c00a9e7c1b20a5e657 Mon Sep 17 00:00:00 2001 From: Rawi01 Date: Tue, 14 Apr 2020 09:15:31 +0200 Subject: [PATCH 04/87] [fixes #2286] Set source range for vararg arrays --- .../lombok/eclipse/agent/EclipsePatcher.java | 11 +++++++++++ src/eclipseAgent/lombok/launch/PatchFixesHider.java | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java index 756c23fa..95d42c2c 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java @@ -103,6 +103,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { patchIdentifierEndReparse(sm); patchRetrieveEllipsisStartPosition(sm); patchRetrieveRightBraceOrSemiColonPosition(sm); + patchRetrieveProperRightBracketPosition(sm); patchSetGeneratedFlag(sm); patchDomAstReparseIssues(sm); patchHideGeneratedNodes(sm); @@ -445,6 +446,16 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { // .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build()); } + private static void patchRetrieveProperRightBracketPosition(ScriptManager sm) { + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "extractSubArrayType", "org.eclipse.jdt.core.dom.ArrayType", "org.eclipse.jdt.core.dom.ArrayType", "int", "int")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveProperRightBracketPosition", "int", "int", "int")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveProperRightBracketPosition", "int", "int", "org.eclipse.jdt.core.dom.ArrayType")) + .requestExtra(StackRequest.PARAM1) + .transplant() + .build()); + } + private static void patchSetGeneratedFlag(ScriptManager sm) { sm.addScript(ScriptBuilder.addField() .targetClass("org.eclipse.jdt.internal.compiler.ast.ASTNode") diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java index 563beab1..deab0be1 100755 --- a/src/eclipseAgent/lombok/launch/PatchFixesHider.java +++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java @@ -38,6 +38,7 @@ import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.ArrayType; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.search.SearchMatch; @@ -517,6 +518,12 @@ final class PatchFixesHider { return -1; } + public static int fixRetrieveProperRightBracketPosition(int retVal, ArrayType arrayType) { + if (retVal != -1 || arrayType == null) return retVal; + if (isGenerated(arrayType)) return arrayType.getStartPosition() + arrayType.getLength() - 1; + return -1; + } + public static final int ALREADY_PROCESSED_FLAG = 0x800000; //Bit 24 public static boolean checkBit24(Object node) throws Exception { From d34fdeb3b4b44aae8c03cee0d3b1dd35a3d74e31 Mon Sep 17 00:00:00 2001 From: Rawi01 Date: Tue, 14 Apr 2020 23:15:50 +0200 Subject: [PATCH 05/87] [fixes #2246] Add null check --- .../lombok/eclipse/handlers/EclipseHandlerUtil.java | 8 +++++--- .../resource/after-delombok/TypeUseAnnotations.java | 10 ++++++++++ .../resource/after-ecj/TypeUseAnnotations.java | 9 +++++++++ test/transform/resource/before/TypeUseAnnotations.java | 2 ++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 4df7a90b..2c6e1e77 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -660,9 +660,11 @@ public class EclipseHandlerUtil { Annotation[][] b = new Annotation[a.length][]; for (int i = 0; i < a.length; i++) { - b[i] = new Annotation[a[i].length]; - for (int j = 0 ; j < a[i].length; j++) { - b[i][j] = copyAnnotation(a[i][j], a[i][j]); + if (a[i] != null) { + b[i] = new Annotation[a[i].length]; + for (int j = 0 ; j < a[i].length; j++) { + b[i][j] = copyAnnotation(a[i][j], a[i][j]); + } } } diff --git a/test/transform/resource/after-delombok/TypeUseAnnotations.java b/test/transform/resource/after-delombok/TypeUseAnnotations.java index 1e1536bf..fbf32577 100644 --- a/test/transform/resource/after-delombok/TypeUseAnnotations.java +++ b/test/transform/resource/after-delombok/TypeUseAnnotations.java @@ -7,8 +7,18 @@ import java.util.List; } class TypeUseAnnotations { List<@TA(x = 5) String> foo; + List bar; + + class Inner { + } + @java.lang.SuppressWarnings("all") public List<@TA(x = 5) String> getFoo() { return this.foo; } + + @java.lang.SuppressWarnings("all") + public List getBar() { + return this.bar; + } } diff --git a/test/transform/resource/after-ecj/TypeUseAnnotations.java b/test/transform/resource/after-ecj/TypeUseAnnotations.java index 156643b9..7041b59e 100644 --- a/test/transform/resource/after-ecj/TypeUseAnnotations.java +++ b/test/transform/resource/after-ecj/TypeUseAnnotations.java @@ -5,11 +5,20 @@ import java.util.List; int x(); } class TypeUseAnnotations { + class Inner { + Inner() { + super(); + } + } @lombok.Getter List<@TA(x = 5) String> foo; + @lombok.Getter List bar; TypeUseAnnotations() { super(); } public @java.lang.SuppressWarnings("all") List<@TA(x = 5) String> getFoo() { return this.foo; } + public @java.lang.SuppressWarnings("all") List getBar() { + return this.bar; + } } \ No newline at end of file diff --git a/test/transform/resource/before/TypeUseAnnotations.java b/test/transform/resource/before/TypeUseAnnotations.java index c09a291d..7175930f 100644 --- a/test/transform/resource/before/TypeUseAnnotations.java +++ b/test/transform/resource/before/TypeUseAnnotations.java @@ -8,4 +8,6 @@ import java.util.List; } class TypeUseAnnotations { @lombok.Getter List<@TA(x=5) String> foo; + @lombok.Getter List bar; + class Inner { } } From dede79bc224eb16566a027f83214c04e065b575b Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 6 Apr 2020 21:43:58 +0200 Subject: [PATCH 06/87] copy more Jackson annotation to the builder, also for @Singular methods --- .../lombok/core/handlers/HandlerUtil.java | 9 +- .../eclipse/handlers/EclipseHandlerUtil.java | 37 +++- .../singulars/EclipseGuavaSingularizer.java | 12 +- .../EclipseJavaUtilListSetSingularizer.java | 12 +- .../EclipseJavaUtilMapSingularizer.java | 8 +- .../javac/handlers/HandleJacksonized.java | 1 - .../javac/handlers/JavacHandlerUtil.java | 18 +- .../javac/handlers/JavacSingularsRecipes.java | 16 +- .../jackson/annotation/JsonAnySetter.java | 12 ++ .../JacksonBuilderSingular.java | 177 ++++++++++++++++++ .../after-ecj/JacksonBuilderSingular.java | 164 ++++++++++++++++ .../before/JacksonBuilderSingular.java | 31 +++ 12 files changed, 476 insertions(+), 21 deletions(-) create mode 100644 test/stubs/com/fasterxml/jackson/annotation/JsonAnySetter.java create mode 100644 test/transform/resource/after-delombok/JacksonBuilderSingular.java create mode 100644 test/transform/resource/after-ecj/JacksonBuilderSingular.java create mode 100644 test/transform/resource/before/JacksonBuilderSingular.java diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 94fd21d9..a8d56041 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -76,7 +76,7 @@ public class HandlerUtil { return 43; } - public static final List NONNULL_ANNOTATIONS, BASE_COPYABLE_ANNOTATIONS, COPY_TO_SETTER_ANNOTATIONS, JACKSON_COPY_TO_BUILDER_ANNOTATIONS; + public static final List NONNULL_ANNOTATIONS, BASE_COPYABLE_ANNOTATIONS, COPY_TO_SETTER_ANNOTATIONS, COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS, JACKSON_COPY_TO_BUILDER_ANNOTATIONS; static { NONNULL_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] { "androidx.annotation.NonNull", @@ -314,6 +314,13 @@ public class HandlerUtil { COPY_TO_SETTER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] { "com.fasterxml.jackson.annotation.JsonProperty", "com.fasterxml.jackson.annotation.JsonSetter", + "com.fasterxml.jackson.annotation.JsonDeserialize", + "com.fasterxml.jackson.annotation.JsonIgnore", + "com.fasterxml.jackson.annotation.JacksonInject", + "com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty", + })); + COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] { + "com.fasterxml.jackson.annotation.JsonAnySetter", })); JACKSON_COPY_TO_BUILDER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] { "com.fasterxml.jackson.annotation.JsonFormat", diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 4df7a90b..d066dc0f 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -26,6 +26,7 @@ import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.EclipseAugments.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.EclipseReflectiveMembers.*; +import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -817,6 +818,20 @@ public class EclipseHandlerUtil { * Searches the given field node for annotations that are specifically intentioned to be copied to the setter. */ public static Annotation[] findCopyableToSetterAnnotations(EclipseNode node) { + return findAnnotationsInList(node, COPY_TO_SETTER_ANNOTATIONS); + } + + /** + * Searches the given field node for annotations that are specifically intentioned to be copied to the builder's singular method. + */ + public static Annotation[] findCopyableToBuilderSingularSetterAnnotations(EclipseNode node) { + return findAnnotationsInList(node, COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS); + } + + /** + * Searches the given field node for annotations that are in the given list, and returns those. + */ + private static Annotation[] findAnnotationsInList(EclipseNode node, java.util.List annotationsToFind) { AbstractVariableDeclaration avd = (AbstractVariableDeclaration) node.get(); if (avd.annotations == null) return EMPTY_ANNOTATIONS_ARRAY; List result = new ArrayList(); @@ -824,7 +839,7 @@ public class EclipseHandlerUtil { for (Annotation annotation : avd.annotations) { TypeReference typeRef = annotation.type; if (typeRef != null && typeRef.getTypeName() != null) { - for (String bn : COPY_TO_SETTER_ANNOTATIONS) if (typeMatches(bn, node, typeRef)) { + for (String bn : annotationsToFind) if (typeMatches(bn, node, typeRef)) { result.add(annotation); break; } @@ -2418,6 +2433,26 @@ public class EclipseHandlerUtil { return array == null ? null : array.clone(); } + public static T[] concat(T[] first, T[] second, Class type) { + if (first == null) + return second; + if (second == null) + return first; + if (first.length == 0) + return second; + if (second.length == 0) + return first; + T[] result = newArray(type, first.length + second.length); + System.arraycopy(first, 0, result, 0, first.length); + System.arraycopy(second, 0, result, first.length, second.length); + return result; + } + + @SuppressWarnings("unchecked") + private static T[] newArray(Class type, int length) { + return (T[]) Array.newInstance(type, length); + } + public static boolean isDirectDescendantOfObject(EclipseNode typeNode) { if (!(typeNode.get() instanceof TypeDeclaration)) throw new IllegalArgumentException("not a type node"); TypeDeclaration typeDecl = (TypeDeclaration) typeNode.get(); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index 392418ff..395d2e59 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java @@ -175,8 +175,10 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { md.returnType = returnType; char[] prefixedSingularName = data.getSetterPrefix().length == 0 ? data.getSingularName() : HandlerUtil.buildAccessorName(new String(data.getSetterPrefix()), new String(data.getSingularName())).toCharArray(); md.selector = fluent ? prefixedSingularName : HandlerUtil.buildAccessorName("add", new String(data.getSingularName())).toCharArray(); - md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); - + Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); + Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToBuilderSingularSetterAnnotations(data.getAnnotation().up())); + md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class); + if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md); data.setGeneratedByRecursive(md); HandleNonNull.INSTANCE.fix(injectMethod(builderType, md)); @@ -213,8 +215,10 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { md.returnType = returnType; char[] prefixedSelector = data.getSetterPrefix().length == 0 ? data.getPluralName() : HandlerUtil.buildAccessorName(new String(data.getSetterPrefix()), new String(data.getPluralName())).toCharArray(); md.selector = fluent ? prefixedSelector : HandlerUtil.buildAccessorName("addAll", new String(data.getPluralName())).toCharArray(); - md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); - + Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); + Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToSetterAnnotations(data.getAnnotation().up())); + md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class); + if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md); data.setGeneratedByRecursive(md); injectMethod(builderType, md); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java index 375f4e2c..deab4530 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java @@ -153,8 +153,10 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula md.returnType = returnType; char[] prefixedSingularName = data.getSetterPrefix().length == 0 ? data.getSingularName() : HandlerUtil.buildAccessorName(new String(data.getSetterPrefix()), new String(data.getSingularName())).toCharArray(); md.selector = fluent ? prefixedSingularName : HandlerUtil.buildAccessorName("add", new String(data.getSingularName())).toCharArray(); - md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); - + Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); + Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToBuilderSingularSetterAnnotations(data.getAnnotation().up())); + md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class); + if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md); data.setGeneratedByRecursive(md); HandleNonNull.INSTANCE.fix(injectMethod(builderType, md)); @@ -189,8 +191,10 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula md.returnType = returnType; char[] prefixedSelector = data.getSetterPrefix().length == 0 ? data.getPluralName() : HandlerUtil.buildAccessorName(new String(data.getSetterPrefix()), new String(data.getPluralName())).toCharArray(); md.selector = fluent ? prefixedSelector : HandlerUtil.buildAccessorName("addAll", new String(data.getPluralName())).toCharArray(); - md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); - + Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); + Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToSetterAnnotations(data.getAnnotation().up())); + md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class); + if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md); data.setGeneratedByRecursive(md); injectMethod(builderType, md); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index cb7d9ed6..1a40369d 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -252,7 +252,9 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer String setterName = HandlerUtil.buildAccessorName(setterPrefix, name); md.selector = setterName.toCharArray(); - md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); + Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); + Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToBuilderSingularSetterAnnotations(data.getAnnotation().up())); + md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class); if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md); data.setGeneratedByRecursive(md); @@ -326,7 +328,9 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer String setterName = HandlerUtil.buildAccessorName(setterPrefix, name); md.selector = setterName.toCharArray(); - md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); + Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); + Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToSetterAnnotations(data.getAnnotation().up())); + md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class); if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md); data.setGeneratedByRecursive(md); diff --git a/src/core/lombok/javac/handlers/HandleJacksonized.java b/src/core/lombok/javac/handlers/HandleJacksonized.java index aff0bf63..0aa02d1b 100644 --- a/src/core/lombok/javac/handlers/HandleJacksonized.java +++ b/src/core/lombok/javac/handlers/HandleJacksonized.java @@ -149,7 +149,6 @@ public class HandleJacksonized extends JavacAnnotationHandler { // @SuperBuilder? Make it package-private! if (superBuilderAnnotationNode != null) builderClass.mods.flags = builderClass.mods.flags & ~Flags.PRIVATE; - } private String getBuilderClassName(JCAnnotation ast, JavacNode annotationNode, JavacNode annotatedNode, JCClassDecl td, AnnotationValues builderAnnotation, JavacTreeMaker maker) { diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index a3e876c4..5241a209 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -1520,6 +1520,20 @@ public class JavacHandlerUtil { * Searches the given field node for annotations that are specifically intentioned to be copied to the setter. */ public static List findCopyableToSetterAnnotations(JavacNode node) { + return findAnnotationsInList(node, COPY_TO_SETTER_ANNOTATIONS); + } + + /** + * Searches the given field node for annotations that are specifically intentioned to be copied to the builder's singular method. + */ + public static List findCopyableToBuilderSingularSetterAnnotations(JavacNode node) { + return findAnnotationsInList(node, COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS); + } + + /** + * Searches the given field node for annotations that are in the given list, and returns those. + */ + private static List findAnnotationsInList(JavacNode node, java.util.List annotationsToFind) { JCAnnotation anno = null; String annoName = null; for (JavacNode child : node.down()) { @@ -1537,7 +1551,7 @@ public class JavacHandlerUtil { if (annoName == null) return List.nil(); if (!annoName.isEmpty()) { - for (String bn : COPY_TO_SETTER_ANNOTATIONS) if (typeMatches(bn, node, anno.annotationType)) return List.of(anno); + for (String bn : annotationsToFind) if (typeMatches(bn, node, anno.annotationType)) return List.of(anno); } ListBuffer result = new ListBuffer(); @@ -1545,7 +1559,7 @@ public class JavacHandlerUtil { if (child.getKind() == Kind.ANNOTATION) { JCAnnotation annotation = (JCAnnotation) child.get(); boolean match = false; - if (!match) for (String bn : COPY_TO_SETTER_ANNOTATIONS) if (typeMatches(bn, node, annotation.annotationType)) { + if (!match) for (String bn : annotationsToFind) if (typeMatches(bn, node, annotation.annotationType)) { result.append(annotation); break; } diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index ba052b5a..7cd52c8c 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -182,11 +182,12 @@ public class JavacSingularsRecipes { return this; } - protected JCModifiers makeMods(JavacTreeMaker maker, CheckerFrameworkVersion cfv, JavacNode node, boolean deprecate, AccessLevel access) { + protected JCModifiers makeMods(JavacTreeMaker maker, CheckerFrameworkVersion cfv, JavacNode node, boolean deprecate, AccessLevel access, List methodAnnotations) { JCAnnotation deprecateAnn = deprecate ? maker.Annotation(genJavaLangTypeRef(node, "Deprecated"), List.nil()) : null; JCAnnotation rrAnn = cfv.generateReturnsReceiver() ? maker.Annotation(genTypeRef(node, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.nil()) : null; List annsOnMethod = (deprecateAnn != null && rrAnn != null) ? List.of(deprecateAnn, rrAnn) : deprecateAnn != null ? List.of(deprecateAnn) : rrAnn != null ? List.of(rrAnn) : List.nil(); + annsOnMethod = mergeAnnotations(annsOnMethod,methodAnnotations); return maker.Modifiers(toJavacModifier(access), annsOnMethod); } @@ -271,10 +272,10 @@ public class JavacSingularsRecipes { generateClearMethod(cfv, deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, access); } - private void finishAndInjectMethod(CheckerFrameworkVersion cfv, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean deprecate, ListBuffer statements, Name methodName, List jcVariableDecls, AccessLevel access, Boolean ignoreNullCollections) { + private void finishAndInjectMethod(CheckerFrameworkVersion cfv, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean deprecate, ListBuffer statements, Name methodName, List jcVariableDecls, List methodAnnotations, AccessLevel access, Boolean ignoreNullCollections) { if (returnStatement != null) statements.append(returnStatement); JCBlock body = maker.Block(0, statements.toList()); - JCModifiers mods = makeMods(maker, cfv, builderType, deprecate, access); + JCModifiers mods = makeMods(maker, cfv, builderType, deprecate, access, methodAnnotations); List typeParams = List.nil(); List thrown = List.nil(); @@ -298,7 +299,7 @@ public class JavacSingularsRecipes { statements.add(clearStatement); Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString())); - finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, methodName, List.nil(), access, null); + finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, methodName, List.nil(), List.nil(), access, null); } protected abstract JCStatement generateClearStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType); @@ -312,7 +313,8 @@ public class JavacSingularsRecipes { if (!setterPrefix.isEmpty()) name = builderType.toName(HandlerUtil.buildAccessorName(setterPrefix, name.toString())); statements.prepend(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); - finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, access, null); + List methodAnnotations = copyAnnotations(findCopyableToBuilderSingularSetterAnnotations(data.annotation.up())); + finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, methodAnnotations, access, null); } protected JCVariableDecl generateSingularMethodParameter(int typeIndex, JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source, Name name) { @@ -357,8 +359,10 @@ public class JavacSingularsRecipes { } else { statements.prepend(JavacHandlerUtil.generateNullCheck(maker, null, data.getPluralName(), builderType, "%s cannot be null")); } + + List methodAnnotations = copyAnnotations(findCopyableToSetterAnnotations(data.annotation.up())); - finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, List.of(param), access, ignoreNullCollections); + finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, List.of(param), methodAnnotations, access, ignoreNullCollections); } protected ListBuffer generatePluralMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { diff --git a/test/stubs/com/fasterxml/jackson/annotation/JsonAnySetter.java b/test/stubs/com/fasterxml/jackson/annotation/JsonAnySetter.java new file mode 100644 index 00000000..51d2c3fa --- /dev/null +++ b/test/stubs/com/fasterxml/jackson/annotation/JsonAnySetter.java @@ -0,0 +1,12 @@ +package com.fasterxml.jackson.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface JsonAnySetter { + boolean enabled() default true; +} diff --git a/test/transform/resource/after-delombok/JacksonBuilderSingular.java b/test/transform/resource/after-delombok/JacksonBuilderSingular.java new file mode 100644 index 00000000..feaa6832 --- /dev/null +++ b/test/transform/resource/after-delombok/JacksonBuilderSingular.java @@ -0,0 +1,177 @@ +import java.util.List; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonBuilderSingular.JacksonBuilderSingularBuilder.class) +public class JacksonBuilderSingular { + @JsonAnySetter + private Map any; + @JsonProperty("v_a_l_u_e_s") + private List values; + @JsonAnySetter + private ImmutableMap guavaAny; + @JsonProperty("guava_v_a_l_u_e_s") + private ImmutableList guavaValues; + @java.lang.SuppressWarnings("all") + JacksonBuilderSingular(final Map any, final List values, final ImmutableMap guavaAny, final ImmutableList guavaValues) { + this.any = any; + this.values = values; + this.guavaAny = guavaAny; + this.guavaValues = guavaValues; + } + @java.lang.SuppressWarnings("all") + @com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build") + public static class JacksonBuilderSingularBuilder { + @java.lang.SuppressWarnings("all") + private java.util.ArrayList any$key; + @java.lang.SuppressWarnings("all") + private java.util.ArrayList any$value; + @java.lang.SuppressWarnings("all") + private java.util.ArrayList values; + @java.lang.SuppressWarnings("all") + private com.google.common.collect.ImmutableMap.Builder guavaAny; + @java.lang.SuppressWarnings("all") + private com.google.common.collect.ImmutableList.Builder guavaValues; + @java.lang.SuppressWarnings("all") + JacksonBuilderSingularBuilder() { + } + @JsonAnySetter + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder any(final String anyKey, final Object anyValue) { + if (this.any$key == null) { + this.any$key = new java.util.ArrayList(); + this.any$value = new java.util.ArrayList(); + } + this.any$key.add(anyKey); + this.any$value.add(anyValue); + return this; + } + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder any(final java.util.Map any) { + if (any == null) { + throw new java.lang.NullPointerException("any cannot be null"); + } + if (this.any$key == null) { + this.any$key = new java.util.ArrayList(); + this.any$value = new java.util.ArrayList(); + } + for (final java.util.Map.Entry $lombokEntry : any.entrySet()) { + this.any$key.add($lombokEntry.getKey()); + this.any$value.add($lombokEntry.getValue()); + } + return this; + } + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder clearAny() { + if (this.any$key != null) { + this.any$key.clear(); + this.any$value.clear(); + } + return this; + } + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder value(final String value) { + if (this.values == null) this.values = new java.util.ArrayList(); + this.values.add(value); + return this; + } + @JsonProperty("v_a_l_u_e_s") + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder values(final java.util.Collection values) { + if (values == null) { + throw new java.lang.NullPointerException("values cannot be null"); + } + if (this.values == null) this.values = new java.util.ArrayList(); + this.values.addAll(values); + return this; + } + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder clearValues() { + if (this.values != null) this.values.clear(); + return this; + } + @JsonAnySetter + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaAny(final String key, final Object value) { + if (this.guavaAny == null) this.guavaAny = com.google.common.collect.ImmutableMap.builder(); + this.guavaAny.put(key, value); + return this; + } + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaAny(final java.util.Map guavaAny) { + if (guavaAny == null) { + throw new java.lang.NullPointerException("guavaAny cannot be null"); + } + if (this.guavaAny == null) this.guavaAny = com.google.common.collect.ImmutableMap.builder(); + this.guavaAny.putAll(guavaAny); + return this; + } + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder clearGuavaAny() { + this.guavaAny = null; + return this; + } + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaValue(final String guavaValue) { + if (this.guavaValues == null) this.guavaValues = com.google.common.collect.ImmutableList.builder(); + this.guavaValues.add(guavaValue); + return this; + } + @JsonProperty("guava_v_a_l_u_e_s") + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaValues(final java.lang.Iterable guavaValues) { + if (guavaValues == null) { + throw new java.lang.NullPointerException("guavaValues cannot be null"); + } + if (this.guavaValues == null) this.guavaValues = com.google.common.collect.ImmutableList.builder(); + this.guavaValues.addAll(guavaValues); + return this; + } + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular.JacksonBuilderSingularBuilder clearGuavaValues() { + this.guavaValues = null; + return this; + } + @java.lang.SuppressWarnings("all") + public JacksonBuilderSingular build() { + java.util.Map any; + switch (this.any$key == null ? 0 : this.any$key.size()) { + case 0: + any = java.util.Collections.emptyMap(); + break; + case 1: + any = java.util.Collections.singletonMap(this.any$key.get(0), this.any$value.get(0)); + break; + default: + any = new java.util.LinkedHashMap(this.any$key.size() < 1073741824 ? 1 + this.any$key.size() + (this.any$key.size() - 3) / 3 : java.lang.Integer.MAX_VALUE); + for (int $i = 0; $i < this.any$key.size(); $i++) any.put(this.any$key.get($i), (Object) this.any$value.get($i)); + any = java.util.Collections.unmodifiableMap(any); + } + java.util.List values; + switch (this.values == null ? 0 : this.values.size()) { + case 0: + values = java.util.Collections.emptyList(); + break; + case 1: + values = java.util.Collections.singletonList(this.values.get(0)); + break; + default: + values = java.util.Collections.unmodifiableList(new java.util.ArrayList(this.values)); + } + com.google.common.collect.ImmutableMap guavaAny = this.guavaAny == null ? com.google.common.collect.ImmutableMap.of() : this.guavaAny.build(); + com.google.common.collect.ImmutableList guavaValues = this.guavaValues == null ? com.google.common.collect.ImmutableList.of() : this.guavaValues.build(); + return new JacksonBuilderSingular(any, values, guavaAny, guavaValues); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "JacksonBuilderSingular.JacksonBuilderSingularBuilder(any$key=" + this.any$key + ", any$value=" + this.any$value + ", values=" + this.values + ", guavaAny=" + this.guavaAny + ", guavaValues=" + this.guavaValues + ")"; + } + } + @java.lang.SuppressWarnings("all") + public static JacksonBuilderSingular.JacksonBuilderSingularBuilder builder() { + return new JacksonBuilderSingular.JacksonBuilderSingularBuilder(); + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/JacksonBuilderSingular.java b/test/transform/resource/after-ecj/JacksonBuilderSingular.java new file mode 100644 index 00000000..50f6a797 --- /dev/null +++ b/test/transform/resource/after-ecj/JacksonBuilderSingular.java @@ -0,0 +1,164 @@ +import java.util.List; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import lombok.Builder; +import lombok.Singular; +import lombok.extern.jackson.Jacksonized; +public @Jacksonized @Builder @com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonBuilderSingular.JacksonBuilderSingularBuilder.class) class JacksonBuilderSingular { + public static @java.lang.SuppressWarnings("all") @com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "",buildMethodName = "build") class JacksonBuilderSingularBuilder { + private @java.lang.SuppressWarnings("all") java.util.ArrayList any$key; + private @java.lang.SuppressWarnings("all") java.util.ArrayList any$value; + private @java.lang.SuppressWarnings("all") java.util.ArrayList values; + private @java.lang.SuppressWarnings("all") com.google.common.collect.ImmutableMap.Builder guavaAny; + private @java.lang.SuppressWarnings("all") com.google.common.collect.ImmutableList.Builder guavaValues; + @java.lang.SuppressWarnings("all") JacksonBuilderSingularBuilder() { + super(); + } + public @JsonAnySetter @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder any(final String anyKey, final Object anyValue) { + if ((this.any$key == null)) + { + this.any$key = new java.util.ArrayList(); + this.any$value = new java.util.ArrayList(); + } + this.any$key.add(anyKey); + this.any$value.add(anyValue); + return this; + } + public @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder any(final java.util.Map any) { + if ((any == null)) + { + throw new java.lang.NullPointerException("any cannot be null"); + } + if ((this.any$key == null)) + { + this.any$key = new java.util.ArrayList(); + this.any$value = new java.util.ArrayList(); + } + for (java.util.Map.Entry $lombokEntry : any.entrySet()) + { + this.any$key.add($lombokEntry.getKey()); + this.any$value.add($lombokEntry.getValue()); + } + return this; + } + public @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder clearAny() { + if ((this.any$key != null)) + { + this.any$key.clear(); + this.any$value.clear(); + } + return this; + } + public @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder value(final String value) { + if ((this.values == null)) + this.values = new java.util.ArrayList(); + this.values.add(value); + return this; + } + public @JsonProperty("v_a_l_u_e_s") @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder values(final java.util.Collection values) { + if ((values == null)) + { + throw new java.lang.NullPointerException("values cannot be null"); + } + if ((this.values == null)) + this.values = new java.util.ArrayList(); + this.values.addAll(values); + return this; + } + public @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder clearValues() { + if ((this.values != null)) + this.values.clear(); + return this; + } + public @JsonAnySetter @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaAny(final String key, final Object value) { + if ((this.guavaAny == null)) + this.guavaAny = com.google.common.collect.ImmutableMap.builder(); + this.guavaAny.put(key, value); + return this; + } + public @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaAny(final java.util.Map guavaAny) { + if ((guavaAny == null)) + { + throw new java.lang.NullPointerException("guavaAny cannot be null"); + } + if ((this.guavaAny == null)) + this.guavaAny = com.google.common.collect.ImmutableMap.builder(); + this.guavaAny.putAll(guavaAny); + return this; + } + public @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder clearGuavaAny() { + this.guavaAny = null; + return this; + } + public @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaValue(final String guavaValue) { + if ((this.guavaValues == null)) + this.guavaValues = com.google.common.collect.ImmutableList.builder(); + this.guavaValues.add(guavaValue); + return this; + } + public @JsonProperty("guava_v_a_l_u_e_s") @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaValues(final java.lang.Iterable guavaValues) { + if ((guavaValues == null)) + { + throw new java.lang.NullPointerException("guavaValues cannot be null"); + } + if ((this.guavaValues == null)) + this.guavaValues = com.google.common.collect.ImmutableList.builder(); + this.guavaValues.addAll(guavaValues); + return this; + } + public @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder clearGuavaValues() { + this.guavaValues = null; + return this; + } + public @java.lang.SuppressWarnings("all") JacksonBuilderSingular build() { + java.util.Map any; + switch (((this.any$key == null) ? 0 : this.any$key.size())) { + case 0 : + any = java.util.Collections.emptyMap(); + break; + case 1 : + any = java.util.Collections.singletonMap(this.any$key.get(0), this.any$value.get(0)); + break; + default : + any = new java.util.LinkedHashMap(((this.any$key.size() < 0x40000000) ? ((1 + this.any$key.size()) + ((this.any$key.size() - 3) / 3)) : java.lang.Integer.MAX_VALUE)); + for (int $i = 0;; ($i < this.any$key.size()); $i ++) + any.put(this.any$key.get($i), this.any$value.get($i)); + any = java.util.Collections.unmodifiableMap(any); + } + java.util.List values; + switch (((this.values == null) ? 0 : this.values.size())) { + case 0 : + values = java.util.Collections.emptyList(); + break; + case 1 : + values = java.util.Collections.singletonList(this.values.get(0)); + break; + default : + values = java.util.Collections.unmodifiableList(new java.util.ArrayList(this.values)); + } + com.google.common.collect.ImmutableMap guavaAny = ((this.guavaAny == null) ? com.google.common.collect.ImmutableMap.of() : this.guavaAny.build()); + com.google.common.collect.ImmutableList guavaValues = ((this.guavaValues == null) ? com.google.common.collect.ImmutableList.of() : this.guavaValues.build()); + return new JacksonBuilderSingular(any, values, guavaAny, guavaValues); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((((((((("JacksonBuilderSingular.JacksonBuilderSingularBuilder(any$key=" + this.any$key) + ", any$value=") + this.any$value) + ", values=") + this.values) + ", guavaAny=") + this.guavaAny) + ", guavaValues=") + this.guavaValues) + ")"); + } + } + private @JsonAnySetter @Singular("any") Map any; + private @JsonProperty("v_a_l_u_e_s") @Singular List values; + private @JsonAnySetter @Singular("guavaAny") ImmutableMap guavaAny; + private @JsonProperty("guava_v_a_l_u_e_s") @Singular ImmutableList guavaValues; + @java.lang.SuppressWarnings("all") JacksonBuilderSingular(final Map any, final List values, final ImmutableMap guavaAny, final ImmutableList guavaValues) { + super(); + this.any = any; + this.values = values; + this.guavaAny = guavaAny; + this.guavaValues = guavaValues; + } + public static @java.lang.SuppressWarnings("all") JacksonBuilderSingular.JacksonBuilderSingularBuilder builder() { + return new JacksonBuilderSingular.JacksonBuilderSingularBuilder(); + } +} \ No newline at end of file diff --git a/test/transform/resource/before/JacksonBuilderSingular.java b/test/transform/resource/before/JacksonBuilderSingular.java new file mode 100644 index 00000000..c179c760 --- /dev/null +++ b/test/transform/resource/before/JacksonBuilderSingular.java @@ -0,0 +1,31 @@ +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import lombok.Builder; +import lombok.Singular; +import lombok.extern.jackson.Jacksonized; + +@Jacksonized +@Builder +public class JacksonBuilderSingular { + @JsonAnySetter + @Singular("any") + private Map any; + + @JsonProperty("v_a_l_u_e_s") + @Singular + private List values; + + @JsonAnySetter + @Singular("guavaAny") + private ImmutableMap guavaAny; + + @JsonProperty("guava_v_a_l_u_e_s") + @Singular + private ImmutableList guavaValues; +} From ca5bd2868af828ec6b26c91b2398e8c6dbf1c24e Mon Sep 17 00:00:00 2001 From: Roel Spilker Date: Thu, 7 May 2020 23:26:51 +0200 Subject: [PATCH 07/87] Delombok prints the ReceiverParameter (this), fixes #2444. --- .../lombok/delombok/PrettyPrinter.java | 9 +++- test/pretty/resource/after/ThisParameter.java | 34 +++++++++++++++ .../pretty/resource/before/ThisParameter.java | 41 +++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/pretty/resource/after/ThisParameter.java create mode 100644 test/pretty/resource/before/ThisParameter.java diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java index 2db70f7f..7eb8410b 100644 --- a/src/delombok/lombok/delombok/PrettyPrinter.java +++ b/src/delombok/lombok/delombok/PrettyPrinter.java @@ -796,8 +796,15 @@ public class PrettyPrinter extends JCTree.Visitor { print(tree.name); } - print("("); boolean first = true; + print("("); + + JCVariableDecl recvparam = readObject(tree, "recvparam", null); + if (recvparam != null) { + printVarDefInline(recvparam); + first = false; + } + for (JCVariableDecl param : tree.params) { if (!first) print(", "); first = false; diff --git a/test/pretty/resource/after/ThisParameter.java b/test/pretty/resource/after/ThisParameter.java new file mode 100644 index 00000000..49452a59 --- /dev/null +++ b/test/pretty/resource/after/ThisParameter.java @@ -0,0 +1,34 @@ +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +class ThisParameter { + void untagged(ThisParameter this, int i) { + // no content + } + void sourceTagged(@SourceTagged("source") ThisParameter this) { + // no content + } + void classTagged(@ClassTagged("class") ThisParameter this) { + // no content + } + void runtimeTagged(@RuntimeTagged("runtime") ThisParameter this) { + // no content + } + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.SOURCE) + @interface SourceTagged { + String value(); + } + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.CLASS) + @interface ClassTagged { + String value(); + } + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + @interface RuntimeTagged { + String value(); + } +} diff --git a/test/pretty/resource/before/ThisParameter.java b/test/pretty/resource/before/ThisParameter.java new file mode 100644 index 00000000..d95c0261 --- /dev/null +++ b/test/pretty/resource/before/ThisParameter.java @@ -0,0 +1,41 @@ +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +class ThisParameter { + + void untagged(ThisParameter this, int i) { + // no content + } + + void sourceTagged(@SourceTagged("source") ThisParameter this) { + // no content + } + + void classTagged(@ClassTagged("class") ThisParameter this) { + // no content + } + + void runtimeTagged(@RuntimeTagged("runtime") ThisParameter this) { + // no content + } + + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.SOURCE) + @interface SourceTagged { + String value(); + } + + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.CLASS) + @interface ClassTagged { + String value(); + } + + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + @interface RuntimeTagged { + String value(); + } +} From d54b360af9bdd66885badb80007d6839ea9d48f8 Mon Sep 17 00:00:00 2001 From: Roel Spilker Date: Thu, 7 May 2020 23:47:28 +0200 Subject: [PATCH 08/87] Update changelog --- doc/changelog.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/changelog.markdown b/doc/changelog.markdown index f56ae524..bfaf8a4a 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -5,6 +5,7 @@ Lombok Changelog * BREAKING CHANGE: mapstruct users should now add a dependency to lombok-mapstruct-binding. This solves compiling modules with lombok (and mapstruct). * FEATURE: Similar to `@Builder`, you can now configure a `@SuperBuilder`'s 'setter' prefixes via `@SuperBuilder(setterPrefix = "set")` for example. We still discourage doing this. [Pull Request #2357](https://github.com/rzwitserloot/lombok/pull/2357). * FEATURE: If using `@Synchronized("lockVar")`, if `lockVar` is referring to a static field, the code lombok generates no longer causes a warning about accessing a static entity incorrectly. [Issue #678](https://github.com/rzwitserloot/lombok/issues/678) +* BUGFIX: Delombok print the first `this` parameter. [Issue #2444](https://github.com/rzwitserloot/lombok/issues/2444) * BUGFIX: Using `val` in combination with values whose generics include wildcards that reference themselves would cause a `StackOverflowError` in javac. [Issue #2358](https://github.com/rzwitserloot/lombok/issues/2358). * BUGFIX: Using `@SuperBuilder` on a class that has some fairly convoluted generics usage would fail with 'Wrong number of type arguments'. [Issue #2359](https://github.com/rzwitserloot/lombok/issues/2359) [Pull Request #2362](https://github.com/rzwitserloot/lombok/pull/2362) * BUGFIX: Various lombok annotations on classes nested inside enums or interfaces would cause errors in eclipse. [Issue #2369](https://github.com/rzwitserloot/lombok/issues/2369) From 8ba6d4b0ea661fd6c701fed56212ecc9c4acabb2 Mon Sep 17 00:00:00 2001 From: Roel Spilker Date: Thu, 14 May 2020 21:28:19 +0200 Subject: [PATCH 09/87] Add more characters not to escape on Windows. Fixes #2435 --- src/installer/lombok/installer/IdeLocation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/installer/lombok/installer/IdeLocation.java b/src/installer/lombok/installer/IdeLocation.java index c3853867..6b9a94c6 100644 --- a/src/installer/lombok/installer/IdeLocation.java +++ b/src/installer/lombok/installer/IdeLocation.java @@ -66,7 +66,7 @@ public abstract class IdeLocation { } private static final String LEGAL_PATH_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/"; - private static final String LEGAL_PATH_CHARS_WINDOWS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/:\\ "; + private static final String LEGAL_PATH_CHARS_WINDOWS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,/;'[]{}!@#$^&()-_+= :\\"; public static String escapePath(String path) { StringBuilder out = new StringBuilder(); String legalChars = OsUtils.getOS() == OsUtils.OS.UNIX ? LEGAL_PATH_CHARS : LEGAL_PATH_CHARS_WINDOWS; From 265c56d229093a307ebf231baa191663f953cf1e Mon Sep 17 00:00:00 2001 From: Rawi01 Date: Sat, 25 Apr 2020 19:20:23 +0200 Subject: [PATCH 10/87] [fixes #1969] Enum.values() requires enumConstantsCounter to be set --- .../lombok/eclipse/handlers/HandleFieldNameConstants.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java index 2db7591c..9343011f 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java @@ -136,9 +136,8 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler Date: Wed, 22 Apr 2020 18:46:06 -0400 Subject: [PATCH 11/87] Update Fake.class to support OpenJ9 Field Layout Currently lombok crashes when building with OpenJ9 JDK as described in #2414 It is due to the `override` field offset from Fake.class doesn't match actual offset value for AccessibleObject.class in OpenJ9 This change is to support the OpenJ9 Field layout algorithm. --- src/utils/lombok/permit/Permit.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/lombok/permit/Permit.java b/src/utils/lombok/permit/Permit.java index b7c5f0d9..407c3922 100644 --- a/src/utils/lombok/permit/Permit.java +++ b/src/utils/lombok/permit/Permit.java @@ -95,6 +95,7 @@ public class Permit { static class Fake { boolean override; + Object accessCheckCache; } public static Method getMethod(Class c, String mName, Class... parameterTypes) throws NoSuchMethodException { From ab028d70a4c178862d3792d7c0cd61710d330b4a Mon Sep 17 00:00:00 2001 From: Roel Spilker Date: Thu, 28 May 2020 21:15:50 +0200 Subject: [PATCH 12/87] add OpenJ9 fix to changelog --- doc/changelog.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/changelog.markdown b/doc/changelog.markdown index bfaf8a4a..063945be 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -2,6 +2,7 @@ Lombok Changelog ---------------- ### v1.18.13 "Edgy Guinea Pig" +* PLATFORM: Added support for compiling projects with OpenJ9 [Pull Request #2437](https://github.com/rzwitserloot/lombok/pull/2437) * BREAKING CHANGE: mapstruct users should now add a dependency to lombok-mapstruct-binding. This solves compiling modules with lombok (and mapstruct). * FEATURE: Similar to `@Builder`, you can now configure a `@SuperBuilder`'s 'setter' prefixes via `@SuperBuilder(setterPrefix = "set")` for example. We still discourage doing this. [Pull Request #2357](https://github.com/rzwitserloot/lombok/pull/2357). * FEATURE: If using `@Synchronized("lockVar")`, if `lockVar` is referring to a static field, the code lombok generates no longer causes a warning about accessing a static entity incorrectly. [Issue #678](https://github.com/rzwitserloot/lombok/issues/678) From faa70bd21d2872e8354574cc98c7aa82ab996bcd Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 26 May 2020 11:40:07 +0200 Subject: [PATCH 13/87] [Jacksonized] copy JsonAutoDetect to builder class --- src/core/lombok/core/handlers/HandlerUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index a8d56041..f620a3ab 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -323,6 +323,7 @@ public class HandlerUtil { "com.fasterxml.jackson.annotation.JsonAnySetter", })); JACKSON_COPY_TO_BUILDER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] { + "com.fasterxml.jackson.annotation.JsonAutoDetect", "com.fasterxml.jackson.annotation.JsonFormat", "com.fasterxml.jackson.annotation.JsonIgnoreProperties", "com.fasterxml.jackson.annotation.JsonIgnoreType", From 48b1f17f7ef0e0b3d90dee74ee1b490a02cacff2 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Fri, 1 May 2020 23:56:17 +0200 Subject: [PATCH 14/87] [SuperBuilder] allow constructor customization --- .../eclipse/handlers/HandleSuperBuilder.java | 28 +++++++++++++-- .../javac/handlers/HandleSuperBuilder.java | 35 +++++++++++++++++-- .../SuperBuilderCustomized.java | 15 ++++---- .../after-ecj/SuperBuilderCustomized.java | 8 +++-- .../before/SuperBuilderCustomized.java | 10 ++++++ 5 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index a49c20a4..455d40ed 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -274,8 +274,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // If there is no superclass, superclassBuilderClassExpression is still == null at this point. // You can use it to check whether to inherit or not. - generateBuilderBasedConstructor(cfv, tdParent, typeParams, builderFields, annotationNode, builderClassName, - superclassBuilderClass != null); + if (!constructorExists(tdParent, builderClassName)) { + generateBuilderBasedConstructor(cfv, tdParent, typeParams, builderFields, annotationNode, builderClassName, + superclassBuilderClass != null); + } // Create the abstract builder class, or reuse an existing one. EclipseNode builderType = findInnerClass(tdParent, builderClassName); @@ -1159,4 +1161,26 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { System.arraycopy(name, 0, out, prefix.length, name.length); return out; } + + private boolean constructorExists(EclipseNode type, String builderClassName) { + if (type != null && type.get() instanceof TypeDeclaration) { + TypeDeclaration typeDecl = (TypeDeclaration)type.get(); + if (typeDecl.methods != null) for (AbstractMethodDeclaration def : typeDecl.methods) { + if (def instanceof ConstructorDeclaration) { + if ((def.bits & ASTNode.IsDefaultConstructor) != 0) continue; + if (!def.isConstructor()) continue; + if (isTolerate(type, def)) continue; + if (def.arguments.length != 1) continue; + + // Cannot use typeMatches() here, because the parameter could be fully-qualified, partially-qualified, or not qualified. + // A string-compare of the last part should work. If it's a false-positive, users could still @Tolerate it. + char[] typeName = def.arguments[0].type.getLastToken(); + if (builderClassName.equals(String.valueOf(typeName))) + return true; + } + } + } + + return false; + } } diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 2dc3247d..b7cd6f9a 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -354,8 +354,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } // Generate a constructor in the annotated class that takes a builder as argument. - generateBuilderBasedConstructor(cfv, tdParent, typeParams, builderFields, annotationNode, builderClassName, - superclassBuilderClassExpression != null); + if (!constructorExists(tdParent, builderClassName)) { + generateBuilderBasedConstructor(cfv, tdParent, typeParams, builderFields, annotationNode, builderClassName, + superclassBuilderClassExpression != null); + } if (isAbstract) { // Only non-abstract classes get the builder() and toBuilder() methods. @@ -1070,4 +1072,33 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } return typeParameter; } + + /** + * Checks if there is a manual constructor in the given type with a single parameter (builder). + */ + private boolean constructorExists(JavacNode type, String builderClassName) { + if (type != null && type.get() instanceof JCClassDecl) { + for (JCTree def : ((JCClassDecl)type.get()).defs) { + if (def instanceof JCMethodDecl) { + JCMethodDecl md = (JCMethodDecl) def; + String name = md.name.toString(); + boolean matches = name.equals(""); + if (isTolerate(type, md)) + continue; + if (matches && md.params != null && md.params.length() == 1) { + // Cannot use typeMatches() here, because the parameter could be fully-qualified, partially-qualified, or not qualified. + // A string-compare of the last part should work. If it's a false-positive, users could still @Tolerate it. + String typeName = md.params.get(0).getType().toString(); + int lastIndexOfDot = typeName.lastIndexOf('.'); + if (lastIndexOfDot >= 0) { + typeName = typeName.substring(lastIndexOfDot+1); + } + if ((builderClassName+"").equals(typeName)) + return true; + } + } + } + } + return false; + } } diff --git a/test/transform/resource/after-delombok/SuperBuilderCustomized.java b/test/transform/resource/after-delombok/SuperBuilderCustomized.java index bffa72fa..f66133c9 100644 --- a/test/transform/resource/after-delombok/SuperBuilderCustomized.java +++ b/test/transform/resource/after-delombok/SuperBuilderCustomized.java @@ -23,6 +23,13 @@ public class SuperBuilderCustomized { } } int field1; + protected Parent(ParentBuilder b) { + if (b.field1 == 0) throw new IllegalArgumentException("field1 must be != 0"); + this.field1 = b.field1; + } + public static SuperBuilderCustomized.Parent.ParentBuilder builder(int field1) { + return new SuperBuilderCustomized.Parent.ParentBuilderImpl().field1(field1); + } @java.lang.SuppressWarnings("all") private static final class ParentBuilderImpl extends SuperBuilderCustomized.Parent.ParentBuilder { @java.lang.SuppressWarnings("all") @@ -39,14 +46,6 @@ public class SuperBuilderCustomized { return new SuperBuilderCustomized.Parent(this); } } - @java.lang.SuppressWarnings("all") - protected Parent(final SuperBuilderCustomized.Parent.ParentBuilder b) { - this.field1 = b.field1; - } - @java.lang.SuppressWarnings("all") - public static SuperBuilderCustomized.Parent.ParentBuilder builder() { - return new SuperBuilderCustomized.Parent.ParentBuilderImpl(); - } } public static class Child extends Parent { private static final class ChildBuilderImpl extends ChildBuilder { diff --git a/test/transform/resource/after-ecj/SuperBuilderCustomized.java b/test/transform/resource/after-ecj/SuperBuilderCustomized.java index 32317f6a..fe0e1238 100644 --- a/test/transform/resource/after-ecj/SuperBuilderCustomized.java +++ b/test/transform/resource/after-ecj/SuperBuilderCustomized.java @@ -32,12 +32,14 @@ public class SuperBuilderCustomized { } } int field1; - protected @java.lang.SuppressWarnings("all") Parent(final SuperBuilderCustomized.Parent.ParentBuilder b) { + protected Parent(ParentBuilder b) { super(); + if ((b.field1 == 0)) + throw new IllegalArgumentException("field1 must be != 0"); this.field1 = b.field1; } - public static @java.lang.SuppressWarnings("all") SuperBuilderCustomized.Parent.ParentBuilder builder() { - return new SuperBuilderCustomized.Parent.ParentBuilderImpl(); + public static SuperBuilderCustomized.Parent.ParentBuilder builder(int field1) { + return new SuperBuilderCustomized.Parent.ParentBuilderImpl().field1(field1); } } public static @lombok.experimental.SuperBuilder class Child extends Parent { diff --git a/test/transform/resource/before/SuperBuilderCustomized.java b/test/transform/resource/before/SuperBuilderCustomized.java index 77830587..8e641d90 100644 --- a/test/transform/resource/before/SuperBuilderCustomized.java +++ b/test/transform/resource/before/SuperBuilderCustomized.java @@ -14,6 +14,16 @@ public class SuperBuilderCustomized { } } int field1; + + protected Parent(ParentBuilder b) { + if (b.field1 == 0) + throw new IllegalArgumentException("field1 must be != 0"); + this.field1 = b.field1; + } + + public static SuperBuilderCustomized.Parent.ParentBuilder builder(int field1) { + return new SuperBuilderCustomized.Parent.ParentBuilderImpl().field1(field1); + } } @lombok.experimental.SuperBuilder From b767427f06b60dd236bc2c0d6ea41a0e8446fe08 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 11 May 2020 08:33:13 +0200 Subject: [PATCH 15/87] [SuperBuilder] don't warn on existing toBuilder method --- src/core/lombok/eclipse/handlers/HandleSuperBuilder.java | 2 +- src/core/lombok/javac/handlers/HandleSuperBuilder.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 455d40ed..7c8e4ea3 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -390,10 +390,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // Add the toBuilder() method to the annotated class. switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) { case EXISTS_BY_USER: - annotationNode.addWarning("Not generating toBuilder() as it already exists."); break; case NOT_EXISTS: injectMethod(tdParent, generateToBuilderMethod(cfv, builderClassName, builderImplClassName, tdParent, typeParams, ast)); + break; default: // Should not happen. } diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index b7cd6f9a..4cd64b77 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -377,14 +377,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (toBuilder) { switch (methodExists(TO_BUILDER_METHOD_NAME, tdParent, 0)) { case EXISTS_BY_USER: - annotationNode.addWarning("Not generating toBuilder() as it already exists."); - return; + break; case NOT_EXISTS: JCMethodDecl md = generateToBuilderMethod(cfv, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); if (md != null) { recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); injectMethod(tdParent, md); } + break; default: // Should not happen. } From b37f3cba3eb7e7babd38894b890ca5fda2f57cf4 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 11 May 2020 08:34:09 +0200 Subject: [PATCH 16/87] [SuperBuilder] update documentation on customizing --- .../templates/features/experimental/SuperBuilder.html | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/website/templates/features/experimental/SuperBuilder.html b/website/templates/features/experimental/SuperBuilder.html index c929e8f5..e9a0958d 100644 --- a/website/templates/features/experimental/SuperBuilder.html +++ b/website/templates/features/experimental/SuperBuilder.html @@ -6,6 +6,8 @@ @SuperBuilder was introduced as experimental feature in lombok v1.18.2.

@SuperBuilder's toBuilder feature and limited support for customization was added with lombok v1.18.4. +

+ @SuperBuilder customization possibilities were extended with lombok v1.18.14.

@@ -13,7 +15,7 @@

The @SuperBuilder annotation produces complex builder APIs for your classes. In contrast to @Builder, @SuperBuilder also works with fields from superclasses. - However, it only works for types, and customization possibilities are limited. + However, it only works for types. Most importantly, it requires that all superclasses also have the @SuperBuilder annotation.

@SuperBuilder lets you automatically produce the code required to have your class be instantiable with code such as:
@@ -21,7 +23,7 @@

@SuperBuilder can generate so-called 'singular' methods for collection parameters/fields. For details, see the @Singular documentation in @Builder.

- @SuperBuilder generates a private constructor on the class that takes a builder instance as a parameter. This constructor sets the fields of the new instance to the values from the builder. + @SuperBuilder generates a protected constructor on the class that takes a builder instance as a parameter. This constructor sets the fields of the new instance to the values from the builder.

@SuperBuilder is not compatible with @Builder.

@@ -32,8 +34,9 @@

To ensure type-safety, @SuperBuilder generates two inner builder classes for each annotated class, one abstract and one concrete class named FoobarBuilder and FoobarBuilderImpl (where Foobar is the name of the annotated class).

- Customizing the code generated by @SuperBuilder is limited to adding new methods or annotations to the builder classes, and providing custom implementations of the 'set', builder(), and build() methods. - You have to make sure that the builder class declaration headers match those that would have been generated by lombok. Due to the heavy generics usage, we strongly advice to copy the builder class definition header from the uncustomized delomboked code. + You can customize most of the code generated by @SuperBuilder, except for internal methods (e.g. self()). + You have to make sure that the builder class declaration headers match those that would have been generated by lombok. + Due to the heavy generics usage, we strongly advice to take the uncustomized delomboked code as a reference when customizing @SuperBuilder.

The configurable aspects of builder are: