From 6d2a474e55db3937eafa5a6d089efd5ba75a62bf Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Tue, 21 Dec 2021 01:53:10 +0100 Subject: [fixes #2849] Make ToString's onlyExplicitlyIncluded a config key. --- src/core/lombok/ConfigurationKeys.java | 7 +++++++ src/core/lombok/core/AST.java | 5 +++++ .../core/handlers/InclusionExclusionUtils.java | 24 +++++++++++++--------- .../lombok/eclipse/handlers/HandleToString.java | 13 ++++++------ src/core/lombok/javac/handlers/HandleToString.java | 18 ++++++++-------- .../ToStringExplicitIncludeConf.java | 9 ++++++++ .../after-ecj/ToStringExplicitIncludeConf.java | 10 +++++++++ .../before/ToStringExplicitIncludeConf.java | 7 +++++++ website/templates/features/ToString.html | 5 +++++ 9 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 test/transform/resource/after-delombok/ToStringExplicitIncludeConf.java create mode 100644 test/transform/resource/after-ecj/ToStringExplicitIncludeConf.java create mode 100644 test/transform/resource/before/ToStringExplicitIncludeConf.java diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 05550a06..457246e7 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -291,6 +291,13 @@ public class ConfigurationKeys { */ public static final ConfigurationKey TO_STRING_INCLUDE_FIELD_NAMES = new ConfigurationKey("lombok.toString.includeFieldNames", "Include the field names in the generated toString method (default = true).") {}; + /** + * lombok configuration: {@code lombok.toString.onlyExplicitlyIncluded} = {@code true} | {@code false}. + * + * If {@code true}, require a {@code @ToString.Include} annotation on any fields/no-args methods you want to include in lombok's generated `@ToString` method. Otherwise, every (non-static, non-dollar-named) field is included by default (default = false). + */ + public static final ConfigurationKey TO_STRING_ONLY_EXPLICITLY_INCLUDED = new ConfigurationKey("lombok.toString.onlyExplicitlyIncluded", "Include only fields/methods explicitly marked with @ToString.Include. Otherwise, include all non-static, non-dollar-named fields (default = false).") {}; + // ----- Builder ----- /** diff --git a/src/core/lombok/core/AST.java b/src/core/lombok/core/AST.java index 07d035c5..738241e2 100755 --- a/src/core/lombok/core/AST.java +++ b/src/core/lombok/core/AST.java @@ -454,4 +454,9 @@ public abstract class AST, L extends LombokNode, if (configTracker != null) configTracker.end(start); } } + + public boolean getBooleanAnnotationValue(AnnotationValues annotation, String annoMethod, ConfigurationKey confKey) { + Boolean conf = readConfiguration(confKey); + return annotation.isExplicit(annoMethod) || conf == null ? annotation.getAsBoolean(annoMethod) : conf; + } } diff --git a/src/core/lombok/core/handlers/InclusionExclusionUtils.java b/src/core/lombok/core/handlers/InclusionExclusionUtils.java index 0aa6c47b..2a519b82 100644 --- a/src/core/lombok/core/handlers/InclusionExclusionUtils.java +++ b/src/core/lombok/core/handlers/InclusionExclusionUtils.java @@ -113,10 +113,14 @@ public class InclusionExclusionUtils { } private static , L extends LombokNode, N, I extends Annotation> List> handleIncludeExcludeMarking(Class inclType, String replaceName, Class exclType, LombokNode typeNode, AnnotationValues annotation, LombokNode annotationNode, boolean includeTransient) { + boolean onlyExplicitlyIncluded = annotation != null ? annotation.getAsBoolean("onlyExplicitlyIncluded") : false; + return handleIncludeExcludeMarking(inclType, onlyExplicitlyIncluded, replaceName, exclType, typeNode, annotation, annotationNode, includeTransient); + } + + private static , L extends LombokNode, N, I extends Annotation> List> handleIncludeExcludeMarking(Class inclType, boolean onlyExplicitlyIncluded, String replaceName, Class exclType, LombokNode typeNode, AnnotationValues annotation, LombokNode annotationNode, boolean includeTransient) { List oldExcludes = (annotation != null && annotation.isExplicit("exclude")) ? annotation.getAsStringList("exclude") : null; List oldIncludes = (annotation != null && annotation.isExplicit("of")) ? annotation.getAsStringList("of") : null; - boolean onlyExplicitlyIncluded = annotation != null ? annotation.getAsBoolean("onlyExplicitlyIncluded") : false; boolean memberAnnotationMode = onlyExplicitlyIncluded; List> members = new ArrayList>(); List namesToAutoExclude = new ArrayList(); @@ -203,14 +207,14 @@ public class InclusionExclusionUtils { return members; } - public static , L extends LombokNode, N> List> handleToStringMarking(LombokNode typeNode, AnnotationValues annotation, LombokNode annotationNode) { - List> members = handleIncludeExcludeMarking(ToString.Include.class, "name", ToString.Exclude.class, typeNode, annotation, annotationNode, true); + public static , L extends LombokNode, N> List> handleToStringMarking(LombokNode typeNode, boolean onlyExplicitlyIncluded, AnnotationValues annotation, LombokNode annotationNode) { + List> members = handleIncludeExcludeMarking(ToString.Include.class, onlyExplicitlyIncluded, "name", ToString.Exclude.class, typeNode, annotation, annotationNode, true); Collections.sort(members, new Comparator>() { @Override public int compare(Included a, Included b) { int ra = a.getInc() == null ? 0 : a.getInc().rank(); int rb = b.getInc() == null ? 0 : b.getInc().rank(); - + return compareRankOrPosition(ra, rb, a.getNode(), b.getNode()); } }); @@ -219,28 +223,28 @@ public class InclusionExclusionUtils { public static , L extends LombokNode, N> List> handleEqualsAndHashCodeMarking(LombokNode typeNode, AnnotationValues annotation, LombokNode annotationNode) { List> members = handleIncludeExcludeMarking(EqualsAndHashCode.Include.class, "replaces", EqualsAndHashCode.Exclude.class, typeNode, annotation, annotationNode, false); - + Collections.sort(members, new Comparator>() { @Override public int compare(Included a, Included b) { int ra = a.hasExplicitRank() ? a.getInc().rank() : HandlerUtil.defaultEqualsAndHashcodeIncludeRank(a.node.fieldOrMethodBaseType()); int rb = b.hasExplicitRank() ? b.getInc().rank() : HandlerUtil.defaultEqualsAndHashcodeIncludeRank(b.node.fieldOrMethodBaseType()); - + return compareRankOrPosition(ra, rb, a.getNode(), b.getNode()); } }); return members; } - + private static , L extends LombokNode, N> int compareRankOrPosition(int ra, int rb, LombokNode nodeA, LombokNode nodeB) { if (ra < rb) return +1; if (ra > rb) return -1; - + int pa = nodeA.getStartPos(); int pb = nodeB.getStartPos(); - + if (pa < pb) return -1; if (pa > pb) return +1; - + return 0; } } diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index 05b0e069..6beaa848 100644 --- a/src/core/lombok/eclipse/handlers/HandleToString.java +++ b/src/core/lombok/eclipse/handlers/HandleToString.java @@ -76,7 +76,8 @@ public class HandleToString extends EclipseAnnotationHandler { handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString"); ToString ann = annotation.getInstance(); - List> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode); + boolean onlyExplicitlyIncluded = annotationNode.getAst().getBooleanAnnotationValue(annotation, "onlyExplicitlyIncluded", ConfigurationKeys.TO_STRING_ONLY_EXPLICITLY_INCLUDED); + List> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), onlyExplicitlyIncluded, annotation, annotationNode); if (members == null) return; Boolean callSuper = ann.callSuper(); @@ -99,16 +100,14 @@ public class HandleToString extends EclipseAnnotationHandler { return; } - boolean includeFieldNames = true; - try { - Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); - includeFieldNames = configuration != null ? configuration : ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); - } catch (Exception ignore) {} + AnnotationValues anno = AnnotationValues.of(ToString.class); + boolean includeFieldNames = typeNode.getAst().getBooleanAnnotationValue(anno, "includeFieldNames", ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); + boolean onlyExplicitlyIncluded = typeNode.getAst().getBooleanAnnotationValue(anno, "onlyExplicitlyIncluded", ConfigurationKeys.TO_STRING_ONLY_EXPLICITLY_INCLUDED); Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; - List> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null); + List> members = InclusionExclusionUtils.handleToStringMarking(typeNode, onlyExplicitlyIncluded, null, null); generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access); } diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index 249993ee..8a0bc686 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -65,7 +65,8 @@ public class HandleToString extends JavacAnnotationHandler { deleteAnnotationIfNeccessary(annotationNode, ToString.class); ToString ann = annotation.getInstance(); - java.util.List> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode); + boolean onlyExplicitlyIncluded = annotationNode.getAst().getBooleanAnnotationValue(annotation, "onlyExplicitlyIncluded", ConfigurationKeys.TO_STRING_ONLY_EXPLICITLY_INCLUDED); + java.util.List> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), onlyExplicitlyIncluded, annotation, annotationNode); if (members == null) return; Boolean callSuper = ann.callSuper(); @@ -76,10 +77,9 @@ public class HandleToString extends JavacAnnotationHandler { boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; - Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); - boolean includeNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration; + boolean includeFieldNames = annotationNode.getAst().getBooleanAnnotationValue(annotation, "includeFieldNames", ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); - generateToString(annotationNode.up(), annotationNode, members, includeNames, callSuper, true, fieldAccess); + generateToString(annotationNode.up(), annotationNode, members, includeFieldNames, callSuper, true, fieldAccess); } public void generateToStringForType(JavacNode typeNode, JavacNode errorNode) { @@ -88,16 +88,14 @@ public class HandleToString extends JavacAnnotationHandler { return; } - boolean includeFieldNames = true; - try { - Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); - includeFieldNames = configuration != null ? configuration : ((Boolean) ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); - } catch (Exception ignore) {} + AnnotationValues anno = AnnotationValues.of(ToString.class); + boolean includeFieldNames = typeNode.getAst().getBooleanAnnotationValue(anno, "includeFieldNames", ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); + boolean onlyExplicitlyIncluded = typeNode.getAst().getBooleanAnnotationValue(anno, "onlyExplicitlyIncluded", ConfigurationKeys.TO_STRING_ONLY_EXPLICITLY_INCLUDED); Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; - java.util.List> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null); + java.util.List> members = InclusionExclusionUtils.handleToStringMarking(typeNode, onlyExplicitlyIncluded, null, null); generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access); } diff --git a/test/transform/resource/after-delombok/ToStringExplicitIncludeConf.java b/test/transform/resource/after-delombok/ToStringExplicitIncludeConf.java new file mode 100644 index 00000000..a1bd8ed9 --- /dev/null +++ b/test/transform/resource/after-delombok/ToStringExplicitIncludeConf.java @@ -0,0 +1,9 @@ +class ToStringExplicitIncludeConf { + int x; + int y; + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "ToStringExplicitIncludeConf(" + this.y + ")"; + } +} diff --git a/test/transform/resource/after-ecj/ToStringExplicitIncludeConf.java b/test/transform/resource/after-ecj/ToStringExplicitIncludeConf.java new file mode 100644 index 00000000..a2b801c6 --- /dev/null +++ b/test/transform/resource/after-ecj/ToStringExplicitIncludeConf.java @@ -0,0 +1,10 @@ +@lombok.ToString class ToStringExplicitIncludeConf { + int x; + @lombok.ToString.Include int y; + ToStringExplicitIncludeConf() { + super(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("ToStringExplicitIncludeConf(y=" + this.y) + ")"); + } +} diff --git a/test/transform/resource/before/ToStringExplicitIncludeConf.java b/test/transform/resource/before/ToStringExplicitIncludeConf.java new file mode 100644 index 00000000..ee49c754 --- /dev/null +++ b/test/transform/resource/before/ToStringExplicitIncludeConf.java @@ -0,0 +1,7 @@ +//CONF: lombok.toString.onlyExplicitlyIncluded = true + +@lombok.ToString +class ToStringExplicitIncludeConf { + int x; + @lombok.ToString.Include int y; +} diff --git a/website/templates/features/ToString.html b/website/templates/features/ToString.html index 456092d5..87e06649 100644 --- a/website/templates/features/ToString.html +++ b/website/templates/features/ToString.html @@ -32,6 +32,11 @@ lombok.toString.callSuper = [call | skip | warn] (default: skip)
If set to call, lombok will generate calls to the superclass implementation of toString if your class extends something. If set to skip no such call is generated. If set to warn no such call is generated either, but lombok does generate a warning to tell you about it. +
+ lombok.toString.onlyExplicitlyIncluded = [true | false] (default: false) +
+ If set to false (default), all fields (unless static, name starts with a dollar, or otherwise excluded for obvious reasons) serve as the default set of things to include in the toString, modifiable by using the @ToString.Exclude and @ToString.Include options. + If set to true, nothing is included unless explicitly marked with @ToString.Include.
lombok.toString.flagUsage = [warning | error] (default: not set)
-- cgit