diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 96366b9afb..1866a79d55 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -565,8 +565,15 @@ private static final char[] prefixWith(char[] prefix, char[] name) { } if (methodExists("toString", job.builderType, 0) == MemberExistsResult.NOT_EXISTS) { + List oldExcludes = findOldToStringExcludes(job.parentType); List> fieldNodes = new ArrayList>(); for (BuilderFieldData bfd : job.builderFields) { + if (bfd.originalFieldNode.hasAnnotation(ToString.Exclude.class)) { + continue; + } + if (oldExcludes.contains(bfd.originalFieldNode.getName())) { + continue; + } for (EclipseNode f : bfd.createdFields) { fieldNodes.add(new Included(f, null, true, false)); } @@ -1137,4 +1144,12 @@ private SingularData getSingularData(EclipseNode node, ASTNode source, final Str return null; } + + private List findOldToStringExcludes(EclipseNode parentType) { + EclipseNode toStringAnnotationNode = findAnnotation(ToString.class, parentType); + if (toStringAnnotationNode == null) return Collections.emptyList(); + + AnnotationValues toStringAnnotation = createAnnotation(ToString.class, toStringAnnotationNode); + return toStringAnnotation.getAsStringList("exclude"); + } } diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 1e023f20cf..8f438c3af6 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -27,6 +27,7 @@ import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.ArrayList; +import java.util.Collections; import javax.lang.model.element.Modifier; @@ -505,8 +506,15 @@ static class BuilderFieldData { } if (methodExists("toString", job.builderType, 0) == MemberExistsResult.NOT_EXISTS) { + java.util.List oldExcludes = findOldToStringExcludes(job.parentType); java.util.List> fieldNodes = new ArrayList>(); for (BuilderFieldData bfd : job.builderFields) { + if (hasAnnotation(ToString.Exclude.class, bfd.originalFieldNode)) { + continue; + } + if (oldExcludes.contains(bfd.originalFieldNode.getName())) { + continue; + } for (JavacNode f : bfd.createdFields) { fieldNodes.add(new Included(f, null, true, false)); } @@ -1020,4 +1028,12 @@ private SingularData getSingularData(JavacNode node, String setterPrefix) { return null; } + + private java.util.List findOldToStringExcludes(JavacNode parentType) { + JavacNode toStringAnnotationNode = findAnnotation(ToString.class, parentType); + if (toStringAnnotationNode == null) return Collections.emptyList(); + + AnnotationValues toStringAnnotation = createAnnotation(ToString.class, toStringAnnotationNode); + return toStringAnnotation.getAsStringList("exclude"); + } } diff --git a/src/core/lombok/javac/handlers/HandleToStringExcludeRemove.java b/src/core/lombok/javac/handlers/HandleToStringExcludeRemove.java new file mode 100644 index 0000000000..f205a27879 --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleToStringExcludeRemove.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2025 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.handlers; + +import static lombok.javac.handlers.JavacHandlerUtil.*; + +import com.sun.tools.javac.tree.JCTree.JCAnnotation; + +import lombok.ToString; +import lombok.ToString.Exclude; +import lombok.core.AlreadyHandledAnnotations; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.spi.Provides; + +@Provides +@HandlerPriority(32768) +@AlreadyHandledAnnotations +public class HandleToStringExcludeRemove extends JavacAnnotationHandler { + @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { + deleteAnnotationIfNeccessary(annotationNode, ToString.Exclude.class); + deleteImportFromCompilationUnit(annotationNode, ToString.class); + deleteImportFromCompilationUnit(annotationNode, ToString.Exclude.class); + } +} diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index f243c56a43..74568dbbbb 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -556,17 +556,22 @@ private static void deleteAnnotationIfNeccessary0(JavacNode annotation, String.. } } + public static void deleteImportFromCompilationUnit(JavacNode node, Class annotationType) { + deleteImportFromCompilationUnit(node, annotationType.getName()); + } + public static void deleteImportFromCompilationUnit(JavacNode node, String name) { if (inNetbeansEditor(node)) return; if (!node.shouldDeleteLombokAnnotations()) return; JCCompilationUnit unit = (JCCompilationUnit) node.top().get(); + String importName = name.replace('$', '.'); for (JCTree def : unit.defs) { if (!(def instanceof JCImport)) continue; JCImport imp0rt = (JCImport) def; if (imp0rt.staticImport) continue; - if (!Javac.getQualid(imp0rt).toString().equals(name)) continue; + if (!Javac.getQualid(imp0rt).toString().equals(importName)) continue; JavacAugments.JCImport_deletable.set(imp0rt, true); } } diff --git a/test/transform/resource/after-delombok/BuilderWithToStringExclude.java b/test/transform/resource/after-delombok/BuilderWithToStringExclude.java new file mode 100644 index 0000000000..8c6e5915d8 --- /dev/null +++ b/test/transform/resource/after-delombok/BuilderWithToStringExclude.java @@ -0,0 +1,58 @@ +public class BuilderWithToStringExclude { + private String a; + private String secret; + @java.lang.SuppressWarnings("all") + @lombok.Generated + BuilderWithToStringExclude(final String a, final String secret) { + this.a = a; + this.secret = secret; + } + @java.lang.SuppressWarnings("all") + @lombok.Generated + public static class BuilderWithToStringExcludeBuilder { + @java.lang.SuppressWarnings("all") + @lombok.Generated + private String a; + @java.lang.SuppressWarnings("all") + @lombok.Generated + private String secret; + @java.lang.SuppressWarnings("all") + @lombok.Generated + BuilderWithToStringExcludeBuilder() { + } + /** + * @return {@code this}. + */ + @java.lang.SuppressWarnings("all") + @lombok.Generated + public BuilderWithToStringExclude.BuilderWithToStringExcludeBuilder a(final String a) { + this.a = a; + return this; + } + /** + * @return {@code this}. + */ + @java.lang.SuppressWarnings("all") + @lombok.Generated + public BuilderWithToStringExclude.BuilderWithToStringExcludeBuilder secret(final String secret) { + this.secret = secret; + return this; + } + @java.lang.SuppressWarnings("all") + @lombok.Generated + public BuilderWithToStringExclude build() { + return new BuilderWithToStringExclude(this.a, this.secret); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + @lombok.Generated + public java.lang.String toString() { + return "BuilderWithToStringExclude.BuilderWithToStringExcludeBuilder(a=" + this.a + ")"; + } + } + @java.lang.SuppressWarnings("all") + @lombok.Generated + public static BuilderWithToStringExclude.BuilderWithToStringExcludeBuilder builder() { + return new BuilderWithToStringExclude.BuilderWithToStringExcludeBuilder(); + } +} diff --git a/test/transform/resource/after-delombok/BuilderWithToStringExcludeAndToString.java b/test/transform/resource/after-delombok/BuilderWithToStringExcludeAndToString.java new file mode 100644 index 0000000000..22f929a2c2 --- /dev/null +++ b/test/transform/resource/after-delombok/BuilderWithToStringExcludeAndToString.java @@ -0,0 +1,64 @@ +public class BuilderWithToStringExcludeAndToString { + private String a; + private String secret; + @java.lang.SuppressWarnings("all") + @lombok.Generated + BuilderWithToStringExcludeAndToString(final String a, final String secret) { + this.a = a; + this.secret = secret; + } + @java.lang.SuppressWarnings("all") + @lombok.Generated + public static class BuilderWithToStringExcludeAndToStringBuilder { + @java.lang.SuppressWarnings("all") + @lombok.Generated + private String a; + @java.lang.SuppressWarnings("all") + @lombok.Generated + private String secret; + @java.lang.SuppressWarnings("all") + @lombok.Generated + BuilderWithToStringExcludeAndToStringBuilder() { + } + /** + * @return {@code this}. + */ + @java.lang.SuppressWarnings("all") + @lombok.Generated + public BuilderWithToStringExcludeAndToString.BuilderWithToStringExcludeAndToStringBuilder a(final String a) { + this.a = a; + return this; + } + /** + * @return {@code this}. + */ + @java.lang.SuppressWarnings("all") + @lombok.Generated + public BuilderWithToStringExcludeAndToString.BuilderWithToStringExcludeAndToStringBuilder secret(final String secret) { + this.secret = secret; + return this; + } + @java.lang.SuppressWarnings("all") + @lombok.Generated + public BuilderWithToStringExcludeAndToString build() { + return new BuilderWithToStringExcludeAndToString(this.a, this.secret); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + @lombok.Generated + public java.lang.String toString() { + return "BuilderWithToStringExcludeAndToString.BuilderWithToStringExcludeAndToStringBuilder(a=" + this.a + ")"; + } + } + @java.lang.SuppressWarnings("all") + @lombok.Generated + public static BuilderWithToStringExcludeAndToString.BuilderWithToStringExcludeAndToStringBuilder builder() { + return new BuilderWithToStringExcludeAndToString.BuilderWithToStringExcludeAndToStringBuilder(); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + @lombok.Generated + public java.lang.String toString() { + return "BuilderWithToStringExcludeAndToString(a=" + this.a + ")"; + } +} diff --git a/test/transform/resource/after-delombok/BuilderWithToStringExcludeOld.java b/test/transform/resource/after-delombok/BuilderWithToStringExcludeOld.java new file mode 100644 index 0000000000..99aa70c3e3 --- /dev/null +++ b/test/transform/resource/after-delombok/BuilderWithToStringExcludeOld.java @@ -0,0 +1,64 @@ +public class BuilderWithToStringExcludeOld { + private String a; + private String secret; + @java.lang.SuppressWarnings("all") + @lombok.Generated + BuilderWithToStringExcludeOld(final String a, final String secret) { + this.a = a; + this.secret = secret; + } + @java.lang.SuppressWarnings("all") + @lombok.Generated + public static class BuilderWithToStringExcludeOldBuilder { + @java.lang.SuppressWarnings("all") + @lombok.Generated + private String a; + @java.lang.SuppressWarnings("all") + @lombok.Generated + private String secret; + @java.lang.SuppressWarnings("all") + @lombok.Generated + BuilderWithToStringExcludeOldBuilder() { + } + /** + * @return {@code this}. + */ + @java.lang.SuppressWarnings("all") + @lombok.Generated + public BuilderWithToStringExcludeOld.BuilderWithToStringExcludeOldBuilder a(final String a) { + this.a = a; + return this; + } + /** + * @return {@code this}. + */ + @java.lang.SuppressWarnings("all") + @lombok.Generated + public BuilderWithToStringExcludeOld.BuilderWithToStringExcludeOldBuilder secret(final String secret) { + this.secret = secret; + return this; + } + @java.lang.SuppressWarnings("all") + @lombok.Generated + public BuilderWithToStringExcludeOld build() { + return new BuilderWithToStringExcludeOld(this.a, this.secret); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + @lombok.Generated + public java.lang.String toString() { + return "BuilderWithToStringExcludeOld.BuilderWithToStringExcludeOldBuilder(a=" + this.a + ")"; + } + } + @java.lang.SuppressWarnings("all") + @lombok.Generated + public static BuilderWithToStringExcludeOld.BuilderWithToStringExcludeOldBuilder builder() { + return new BuilderWithToStringExcludeOld.BuilderWithToStringExcludeOldBuilder(); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + @lombok.Generated + public java.lang.String toString() { + return "BuilderWithToStringExcludeOld(a=" + this.a + ")"; + } +} diff --git a/test/transform/resource/after-ecj/BuilderWithToStringExclude.java b/test/transform/resource/after-ecj/BuilderWithToStringExclude.java new file mode 100644 index 0000000000..d59a5dfc91 --- /dev/null +++ b/test/transform/resource/after-ecj/BuilderWithToStringExclude.java @@ -0,0 +1,42 @@ +import lombok.Builder; +import lombok.ToString; +import lombok.ToString.Exclude; +public @Builder class BuilderWithToStringExclude { + public static @java.lang.SuppressWarnings("all") @lombok.Generated class BuilderWithToStringExcludeBuilder { + private @java.lang.SuppressWarnings("all") @lombok.Generated String a; + private @java.lang.SuppressWarnings("all") @lombok.Generated String secret; + @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeBuilder() { + super(); + } + /** + * @return {@code this}. + */ + public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExclude.BuilderWithToStringExcludeBuilder a(final String a) { + this.a = a; + return this; + } + /** + * @return {@code this}. + */ + public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExclude.BuilderWithToStringExcludeBuilder secret(final String secret) { + this.secret = secret; + return this; + } + public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExclude build() { + return new BuilderWithToStringExclude(this.a, this.secret); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() { + return (("BuilderWithToStringExclude.BuilderWithToStringExcludeBuilder(a=" + this.a) + ")"); + } + } + private String a; + private @ToString.Exclude String secret; + @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExclude(final String a, final String secret) { + super(); + this.a = a; + this.secret = secret; + } + public static @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExclude.BuilderWithToStringExcludeBuilder builder() { + return new BuilderWithToStringExclude.BuilderWithToStringExcludeBuilder(); + } +} diff --git a/test/transform/resource/after-ecj/BuilderWithToStringExcludeAndToString.java b/test/transform/resource/after-ecj/BuilderWithToStringExcludeAndToString.java new file mode 100644 index 0000000000..8c0c83c594 --- /dev/null +++ b/test/transform/resource/after-ecj/BuilderWithToStringExcludeAndToString.java @@ -0,0 +1,45 @@ +import lombok.Builder; +import lombok.ToString; +import lombok.ToString.Exclude; +public @Builder @ToString class BuilderWithToStringExcludeAndToString { + public static @java.lang.SuppressWarnings("all") @lombok.Generated class BuilderWithToStringExcludeAndToStringBuilder { + private @java.lang.SuppressWarnings("all") @lombok.Generated String a; + private @java.lang.SuppressWarnings("all") @lombok.Generated String secret; + @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeAndToStringBuilder() { + super(); + } + /** + * @return {@code this}. + */ + public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeAndToString.BuilderWithToStringExcludeAndToStringBuilder a(final String a) { + this.a = a; + return this; + } + /** + * @return {@code this}. + */ + public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeAndToString.BuilderWithToStringExcludeAndToStringBuilder secret(final String secret) { + this.secret = secret; + return this; + } + public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeAndToString build() { + return new BuilderWithToStringExcludeAndToString(this.a, this.secret); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() { + return (("BuilderWithToStringExcludeAndToString.BuilderWithToStringExcludeAndToStringBuilder(a=" + this.a) + ")"); + } + } + private String a; + private @ToString.Exclude String secret; + @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeAndToString(final String a, final String secret) { + super(); + this.a = a; + this.secret = secret; + } + public static @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeAndToString.BuilderWithToStringExcludeAndToStringBuilder builder() { + return new BuilderWithToStringExcludeAndToString.BuilderWithToStringExcludeAndToStringBuilder(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() { + return (("BuilderWithToStringExcludeAndToString(a=" + this.a) + ")"); + } +} diff --git a/test/transform/resource/after-ecj/BuilderWithToStringExcludeOld.java b/test/transform/resource/after-ecj/BuilderWithToStringExcludeOld.java new file mode 100644 index 0000000000..658800d0e7 --- /dev/null +++ b/test/transform/resource/after-ecj/BuilderWithToStringExcludeOld.java @@ -0,0 +1,44 @@ +import lombok.Builder; +import lombok.ToString; +public @ToString(exclude = "secret") @Builder class BuilderWithToStringExcludeOld { + public static @java.lang.SuppressWarnings("all") @lombok.Generated class BuilderWithToStringExcludeOldBuilder { + private @java.lang.SuppressWarnings("all") @lombok.Generated String a; + private @java.lang.SuppressWarnings("all") @lombok.Generated String secret; + @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeOldBuilder() { + super(); + } + /** + * @return {@code this}. + */ + public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeOld.BuilderWithToStringExcludeOldBuilder a(final String a) { + this.a = a; + return this; + } + /** + * @return {@code this}. + */ + public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeOld.BuilderWithToStringExcludeOldBuilder secret(final String secret) { + this.secret = secret; + return this; + } + public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeOld build() { + return new BuilderWithToStringExcludeOld(this.a, this.secret); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() { + return (("BuilderWithToStringExcludeOld.BuilderWithToStringExcludeOldBuilder(a=" + this.a) + ")"); + } + } + private String a; + private String secret; + @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeOld(final String a, final String secret) { + super(); + this.a = a; + this.secret = secret; + } + public static @java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithToStringExcludeOld.BuilderWithToStringExcludeOldBuilder builder() { + return new BuilderWithToStringExcludeOld.BuilderWithToStringExcludeOldBuilder(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() { + return (("BuilderWithToStringExcludeOld(a=" + this.a) + ")"); + } +} diff --git a/test/transform/resource/before/BuilderWithToStringExclude.java b/test/transform/resource/before/BuilderWithToStringExclude.java new file mode 100644 index 0000000000..ccbfc665b0 --- /dev/null +++ b/test/transform/resource/before/BuilderWithToStringExclude.java @@ -0,0 +1,10 @@ +import lombok.Builder; +import lombok.ToString; +import lombok.ToString.Exclude; + +@Builder +public class BuilderWithToStringExclude { + private String a; + @ToString.Exclude + private String secret; +} \ No newline at end of file diff --git a/test/transform/resource/before/BuilderWithToStringExcludeAndToString.java b/test/transform/resource/before/BuilderWithToStringExcludeAndToString.java new file mode 100644 index 0000000000..33c125f729 --- /dev/null +++ b/test/transform/resource/before/BuilderWithToStringExcludeAndToString.java @@ -0,0 +1,11 @@ +import lombok.Builder; +import lombok.ToString; +import lombok.ToString.Exclude; + +@Builder +@ToString +public class BuilderWithToStringExcludeAndToString { + private String a; + @ToString.Exclude + private String secret; +} \ No newline at end of file diff --git a/test/transform/resource/before/BuilderWithToStringExcludeOld.java b/test/transform/resource/before/BuilderWithToStringExcludeOld.java new file mode 100644 index 0000000000..4dd93372e6 --- /dev/null +++ b/test/transform/resource/before/BuilderWithToStringExcludeOld.java @@ -0,0 +1,9 @@ +import lombok.Builder; +import lombok.ToString; + +@ToString(exclude = "secret") +@Builder +public class BuilderWithToStringExcludeOld { + private String a; + private String secret; +} \ No newline at end of file