From 0cc5e054a3d3645081ad86a3ff70e742a590d2b1 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 30 Sep 2025 00:27:46 +0200 Subject: [PATCH 1/4] ecj: fix duplicate jackson annotation copying when generating setter --- .../lombok/eclipse/handlers/HandleSetter.java | 20 ++-- .../after-delombok/JacksonJsonProperty2.java | 103 ++++++++++++++++++ .../after-ecj/JacksonJsonProperty2.java | 66 +++++++++++ .../resource/before/JacksonJsonProperty2.java | 12 ++ 4 files changed, 188 insertions(+), 13 deletions(-) create mode 100644 test/transform/resource/after-delombok/JacksonJsonProperty2.java create mode 100644 test/transform/resource/after-ecj/JacksonJsonProperty2.java create mode 100644 test/transform/resource/before/JacksonJsonProperty2.java diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index dc2c2139..ec853056 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -111,13 +111,6 @@ public class HandleSetter extends EclipseAnnotationHandler { return; } - // If a fluent setter should be generated, force-copy annotations from the field. - AnnotationValues accessorsForField = EclipseHandlerUtil.getAccessorsForField(fieldNode); - // Copying Jackson annotations is required for fluent accessors (otherwise Jackson would not find the accessor). - Annotation[] copyableToSetterAnnotations = findCopyableToSetterAnnotations(fieldNode, accessorsForField.isExplicit("fluent")); - onMethod = new ArrayList(onMethod); - onMethod.addAll(Arrays.asList(copyableToSetterAnnotations)); - createSetterForField(level, fieldNode, sourceNode, false, onMethod, onParam); } @@ -170,7 +163,7 @@ public class HandleSetter extends EclipseAnnotationHandler { AnnotationValues accessors = getAccessorsForField(fieldNode); String setterName = toSetterName(fieldNode, isBoolean, accessors); boolean shouldReturnThis = shouldReturnThis(fieldNode, accessors); - + boolean fluent = accessors.isExplicit("fluent"); if (setterName == null) { fieldNode.addWarning("Not generating setter for this field: It does not fit your @Accessors prefix list."); return; @@ -196,11 +189,11 @@ public class HandleSetter extends EclipseAnnotationHandler { } } - MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), false, fieldNode, setterName, null, null, shouldReturnThis, modifier, sourceNode, onMethod, onParam); + MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), false, fieldNode, setterName, null, null, shouldReturnThis, modifier, sourceNode, onMethod, onParam, fluent); injectMethod(fieldNode.up(), method); } - static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] paramName, char[] booleanFieldToSet, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List onMethod, List onParam) { + static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] paramName, char[] booleanFieldToSet, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List onMethod, List onParam, boolean fluent) { ASTNode source = sourceNode.get(); int pS = source.sourceStart, pE = source.sourceEnd; @@ -213,11 +206,11 @@ public class HandleSetter extends EclipseAnnotationHandler { returnThis = new ReturnStatement(thisRef, pS, pE); } - MethodDeclaration d = createSetter(parent, deprecate, fieldNode, name, paramName, booleanFieldToSet, returnType, returnThis, modifier, sourceNode, onMethod, onParam); + MethodDeclaration d = createSetter(parent, deprecate, fieldNode, name, paramName, booleanFieldToSet, returnType, returnThis, modifier, sourceNode, onMethod, onParam, fluent); return d; } - static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] paramName, char[] booleanFieldToSet, TypeReference returnType, Statement returnStatement, int modifier, EclipseNode sourceNode, List onMethod, List onParam) { + static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] paramName, char[] booleanFieldToSet, TypeReference returnType, Statement returnStatement, int modifier, EclipseNode sourceNode, List onMethod, List onParam, boolean fluent) { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); if (paramName == null) paramName = field.name; ASTNode source = sourceNode.get(); @@ -237,7 +230,8 @@ public class HandleSetter extends EclipseAnnotationHandler { if (isFieldDeprecated(fieldNode) || deprecate) { deprecated = new Annotation[] { generateDeprecatedAnnotation(source) }; } - method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated, findCopyableToSetterAnnotations(fieldNode, false)); + // Copying Jackson annotations is required for fluent accessors (otherwise Jackson would not find the accessor). + method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated, findCopyableToSetterAnnotations(fieldNode, fluent)); Argument param = new Argument(paramName, p, copyType(field.type, source), Modifier.FINAL); param.sourceStart = pS; param.sourceEnd = pE; method.arguments = new Argument[] { param }; diff --git a/test/transform/resource/after-delombok/JacksonJsonProperty2.java b/test/transform/resource/after-delombok/JacksonJsonProperty2.java new file mode 100644 index 00000000..e683fab1 --- /dev/null +++ b/test/transform/resource/after-delombok/JacksonJsonProperty2.java @@ -0,0 +1,103 @@ +//CONF: lombok.copyJacksonAnnotationsToAccessors = true +//version 8: Jackson deps are at least Java7+. +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class JacksonJsonProperty3 { + @JsonProperty("a") + String propertyOne; + @JsonProperty("b") + int propertyTwo; + Map additional = new HashMap<>(); + + @java.lang.SuppressWarnings("all") + @lombok.Generated + public JacksonJsonProperty3() { + } + + @JsonProperty("a") + @java.lang.SuppressWarnings("all") + @lombok.Generated + public String getPropertyOne() { + return this.propertyOne; + } + + @JsonProperty("b") + @java.lang.SuppressWarnings("all") + @lombok.Generated + public int getPropertyTwo() { + return this.propertyTwo; + } + + @java.lang.SuppressWarnings("all") + @lombok.Generated + public Map getAdditional() { + return this.additional; + } + + @JsonProperty("a") + @java.lang.SuppressWarnings("all") + @lombok.Generated + public void setPropertyOne(final String propertyOne) { + this.propertyOne = propertyOne; + } + + @JsonProperty("b") + @java.lang.SuppressWarnings("all") + @lombok.Generated + public void setPropertyTwo(final int propertyTwo) { + this.propertyTwo = propertyTwo; + } + + @java.lang.SuppressWarnings("all") + @lombok.Generated + public void setAdditional(final Map additional) { + this.additional = additional; + } + + @java.lang.Override + @java.lang.SuppressWarnings("all") + @lombok.Generated + public boolean equals(final java.lang.Object o) { + if (o == this) return true; + if (!(o instanceof JacksonJsonProperty3)) return false; + final JacksonJsonProperty3 other = (JacksonJsonProperty3) o; + if (!other.canEqual((java.lang.Object) this)) return false; + if (this.getPropertyTwo() != other.getPropertyTwo()) return false; + final java.lang.Object this$propertyOne = this.getPropertyOne(); + final java.lang.Object other$propertyOne = other.getPropertyOne(); + if (this$propertyOne == null ? other$propertyOne != null : !this$propertyOne.equals(other$propertyOne)) return false; + final java.lang.Object this$additional = this.getAdditional(); + final java.lang.Object other$additional = other.getAdditional(); + if (this$additional == null ? other$additional != null : !this$additional.equals(other$additional)) return false; + return true; + } + + @java.lang.SuppressWarnings("all") + @lombok.Generated + protected boolean canEqual(final java.lang.Object other) { + return other instanceof JacksonJsonProperty3; + } + + @java.lang.Override + @java.lang.SuppressWarnings("all") + @lombok.Generated + public int hashCode() { + final int PRIME = 59; + int result = 1; + result = result * PRIME + this.getPropertyTwo(); + final java.lang.Object $propertyOne = this.getPropertyOne(); + result = result * PRIME + ($propertyOne == null ? 43 : $propertyOne.hashCode()); + final java.lang.Object $additional = this.getAdditional(); + result = result * PRIME + ($additional == null ? 43 : $additional.hashCode()); + return result; + } + + @java.lang.Override + @java.lang.SuppressWarnings("all") + @lombok.Generated + public java.lang.String toString() { + return "JacksonJsonProperty3(propertyOne=" + this.getPropertyOne() + ", propertyTwo=" + this.getPropertyTwo() + ", additional=" + this.getAdditional() + ")"; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/JacksonJsonProperty2.java b/test/transform/resource/after-ecj/JacksonJsonProperty2.java new file mode 100644 index 00000000..3ddabe68 --- /dev/null +++ b/test/transform/resource/after-ecj/JacksonJsonProperty2.java @@ -0,0 +1,66 @@ +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +public @Data class JacksonJsonProperty3 { + @JsonProperty("a") String propertyOne; + @JsonProperty("b") int propertyTwo; + Map additional = new HashMap<>(); + public @JsonProperty("a") @java.lang.SuppressWarnings("all") @lombok.Generated String getPropertyOne() { + return this.propertyOne; + } + public @JsonProperty("b") @java.lang.SuppressWarnings("all") @lombok.Generated int getPropertyTwo() { + return this.propertyTwo; + } + public @java.lang.SuppressWarnings("all") @lombok.Generated Map getAdditional() { + return this.additional; + } + public @JsonProperty("a") @java.lang.SuppressWarnings("all") @lombok.Generated void setPropertyOne(final String propertyOne) { + this.propertyOne = propertyOne; + } + public @JsonProperty("b") @java.lang.SuppressWarnings("all") @lombok.Generated void setPropertyTwo(final int propertyTwo) { + this.propertyTwo = propertyTwo; + } + public @java.lang.SuppressWarnings("all") @lombok.Generated void setAdditional(final Map additional) { + this.additional = additional; + } + public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated boolean equals(final java.lang.Object o) { + if ((o == this)) + return true; + if ((! (o instanceof JacksonJsonProperty3))) + return false; + final JacksonJsonProperty3 other = (JacksonJsonProperty3) o; + if ((! other.canEqual((java.lang.Object) this))) + return false; + if ((this.getPropertyTwo() != other.getPropertyTwo())) + return false; + final java.lang.Object this$propertyOne = this.getPropertyOne(); + final java.lang.Object other$propertyOne = other.getPropertyOne(); + if (((this$propertyOne == null) ? (other$propertyOne != null) : (! this$propertyOne.equals(other$propertyOne)))) + return false; + final java.lang.Object this$additional = this.getAdditional(); + final java.lang.Object other$additional = other.getAdditional(); + if (((this$additional == null) ? (other$additional != null) : (! this$additional.equals(other$additional)))) + return false; + return true; + } + protected @java.lang.SuppressWarnings("all") @lombok.Generated boolean canEqual(final java.lang.Object other) { + return (other instanceof JacksonJsonProperty3); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated int hashCode() { + final int PRIME = 59; + int result = 1; + result = ((result * PRIME) + this.getPropertyTwo()); + final java.lang.Object $propertyOne = this.getPropertyOne(); + result = ((result * PRIME) + (($propertyOne == null) ? 43 : $propertyOne.hashCode())); + final java.lang.Object $additional = this.getAdditional(); + result = ((result * PRIME) + (($additional == null) ? 43 : $additional.hashCode())); + return result; + } + public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() { + return (((((("JacksonJsonProperty3(propertyOne=" + this.getPropertyOne()) + ", propertyTwo=") + this.getPropertyTwo()) + ", additional=") + this.getAdditional()) + ")"); + } + public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonJsonProperty3() { + super(); + } +} \ No newline at end of file diff --git a/test/transform/resource/before/JacksonJsonProperty2.java b/test/transform/resource/before/JacksonJsonProperty2.java new file mode 100644 index 00000000..38cbc1cc --- /dev/null +++ b/test/transform/resource/before/JacksonJsonProperty2.java @@ -0,0 +1,12 @@ +//CONF: lombok.copyJacksonAnnotationsToAccessors = true +//version 8: Jackson deps are at least Java7+. +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data public class JacksonJsonProperty3 { + @JsonProperty("a") String propertyOne; + @JsonProperty("b") int propertyTwo; + Map additional = new HashMap<>(); +} From ca16c5b9f6a8bae72f4b4938349ce1ff11b30c01 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 30 Sep 2025 13:21:54 +0200 Subject: [PATCH 2/4] fix test --- .../after-delombok/JacksonJsonProperty2.java | 12 ++++++------ .../resource/after-ecj/JacksonJsonProperty2.java | 12 ++++++------ .../resource/before/JacksonJsonProperty2.java | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/test/transform/resource/after-delombok/JacksonJsonProperty2.java b/test/transform/resource/after-delombok/JacksonJsonProperty2.java index e683fab1..8125a41d 100644 --- a/test/transform/resource/after-delombok/JacksonJsonProperty2.java +++ b/test/transform/resource/after-delombok/JacksonJsonProperty2.java @@ -4,7 +4,7 @@ import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonProperty; -public class JacksonJsonProperty3 { +public class JacksonJsonProperty2 { @JsonProperty("a") String propertyOne; @JsonProperty("b") @@ -13,7 +13,7 @@ public class JacksonJsonProperty3 { @java.lang.SuppressWarnings("all") @lombok.Generated - public JacksonJsonProperty3() { + public JacksonJsonProperty2() { } @JsonProperty("a") @@ -61,8 +61,8 @@ public class JacksonJsonProperty3 { @lombok.Generated public boolean equals(final java.lang.Object o) { if (o == this) return true; - if (!(o instanceof JacksonJsonProperty3)) return false; - final JacksonJsonProperty3 other = (JacksonJsonProperty3) o; + if (!(o instanceof JacksonJsonProperty2)) return false; + final JacksonJsonProperty2 other = (JacksonJsonProperty2) o; if (!other.canEqual((java.lang.Object) this)) return false; if (this.getPropertyTwo() != other.getPropertyTwo()) return false; final java.lang.Object this$propertyOne = this.getPropertyOne(); @@ -77,7 +77,7 @@ public class JacksonJsonProperty3 { @java.lang.SuppressWarnings("all") @lombok.Generated protected boolean canEqual(final java.lang.Object other) { - return other instanceof JacksonJsonProperty3; + return other instanceof JacksonJsonProperty2; } @java.lang.Override @@ -98,6 +98,6 @@ public class JacksonJsonProperty3 { @java.lang.SuppressWarnings("all") @lombok.Generated public java.lang.String toString() { - return "JacksonJsonProperty3(propertyOne=" + this.getPropertyOne() + ", propertyTwo=" + this.getPropertyTwo() + ", additional=" + this.getAdditional() + ")"; + return "JacksonJsonProperty2(propertyOne=" + this.getPropertyOne() + ", propertyTwo=" + this.getPropertyTwo() + ", additional=" + this.getAdditional() + ")"; } } \ No newline at end of file diff --git a/test/transform/resource/after-ecj/JacksonJsonProperty2.java b/test/transform/resource/after-ecj/JacksonJsonProperty2.java index 3ddabe68..b3626250 100644 --- a/test/transform/resource/after-ecj/JacksonJsonProperty2.java +++ b/test/transform/resource/after-ecj/JacksonJsonProperty2.java @@ -2,7 +2,7 @@ import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; -public @Data class JacksonJsonProperty3 { +public @Data class JacksonJsonProperty2 { @JsonProperty("a") String propertyOne; @JsonProperty("b") int propertyTwo; Map additional = new HashMap<>(); @@ -27,9 +27,9 @@ public @Data class JacksonJsonProperty3 { public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated boolean equals(final java.lang.Object o) { if ((o == this)) return true; - if ((! (o instanceof JacksonJsonProperty3))) + if ((! (o instanceof JacksonJsonProperty2))) return false; - final JacksonJsonProperty3 other = (JacksonJsonProperty3) o; + final JacksonJsonProperty2 other = (JacksonJsonProperty2) o; if ((! other.canEqual((java.lang.Object) this))) return false; if ((this.getPropertyTwo() != other.getPropertyTwo())) @@ -45,7 +45,7 @@ public @Data class JacksonJsonProperty3 { return true; } protected @java.lang.SuppressWarnings("all") @lombok.Generated boolean canEqual(final java.lang.Object other) { - return (other instanceof JacksonJsonProperty3); + return (other instanceof JacksonJsonProperty2); } public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated int hashCode() { final int PRIME = 59; @@ -58,9 +58,9 @@ public @Data class JacksonJsonProperty3 { return result; } public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() { - return (((((("JacksonJsonProperty3(propertyOne=" + this.getPropertyOne()) + ", propertyTwo=") + this.getPropertyTwo()) + ", additional=") + this.getAdditional()) + ")"); + return (((((("JacksonJsonProperty2(propertyOne=" + this.getPropertyOne()) + ", propertyTwo=") + this.getPropertyTwo()) + ", additional=") + this.getAdditional()) + ")"); } - public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonJsonProperty3() { + public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonJsonProperty2() { super(); } } \ No newline at end of file diff --git a/test/transform/resource/before/JacksonJsonProperty2.java b/test/transform/resource/before/JacksonJsonProperty2.java index 38cbc1cc..9797feac 100644 --- a/test/transform/resource/before/JacksonJsonProperty2.java +++ b/test/transform/resource/before/JacksonJsonProperty2.java @@ -5,7 +5,7 @@ import java.util.Map; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; -@Data public class JacksonJsonProperty3 { +@Data public class JacksonJsonProperty2 { @JsonProperty("a") String propertyOne; @JsonProperty("b") int propertyTwo; Map additional = new HashMap<>(); From b19688c01d2b498d0e9c4cd984a3449ad09cecfb Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 30 Sep 2025 13:31:17 +0200 Subject: [PATCH 3/4] fix compile --- src/core/lombok/eclipse/handlers/HandleBuilder.java | 2 +- src/core/lombok/eclipse/handlers/HandleSetter.java | 3 ++- src/core/lombok/eclipse/handlers/HandleSuperBuilder.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 96366b9a..c39acdc7 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -1046,7 +1046,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (methodAnns != null && methodAnns.length > 0) methodAnnsList = Arrays.asList(methodAnns); ASTNode source = job.sourceNode.get(); MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, bfd.name, bfd.nameOfSetFlag, job.oldChain, toEclipseModifier(job.accessInners), - job.sourceNode, methodAnnsList, bfd.annotations != null ? Arrays.asList(copyAnnotations(source, bfd.annotations)) : Collections.emptyList()); + job.sourceNode, methodAnnsList, bfd.annotations != null ? Arrays.asList(copyAnnotations(source, bfd.annotations)) : Collections.emptyList(), false); if (job.sourceNode.up().getKind() == Kind.METHOD) { copyJavadocFromParam(bfd.originalFieldNode.up(), setter, td, new String(bfd.name)); } else { diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index ec853056..662c6d4e 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -163,7 +163,8 @@ public class HandleSetter extends EclipseAnnotationHandler { AnnotationValues accessors = getAccessorsForField(fieldNode); String setterName = toSetterName(fieldNode, isBoolean, accessors); boolean shouldReturnThis = shouldReturnThis(fieldNode, accessors); - boolean fluent = accessors.isExplicit("fluent"); + boolean fluent = accessors.isExplicit("fluent"); + if (setterName == null) { fieldNode.addWarning("Not generating setter for this field: It does not fit your @Accessors prefix list."); return; diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 096398b8..391befb9 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -1002,7 +1002,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { List methodAnnsList = Arrays.asList(EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode, true)); addCheckerFrameworkReturnsReceiver(returnType, job.source, job.checkerFramework); MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, paramName, nameOfSetFlag, returnType, returnStatement, ClassFileConstants.AccPublic, - job.sourceNode, methodAnnsList, annosOnParam != null ? Arrays.asList(copyAnnotations(job.source, annosOnParam)) : Collections.emptyList()); + job.sourceNode, methodAnnsList, annosOnParam != null ? Arrays.asList(copyAnnotations(job.source, annosOnParam)) : Collections.emptyList(), false); if (job.sourceNode.up().getKind() == Kind.METHOD) { copyJavadocFromParam(originalFieldNode.up(), setter, td, paramName.toString()); } else { From 5a423d50d5dd9935be43ee399c8dc7466e8fad7e Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Tue, 21 Apr 2026 23:19:59 +0200 Subject: [PATCH 4/4] [#3934] mention in changelog: fluent+Jacksonized now fixed. --- doc/changelog.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 14e70e29..fca8bc52 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -5,6 +5,7 @@ Lombok Changelog * PLATFORM: JDK26 support added [#4019](https://github.com/projectlombok/lombok/issues/4019). * PLATFORM: Spring Tools Suite 5 supported [#3985](https://github.com/projectlombok/lombok/issues/3985). * BUGFIX: `@Jacksonized` no longer stops generating `@JsonProperty` once an explicit `@JsonIgnore` annotations is encountered [#4022](https://github.com/projectlombok/lombok/issues/4022). +* BUGFIX: In eclipse, mixing `@Jacksonized` and `fluent = true` no longer causes the error `com.fasterxml.jackson.annotation.JsonProperty is not a repeatable annotation interface`. [#3934](https://github.com/projectlombok/lombok/issues/3934). ### v1.18.44 (March 11th, 2026) * FEATURE: `@Jacksonized` now supports both Jackson2 and Jackson3; you'll get a warning until you configure which one (or even both!) you want lombok to generate. [#3950](https://github.com/projectlombok/lombok/issues/3950).