From 261758b4448b3d48ff2f48926ffcb8ea66121603 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Tue, 8 Feb 2022 06:02:05 +0100 Subject: [implements #1456] accessors can now be made final via `@Accessors`. --- doc/changelog.markdown | 1 + src/core/lombok/ConfigurationKeys.java | 11 ++- src/core/lombok/core/AnnotationValues.java | 23 +++++- src/core/lombok/core/handlers/HandlerUtil.java | 10 ++- .../eclipse/handlers/EclipseHandlerUtil.java | 94 +++++++++++++++++++-- .../lombok/eclipse/handlers/HandleAccessors.java | 3 +- src/core/lombok/eclipse/handlers/HandleGetter.java | 10 ++- src/core/lombok/eclipse/handlers/HandleSetter.java | 12 ++- src/core/lombok/eclipse/handlers/HandleWith.java | 12 ++- src/core/lombok/eclipse/handlers/HandleWithBy.java | 12 ++- src/core/lombok/experimental/Accessors.java | 17 +++- .../lombok/javac/handlers/HandleAccessors.java | 3 +- src/core/lombok/javac/handlers/HandleGetter.java | 16 ++-- src/core/lombok/javac/handlers/HandleSetter.java | 21 +++-- src/core/lombok/javac/handlers/HandleWith.java | 13 ++- src/core/lombok/javac/handlers/HandleWithBy.java | 11 ++- .../lombok/javac/handlers/JavacHandlerUtil.java | 96 ++++++++++++++++++++-- .../resource/after-delombok/AccessorsCascade.java | 39 +++++++++ .../after-delombok/AccessorsMakeFinal.java | 11 +++ .../AccessorsMakeFinalLombokConfig.java | 7 ++ .../after-delombok/AccessorsNoParamWarning.java | 14 ++++ test/transform/resource/after-ecj/Accessors.java | 2 +- .../resource/after-ecj/AccessorsCascade.java | 43 ++++++++++ .../resource/after-ecj/AccessorsMakeFinal.java | 13 +++ .../after-ecj/AccessorsMakeFinalLombokConfig.java | 9 ++ .../after-ecj/AccessorsNoParamWarning.java | 18 ++++ test/transform/resource/before/Accessors.java | 2 +- .../resource/before/AccessorsCascade.java | 23 ++++++ .../resource/before/AccessorsMakeFinal.java | 5 ++ .../before/AccessorsMakeFinalLombokConfig.java | 6 ++ .../resource/before/AccessorsNoParamWarning.java | 9 ++ .../AccessorsNoParamWarning.java.messages | 2 + .../AccessorsNoParamWarning.java.messages | 2 + .../templates/features/experimental/Accessors.html | 28 +++++-- 34 files changed, 527 insertions(+), 71 deletions(-) create mode 100644 test/transform/resource/after-delombok/AccessorsCascade.java create mode 100644 test/transform/resource/after-delombok/AccessorsMakeFinal.java create mode 100644 test/transform/resource/after-delombok/AccessorsMakeFinalLombokConfig.java create mode 100644 test/transform/resource/after-delombok/AccessorsNoParamWarning.java create mode 100644 test/transform/resource/after-ecj/AccessorsCascade.java create mode 100644 test/transform/resource/after-ecj/AccessorsMakeFinal.java create mode 100644 test/transform/resource/after-ecj/AccessorsMakeFinalLombokConfig.java create mode 100644 test/transform/resource/after-ecj/AccessorsNoParamWarning.java create mode 100644 test/transform/resource/before/AccessorsCascade.java create mode 100644 test/transform/resource/before/AccessorsMakeFinal.java create mode 100644 test/transform/resource/before/AccessorsMakeFinalLombokConfig.java create mode 100644 test/transform/resource/before/AccessorsNoParamWarning.java create mode 100644 test/transform/resource/messages-delombok/AccessorsNoParamWarning.java.messages create mode 100644 test/transform/resource/messages-ecj/AccessorsNoParamWarning.java.messages diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 9c22025d..0ba93306 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -5,6 +5,7 @@ Lombok Changelog * FEATURE: `@ToString` has an annotation parameter called `onlyExplicitlyIncluded`. There's now a config key `lombok.toString.onlyExplicitlyIncluded` to set this property as well. [Issue #2849](https://github.com/projectlombok/lombok/pull/2849). * FEATURE: Turning a field named `uShape` into a getter is tricky: `getUShape` or `getuShape`? The community is split on which style to use. Lombok does `getUShape`, but if you prefer the `getuShape` style, add to `lombok.config`: `lombok.accessors.capitalization = beanspec`. [Issue #2693](https://github.com/projectlombok/lombok/issues/2693) [Pull Request #2996](https://github.com/projectlombok/lombok/pull/2996). Thanks __@YonathanSherwin__! +* FEATURE: You can now use `@Accessors(makeFinal = true)` to make `final` getters, setters, and with-ers. [Issue #1456](https://github.com/projectlombok/lombok/issues/1456) * BUGFIX: Various save actions and refactor scripts in eclipse work better. [Issue #2995](https://github.com/projectlombok/lombok/issues/2995) [Issue #1309](https://github.com/projectlombok/lombok/issues/1309) [Issue #2985](https://github.com/projectlombok/lombok/issues/2985) [Issue #2509](https://github.com/projectlombok/lombok/issues/2509) * BUGFIX: Eclipse projects using the jasperreports-plugin will now compile [Issue #1036](https://github.com/projectlombok/lombok/issues/1036) * SECURITY: A widely reported security issue with log4j2 ([CVE-2021-44228](https://www.randori.com/blog/cve-2021-44228/)) has absolutely no effect on either lombok itself nor does usage of lombok on its own, or even the usage of lombok's `@Log4j2`, cause any issues whatsoever: You have to ship your own log4j2 dependency in your app - update that to 2.17 or otherwise mitigate this issue (see the CVE page). To avoid unneccessary warnings from dependency checkers, our dep on log4j2, which is used solely for testing, isn't shipped by us, and cannot be exploited in any way, has been updated to 2.17.1. [Issue #3063](https://github.com/projectlombok/lombok/issues/3063) diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 457246e7..22d5a4c5 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 The Project Lombok Authors. + * Copyright (C) 2013-2022 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 @@ -550,7 +550,7 @@ public class ConfigurationKeys { * * For any class without an {@code @Accessors} that explicitly defines the {@code prefix} option, this list of prefixes is used. */ - public static final ConfigurationKey> ACCESSORS_PREFIX = new ConfigurationKey>("lombok.accessors.prefix", "Strip this field prefix, like 'f' or 'm_', from the names of generated getters and setters.") {}; + public static final ConfigurationKey> ACCESSORS_PREFIX = new ConfigurationKey>("lombok.accessors.prefix", "Strip this field prefix, like 'f' or 'm_', from the names of generated getters, setters, and with-ers.") {}; /** * lombok configuration: {@code lombok.accessors.chain} = {@code true} | {@code false}. @@ -566,6 +566,13 @@ public class ConfigurationKeys { */ public static final ConfigurationKey ACCESSORS_FLUENT = new ConfigurationKey("lombok.accessors.fluent", "Generate getters and setters using only the field name (no get/set prefix) (default: false).") {}; + /** + * lombok configuration: {@code lombok.accessors.makeFinal} = {@code true} | {@code false}. + * + * Unless an explicit {@code @Accessors} that explicitly defines the {@code makeFinal} option, this value is used (default = false). + */ + public static final ConfigurationKey ACCESSORS_MAKE_FINAL = new ConfigurationKey("lombok.accessors.makeFinal", "Generate getters, setters and with-ers with the 'final' modifier (default: false).") {}; + /** * lombok configuration: {@code lombok.accessors.capitalization} = {@code basic} | {@code beanspec}. * diff --git a/src/core/lombok/core/AnnotationValues.java b/src/core/lombok/core/AnnotationValues.java index f5db553c..390e9b71 100644 --- a/src/core/lombok/core/AnnotationValues.java +++ b/src/core/lombok/core/AnnotationValues.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2022 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 @@ -550,4 +550,25 @@ public class AnnotationValues { result.append(typeName); return result.toString(); } + + /** + * Creates an amalgamation where any values in this AnnotationValues that aren't explicit are 'enriched' by explicitly set stuff from {@code defaults}. + * Note that this code may modify self and then returns self, or it returns defaults - do not rely on immutability nor on getting self. + */ + public AnnotationValues integrate(AnnotationValues defaults) { + if (values.isEmpty()) return defaults; + for (Map.Entry entry : defaults.values.entrySet()) { + if (!entry.getValue().isExplicit) continue; + AnnotationValue existingValue = values.get(entry.getKey()); + if (existingValue != null && existingValue.isExplicit) continue; + values.put(entry.getKey(), entry.getValue()); + } + return this; + } + + /** Returns {@code true} if the annotation has zero parameters. */ + public boolean isMarking() { + for (AnnotationValue v : values.values()) if (v.isExplicit) return false; + return true; + } } diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 2415b750..039ce870 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 The Project Lombok Authors. + * Copyright (C) 2013-2022 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 @@ -555,6 +555,14 @@ public class HandlerUtil { return chain || fluent; } + public static boolean shouldMakeFinal0(AnnotationValues accessors, AST ast) { + boolean isExplicit = accessors.isExplicit("makeFinal"); + if (isExplicit) return accessors.getAsBoolean("makeFinal"); + Boolean config = ast.readConfiguration(ConfigurationKeys.ACCESSORS_MAKE_FINAL); + if (config != null) return config.booleanValue(); + return false; + } + @SuppressWarnings({"all", "unchecked", "deprecation"}) public static final List INVALID_ON_BUILDERS = Collections.unmodifiableList( Arrays.asList( diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 6483a749..65e2c5c8 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2009-2022 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 @@ -1631,6 +1631,14 @@ public class EclipseHandlerUtil { return HandlerUtil.toAllGetterNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } + /** + * Translates the given field into all possible getter names. + * Convenient wrapper around {@link HandlerUtil#toAllGetterNames(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static List toAllGetterNames(EclipseNode field, boolean isBoolean, AnnotationValues accessors) { + return HandlerUtil.toAllGetterNames(field.getAst(), accessors, field.getName(), isBoolean); + } + /** * @return the likely getter name for the stated field. (e.g. private boolean foo; to isFoo). * @@ -1640,6 +1648,15 @@ public class EclipseHandlerUtil { return HandlerUtil.toGetterName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } + /** + * @return the likely getter name for the stated field. (e.g. private boolean foo; to isFoo). + * + * Convenient wrapper around {@link HandlerUtil#toGetterName(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static String toGetterName(EclipseNode field, boolean isBoolean, AnnotationValues accessors) { + return HandlerUtil.toGetterName(field.getAst(), accessors, field.getName(), isBoolean); + } + /** * Translates the given field into all possible setter names. * Convenient wrapper around {@link HandlerUtil#toAllSetterNames(lombok.core.AnnotationValues, CharSequence, boolean)}. @@ -1648,6 +1665,14 @@ public class EclipseHandlerUtil { return HandlerUtil.toAllSetterNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } + /** + * Translates the given field into all possible setter names. + * Convenient wrapper around {@link HandlerUtil#toAllSetterNames(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static java.util.List toAllSetterNames(EclipseNode field, boolean isBoolean, AnnotationValues accessors) { + return HandlerUtil.toAllSetterNames(field.getAst(), accessors, field.getName(), isBoolean); + } + /** * @return the likely setter name for the stated field. (e.g. private boolean foo; to setFoo). * @@ -1657,6 +1682,15 @@ public class EclipseHandlerUtil { return HandlerUtil.toSetterName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } + /** + * @return the likely setter name for the stated field. (e.g. private boolean foo; to setFoo). + * + * Convenient wrapper around {@link HandlerUtil#toSetterName(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static String toSetterName(EclipseNode field, boolean isBoolean, AnnotationValues accessors) { + return HandlerUtil.toSetterName(field.getAst(), accessors, field.getName(), isBoolean); + } + /** * Translates the given field into all possible with names. * Convenient wrapper around {@link HandlerUtil#toAllWithNames(lombok.core.AnnotationValues, CharSequence, boolean)}. @@ -1665,6 +1699,14 @@ public class EclipseHandlerUtil { return HandlerUtil.toAllWithNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } + /** + * Translates the given field into all possible with names. + * Convenient wrapper around {@link HandlerUtil#toAllWithNames(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static java.util.List toAllWithNames(EclipseNode field, boolean isBoolean, AnnotationValues accessors) { + return HandlerUtil.toAllWithNames(field.getAst(), accessors, field.getName(), isBoolean); + } + /** * Translates the given field into all possible withBy names. * Convenient wrapper around {@link HandlerUtil#toAllWithByNames(lombok.core.AnnotationValues, CharSequence, boolean)}. @@ -1673,6 +1715,14 @@ public class EclipseHandlerUtil { return HandlerUtil.toAllWithByNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } + /** + * Translates the given field into all possible withBy names. + * Convenient wrapper around {@link HandlerUtil#toAllWithByNames(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static java.util.List toAllWithByNames(EclipseNode field, boolean isBoolean, AnnotationValues accessors) { + return HandlerUtil.toAllWithByNames(field.getAst(), accessors, field.getName(), isBoolean); + } + /** * @return the likely with name for the stated field. (e.g. private boolean foo; to withFoo). * @@ -1682,6 +1732,15 @@ public class EclipseHandlerUtil { return HandlerUtil.toWithName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } + /** + * @return the likely with name for the stated field. (e.g. private boolean foo; to withFoo). + * + * Convenient wrapper around {@link HandlerUtil#toWithName(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static String toWithName(EclipseNode field, boolean isBoolean, AnnotationValues accessors) { + return HandlerUtil.toWithName(field.getAst(), accessors, field.getName(), isBoolean); + } + /** * @return the likely withBy name for the stated field. (e.g. private boolean foo; to withFooBy). * @@ -1691,13 +1750,28 @@ public class EclipseHandlerUtil { return HandlerUtil.toWithByName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } + /** + * @return the likely withBy name for the stated field. (e.g. private boolean foo; to withFooBy). + * + * Convenient wrapper around {@link HandlerUtil#toWithByName(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static String toWithByName(EclipseNode field, boolean isBoolean, AnnotationValues accessors) { + return HandlerUtil.toWithByName(field.getAst(), accessors, field.getName(), isBoolean); + } + + /** + * When generating a setter/getter/wither, should it be made final? + */ + public static boolean shouldMakeFinal(EclipseNode field, AnnotationValues accessors) { + if ((((FieldDeclaration) field.get()).modifiers & ClassFileConstants.AccStatic) != 0) return false; + return shouldMakeFinal0(accessors, field.getAst()); + } /** * When generating a setter, the setter either returns void (beanspec) or Self (fluent). * This method scans for the {@code Accessors} annotation and associated config properties to figure that out. */ - public static boolean shouldReturnThis(EclipseNode field) { + public static boolean shouldReturnThis(EclipseNode field, AnnotationValues accessors) { if ((((FieldDeclaration) field.get()).modifiers & ClassFileConstants.AccStatic) != 0) return false; - AnnotationValues accessors = EclipseHandlerUtil.getAccessorsForField(field); return shouldReturnThis0(accessors, field.getAst()); } @@ -1760,9 +1834,12 @@ public class EclipseHandlerUtil { } public static AnnotationValues getAccessorsForField(EclipseNode field) { + AnnotationValues values = null; + for (EclipseNode node : field.down()) { if (annotationTypeMatches(Accessors.class, node)) { - return createAnnotation(Accessors.class, node); + values = createAnnotation(Accessors.class, node); + break; } } @@ -1770,15 +1847,16 @@ public class EclipseHandlerUtil { while (current != null) { for (EclipseNode node : current.down()) { if (annotationTypeMatches(Accessors.class, node)) { - return createAnnotation(Accessors.class, node); + AnnotationValues onType = createAnnotation(Accessors.class, node); + values = values == null ? onType : values.integrate(onType); } } current = current.up(); } - return AnnotationValues.of(Accessors.class, field); + return values == null ? AnnotationValues.of(Accessors.class, field) : values; } - + public static EclipseNode upToTypeNode(EclipseNode node) { if (node == null) throw new NullPointerException("node"); while (node != null && !(node.get() instanceof TypeDeclaration)) node = node.up(); @@ -2823,7 +2901,7 @@ public class EclipseHandlerUtil { if (!sectionBased) { out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), JavadocTag.RETURN); } - return shouldReturnThis(node) ? addReturnsThisIfNeeded(out) : out; + return shouldReturnThis(node, EclipseHandlerUtil.getAccessorsForField(node)) ? addReturnsThisIfNeeded(out) : out; } } diff --git a/src/core/lombok/eclipse/handlers/HandleAccessors.java b/src/core/lombok/eclipse/handlers/HandleAccessors.java index 6a92dee2..3bb63aa6 100644 --- a/src/core/lombok/eclipse/handlers/HandleAccessors.java +++ b/src/core/lombok/eclipse/handlers/HandleAccessors.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2021 The Project Lombok Authors. + * Copyright (C) 2014-2022 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 @@ -39,5 +39,6 @@ public class HandleAccessors extends EclipseAnnotationHandler { // Accessors itself is handled by HandleGetter/Setter; this is just to ensure that usages are flagged if requested. handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.ACCESSORS_FLAG_USAGE, "@Accessors"); + if (annotation.isMarking()) annotationNode.addWarning("Accessors on its own does nothing. Set at least one parameter"); } } diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java index 7f8fdef2..31236d21 100644 --- a/src/core/lombok/eclipse/handlers/HandleGetter.java +++ b/src/core/lombok/eclipse/handlers/HandleGetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2009-2022 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 @@ -34,6 +34,7 @@ import java.util.Map; import lombok.AccessLevel; import lombok.ConfigurationKeys; +import lombok.experimental.Accessors; import lombok.experimental.Delegate; import lombok.spi.Provides; import lombok.Getter; @@ -190,7 +191,8 @@ public class HandleGetter extends EclipseAnnotationHandler { TypeReference fieldType = copyType(field.type, source); boolean isBoolean = isBoolean(fieldType); - String getterName = toGetterName(fieldNode, isBoolean); + AnnotationValues accessors = getAccessorsForField(fieldNode); + String getterName = toGetterName(fieldNode, isBoolean, accessors); if (getterName == null) { errorNode.addWarning("Not generating getter for this field: It does not fit your @Accessors prefix list."); @@ -199,7 +201,7 @@ public class HandleGetter extends EclipseAnnotationHandler { int modifier = toEclipseModifier(level) | (field.modifiers & ClassFileConstants.AccStatic); - for (String altName : toAllGetterNames(fieldNode, isBoolean)) { + for (String altName : toAllGetterNames(fieldNode, isBoolean, accessors)) { switch (methodExists(altName, fieldNode, false, 0)) { case EXISTS_BY_LOMBOK: return; @@ -247,7 +249,9 @@ public class HandleGetter extends EclipseAnnotationHandler { statements = createSimpleGetterBody(source, fieldNode); } + AnnotationValues accessors = getAccessorsForField(fieldNode); MethodDeclaration method = new MethodDeclaration(parent.compilationResult); + if (shouldMakeFinal(fieldNode, accessors)) modifier |= ClassFileConstants.AccFinal; method.modifiers = modifier; method.returnType = returnType; method.annotations = null; diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index 0fdd058f..fda1651d 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2009-2022 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 @@ -37,6 +37,7 @@ import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.experimental.Accessors; import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.ASTNode; @@ -150,8 +151,9 @@ public class HandleSetter extends EclipseAnnotationHandler { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); TypeReference fieldType = copyType(field.type, source); boolean isBoolean = isBoolean(fieldType); - String setterName = toSetterName(fieldNode, isBoolean); - boolean shouldReturnThis = shouldReturnThis(fieldNode); + AnnotationValues accessors = getAccessorsForField(fieldNode); + String setterName = toSetterName(fieldNode, isBoolean, accessors); + boolean shouldReturnThis = shouldReturnThis(fieldNode, accessors); if (setterName == null) { fieldNode.addWarning("Not generating setter for this field: It does not fit your @Accessors prefix list."); @@ -160,7 +162,7 @@ public class HandleSetter extends EclipseAnnotationHandler { int modifier = toEclipseModifier(level) | (field.modifiers & ClassFileConstants.AccStatic); - for (String altName : toAllSetterNames(fieldNode, isBoolean)) { + for (String altName : toAllSetterNames(fieldNode, isBoolean, accessors)) { switch (methodExists(altName, fieldNode, false, 1)) { case EXISTS_BY_LOMBOK: return; @@ -206,6 +208,8 @@ public class HandleSetter extends EclipseAnnotationHandler { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; MethodDeclaration method = new MethodDeclaration(parent.compilationResult); + AnnotationValues accessors = getAccessorsForField(fieldNode); + if (shouldMakeFinal(fieldNode, accessors)) modifier |= ClassFileConstants.AccFinal; method.modifiers = modifier; if (returnType != null) { method.returnType = returnType; diff --git a/src/core/lombok/eclipse/handlers/HandleWith.java b/src/core/lombok/eclipse/handlers/HandleWith.java index bfad682b..153f0c4a 100644 --- a/src/core/lombok/eclipse/handlers/HandleWith.java +++ b/src/core/lombok/eclipse/handlers/HandleWith.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2021 The Project Lombok Authors. + * Copyright (C) 2012-2022 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 @@ -38,6 +38,7 @@ import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.experimental.Accessors; import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.ASTNode; @@ -169,7 +170,8 @@ public class HandleWith extends EclipseAnnotationHandler { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); TypeReference fieldType = copyType(field.type, source); boolean isBoolean = isBoolean(fieldType); - String withName = toWithName(fieldNode, isBoolean); + AnnotationValues accessors = getAccessorsForField(fieldNode); + String withName = toWithName(fieldNode, isBoolean, accessors); if (withName == null) { fieldNode.addWarning("Not generating a with method for this field: It does not fit your @Accessors prefix list."); @@ -191,7 +193,7 @@ public class HandleWith extends EclipseAnnotationHandler { return; } - for (String altName : toAllWithNames(fieldNode, isBoolean)) { + for (String altName : toAllWithNames(fieldNode, isBoolean, accessors)) { switch (methodExists(altName, fieldNode, false, 1)) { case EXISTS_BY_LOMBOK: return; @@ -222,7 +224,9 @@ public class HandleWith extends EclipseAnnotationHandler { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; MethodDeclaration method = new MethodDeclaration(parent.compilationResult); - if (makeAbstract) modifier = modifier | ClassFileConstants.AccAbstract | ExtraCompilerModifiers.AccSemicolonBody; + AnnotationValues accessors = getAccessorsForField(fieldNode); + if (makeAbstract) modifier |= ClassFileConstants.AccAbstract | ExtraCompilerModifiers.AccSemicolonBody; + if (shouldMakeFinal(fieldNode, accessors)) modifier |= ClassFileConstants.AccFinal; method.modifiers = modifier; method.returnType = cloneSelfType(fieldNode, source); if (method.returnType == null) return null; diff --git a/src/core/lombok/eclipse/handlers/HandleWithBy.java b/src/core/lombok/eclipse/handlers/HandleWithBy.java index a8d13a84..5ab3cf81 100644 --- a/src/core/lombok/eclipse/handlers/HandleWithBy.java +++ b/src/core/lombok/eclipse/handlers/HandleWithBy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2021 The Project Lombok Authors. + * Copyright (C) 2020-2022 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 @@ -61,6 +61,7 @@ import lombok.core.handlers.HandlerUtil.FieldAccess; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.experimental.Accessors; import lombok.experimental.WithBy; import lombok.spi.Provides; @@ -169,7 +170,8 @@ public class HandleWithBy extends EclipseAnnotationHandler { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); TypeReference fieldType = copyType(field.type, source); boolean isBoolean = isBoolean(fieldType); - String withName = toWithByName(fieldNode, isBoolean); + AnnotationValues accessors = getAccessorsForField(fieldNode); + String withName = toWithByName(fieldNode, isBoolean, accessors); if (withName == null) { fieldNode.addWarning("Not generating a withXBy method for this field: It does not fit your @Accessors prefix list."); @@ -191,7 +193,7 @@ public class HandleWithBy extends EclipseAnnotationHandler { return; } - for (String altName : toAllWithByNames(fieldNode, isBoolean)) { + for (String altName : toAllWithByNames(fieldNode, isBoolean, accessors)) { switch (methodExists(altName, fieldNode, false, 1)) { case EXISTS_BY_LOMBOK: return; @@ -242,7 +244,9 @@ public class HandleWithBy extends EclipseAnnotationHandler { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; MethodDeclaration method = new MethodDeclaration(parent.compilationResult); - if (makeAbstract) modifier = modifier | ClassFileConstants.AccAbstract | ExtraCompilerModifiers.AccSemicolonBody; + AnnotationValues accessors = getAccessorsForField(fieldNode); + if (makeAbstract) modifier |= ClassFileConstants.AccAbstract | ExtraCompilerModifiers.AccSemicolonBody; + if (shouldMakeFinal(fieldNode, accessors)) modifier |= ClassFileConstants.AccFinal; method.modifiers = modifier; method.returnType = cloneSelfType(fieldNode, source); if (method.returnType == null) return null; diff --git a/src/core/lombok/experimental/Accessors.java b/src/core/lombok/experimental/Accessors.java index dc9ae4b0..394fe5c4 100644 --- a/src/core/lombok/experimental/Accessors.java +++ b/src/core/lombok/experimental/Accessors.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2017 The Project Lombok Authors. + * Copyright (C) 2012-2022 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 @@ -27,11 +27,11 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * A container for settings for the generation of getters and setters. + * A container for settings for the generation of getters, setters and "with"-ers. *

* Complete documentation is found at the project lombok features page for @Accessors. *

- * Using this annotation does nothing by itself; an annotation that makes lombok generate getters and setters, + * Using this annotation does nothing by itself; an annotation that makes lombok generate getters, setters, or "with"-ers * such as {@link lombok.Setter} or {@link lombok.Data} is also required. */ @Target({ElementType.TYPE, ElementType.FIELD}) @@ -39,7 +39,8 @@ import java.lang.annotation.Target; public @interface Accessors { /** * If true, accessors will be named after the field and not include a {@code get} or {@code set} - * prefix. If true and {@code chain} is omitted, {@code chain} defaults to {@code true}. + * prefix. If true and {@code chain} is omitted, {@code chain} defaults to {@code true}.
+ * NB: This setting has no effect on {@code @With}; they always get a "with" prefix.
* default: false * * @return Whether or not to make fluent methods (named {@code fieldName()}, not for example {@code setFieldName}). @@ -54,6 +55,14 @@ public @interface Accessors { */ boolean chain() default false; + /** + * If true, generated accessors will be marked {@code final}. + * default: false + * + * @return Whether or not accessors should be marked {@code final}. + */ + boolean makeFinal() default false; + /** * If present, only fields with any of the stated prefixes are given the getter/setter treatment. * Note that a prefix only counts if the next character is NOT a lowercase character or the last diff --git a/src/core/lombok/javac/handlers/HandleAccessors.java b/src/core/lombok/javac/handlers/HandleAccessors.java index 60466d78..ac0ace4f 100644 --- a/src/core/lombok/javac/handlers/HandleAccessors.java +++ b/src/core/lombok/javac/handlers/HandleAccessors.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2021 The Project Lombok Authors. + * Copyright (C) 2012-2022 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 @@ -44,5 +44,6 @@ public class HandleAccessors extends JavacAnnotationHandler { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.ACCESSORS_FLAG_USAGE, "@Accessors"); deleteAnnotationIfNeccessary(annotationNode, Accessors.class); + if (annotation.isMarking()) annotationNode.addWarning("Accessors on its own does nothing. Set at least one parameter"); } } diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 7a7e41f9..86eb9fda 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2009-2022 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 @@ -33,6 +33,7 @@ import java.util.Map; import lombok.AccessLevel; import lombok.ConfigurationKeys; +import lombok.experimental.Accessors; import lombok.experimental.Delegate; import lombok.Getter; import lombok.core.AST.Kind; @@ -169,7 +170,7 @@ public class HandleGetter extends JavacAnnotationHandler { return; } - JCVariableDecl fieldDecl = (JCVariableDecl)fieldNode.get(); + JCVariableDecl fieldDecl = (JCVariableDecl) fieldNode.get(); if (lazy) { if ((fieldDecl.mods.flags & Flags.PRIVATE) == 0 || (fieldDecl.mods.flags & Flags.FINAL) == 0) { @@ -186,14 +187,15 @@ public class HandleGetter extends JavacAnnotationHandler { } } - String methodName = toGetterName(fieldNode); + AnnotationValues accessors = getAccessorsForField(fieldNode); + String methodName = toGetterName(fieldNode, accessors); if (methodName == null) { source.addWarning("Not generating getter for this field: It does not fit your @Accessors prefix list."); return; } - for (String altName : toAllGetterNames(fieldNode)) { + for (String altName : toAllGetterNames(fieldNode, accessors)) { switch (methodExists(altName, fieldNode, false, 0)) { case EXISTS_BY_LOMBOK: return; @@ -221,8 +223,10 @@ public class HandleGetter extends JavacAnnotationHandler { // Remember the type; lazy will change it JCExpression methodType = cloneType(treeMaker, copyType(treeMaker, fieldNode), source); + AnnotationValues accessors = JavacHandlerUtil.getAccessorsForField(field); // Generate the methodName; lazy will change the field type - Name methodName = field.toName(toGetterName(field)); + Name methodName = field.toName(toGetterName(field, accessors)); + boolean makeFinal = shouldMakeFinal(field, accessors); List statements; JCTree toClearOfMarkers = null; @@ -260,6 +264,7 @@ public class HandleGetter extends JavacAnnotationHandler { } if (isFieldDeprecated(field)) annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.nil())); + if (makeFinal) access |= Flags.FINAL; JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType, methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source); @@ -270,7 +275,6 @@ public class HandleGetter extends JavacAnnotationHandler { } } decl.mods.annotations = decl.mods.annotations.appendList(delegates); - if (addSuppressWarningsUnchecked) { ListBuffer suppressions = new ListBuffer(); if (!Boolean.FALSE.equals(field.getAst().readConfiguration(ConfigurationKeys.ADD_SUPPRESSWARNINGS_ANNOTATIONS))) { diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 1b675e8c..04fa8b77 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2009-2022 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 @@ -31,6 +31,7 @@ import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.Setter; import lombok.core.AST.Kind; +import lombok.experimental.Accessors; import lombok.core.AnnotationValues; import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; @@ -146,8 +147,9 @@ public class HandleSetter extends JavacAnnotationHandler { return; } + AnnotationValues accessors = JavacHandlerUtil.getAccessorsForField(fieldNode); JCVariableDecl fieldDecl = (JCVariableDecl) fieldNode.get(); - String methodName = toSetterName(fieldNode); + String methodName = toSetterName(fieldNode, accessors); if (methodName == null) { fieldNode.addWarning("Not generating setter for this field: It does not fit your @Accessors prefix list."); @@ -159,7 +161,7 @@ public class HandleSetter extends JavacAnnotationHandler { return; } - for (String altName : toAllSetterNames(fieldNode)) { + for (String altName : toAllSetterNames(fieldNode, accessors)) { switch (methodExists(altName, fieldNode, false, 1)) { case EXISTS_BY_LOMBOK: return; @@ -184,9 +186,11 @@ public class HandleSetter extends JavacAnnotationHandler { } public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, JavacNode source, List onMethod, List onParam) { - String setterName = toSetterName(field); - boolean returnThis = shouldReturnThis(field); - return createSetter(access, false, field, treeMaker, setterName, null, null, returnThis, source, onMethod, onParam); + AnnotationValues accessors = JavacHandlerUtil.getAccessorsForField(field); + String setterName = toSetterName(field, accessors); + boolean returnThis = shouldReturnThis(field, accessors); + JCMethodDecl setter = createSetter(access, false, field, treeMaker, setterName, null, null, returnThis, source, onMethod, onParam); + return setter; } public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name paramName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List onMethod, List onParam) { @@ -198,8 +202,7 @@ public class HandleSetter extends JavacAnnotationHandler { returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this"))); } - JCMethodDecl d = createSetter(access, deprecate, field, treeMaker, setterName, paramName, booleanFieldToSet, returnType, returnStatement, source, onMethod, onParam); - return d; + return createSetter(access, deprecate, field, treeMaker, setterName, paramName, booleanFieldToSet, returnType, returnStatement, source, onMethod, onParam); } public static JCMethodDecl createSetterWithRecv(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name paramName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List onMethod, List onParam, JCVariableDecl recv) { @@ -270,6 +273,8 @@ public class HandleSetter extends JavacAnnotationHandler { annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.nil())); } + AnnotationValues accessors = JavacHandlerUtil.getAccessorsForField(field); + if (shouldMakeFinal(field, accessors)) access |= Flags.FINAL; JCMethodDecl methodDef; if (recv != null && treeMaker.hasMethodDefWithRecvParam()) { methodDef = treeMaker.MethodDefWithRecvParam(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType, diff --git a/src/core/lombok/javac/handlers/HandleWith.java b/src/core/lombok/javac/handlers/HandleWith.java index 47f78b1e..c7fa0531 100644 --- a/src/core/lombok/javac/handlers/HandleWith.java +++ b/src/core/lombok/javac/handlers/HandleWith.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2021 The Project Lombok Authors. + * Copyright (C) 2012-2022 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 @@ -33,6 +33,7 @@ import lombok.With; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.configuration.CheckerFrameworkVersion; +import lombok.experimental.Accessors; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -158,8 +159,9 @@ public class HandleWith extends JavacAnnotationHandler { return; } + AnnotationValues accessors = getAccessorsForField(fieldNode); JCVariableDecl fieldDecl = (JCVariableDecl) fieldNode.get(); - String methodName = toWithName(fieldNode); + String methodName = toWithName(fieldNode, accessors); if (methodName == null) { fieldNode.addWarning("Not generating a withX method for this field: It does not fit your @Accessors prefix list."); @@ -187,7 +189,7 @@ public class HandleWith extends JavacAnnotationHandler { return; } - for (String altName : toAllWithNames(fieldNode)) { + for (String altName : toAllWithNames(fieldNode, accessors)) { switch (methodExists(altName, fieldNode, false, 1)) { case EXISTS_BY_LOMBOK: return; @@ -282,7 +284,10 @@ public class HandleWith extends JavacAnnotationHandler { if (isFieldDeprecated(field)) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.nil())); - if (makeAbstract) access = access | Flags.ABSTRACT; + if (makeAbstract) access |= Flags.ABSTRACT; + AnnotationValues accessors = JavacHandlerUtil.getAccessorsForField(field); + boolean makeFinal = shouldMakeFinal(field, accessors); + if (makeFinal) access |= Flags.FINAL; JCMethodDecl decl = recursiveSetGeneratedBy(maker.MethodDef(maker.Modifiers(access, annsOnMethod), methodName, returnType, methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source); copyJavadoc(field, decl, CopyJavadoc.WITH); diff --git a/src/core/lombok/javac/handlers/HandleWithBy.java b/src/core/lombok/javac/handlers/HandleWithBy.java index 4ba4337e..ff67fd9f 100644 --- a/src/core/lombok/javac/handlers/HandleWithBy.java +++ b/src/core/lombok/javac/handlers/HandleWithBy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2021 The Project Lombok Authors. + * Copyright (C) 2020-2022 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 @@ -34,6 +34,7 @@ import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.LombokImmutableList; import lombok.core.configuration.CheckerFrameworkVersion; +import lombok.experimental.Accessors; import lombok.experimental.WithBy; import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; @@ -158,8 +159,9 @@ public class HandleWithBy extends JavacAnnotationHandler { return; } + AnnotationValues accessors = JavacHandlerUtil.getAccessorsForField(fieldNode); JCVariableDecl fieldDecl = (JCVariableDecl) fieldNode.get(); - String methodName = toWithByName(fieldNode); + String methodName = toWithByName(fieldNode, accessors); if (methodName == null) { fieldNode.addWarning("Not generating a withXBy method for this field: It does not fit your @Accessors prefix list."); @@ -181,7 +183,7 @@ public class HandleWithBy extends JavacAnnotationHandler { return; } - for (String altName : toAllWithByNames(fieldNode)) { + for (String altName : toAllWithByNames(fieldNode, accessors)) { switch (methodExists(altName, fieldNode, false, 1)) { case EXISTS_BY_LOMBOK: return; @@ -326,6 +328,9 @@ public class HandleWithBy extends JavacAnnotationHandler { if (isFieldDeprecated(field)) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.nil())); if (makeAbstract) access = access | Flags.ABSTRACT; + AnnotationValues accessors = JavacHandlerUtil.getAccessorsForField(field); + boolean makeFinal = shouldMakeFinal(field, accessors); + if (makeFinal) access |= Flags.FINAL; createRelevantNonNullAnnotation(source, param); JCMethodDecl decl = recursiveSetGeneratedBy(maker.MethodDef(maker.Modifiers(access, annsOnMethod), methodName, returnType, methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source); diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index d3532f79..53a518b4 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2009-2022 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 @@ -572,6 +572,14 @@ public class JavacHandlerUtil { return HandlerUtil.toAllGetterNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } + /** + * Translates the given field into all possible getter names. + * Convenient wrapper around {@link HandlerUtil#toAllGetterNames(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static java.util.List toAllGetterNames(JavacNode field, AnnotationValues accessors) { + return HandlerUtil.toAllGetterNames(field.getAst(), accessors, field.getName(), isBoolean(field)); + } + /** * @return the likely getter name for the stated field. (e.g. private boolean foo; to isFoo). * @@ -581,6 +589,15 @@ public class JavacHandlerUtil { return HandlerUtil.toGetterName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } + /** + * @return the likely getter name for the stated field. (e.g. private boolean foo; to isFoo). + * + * Convenient wrapper around {@link HandlerUtil#toGetterName(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static String toGetterName(JavacNode field, AnnotationValues accessors) { + return HandlerUtil.toGetterName(field.getAst(), accessors, field.getName(), isBoolean(field)); + } + /** * Translates the given field into all possible setter names. * Convenient wrapper around {@link HandlerUtil#toAllSetterNames(lombok.core.AnnotationValues, CharSequence, boolean)}. @@ -589,6 +606,14 @@ public class JavacHandlerUtil { return HandlerUtil.toAllSetterNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } + /** + * Translates the given field into all possible setter names. + * Convenient wrapper around {@link HandlerUtil#toAllSetterNames(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static java.util.List toAllSetterNames(JavacNode field, AnnotationValues accessors) { + return HandlerUtil.toAllSetterNames(field.getAst(), accessors, field.getName(), isBoolean(field)); + } + /** * @return the likely setter name for the stated field. (e.g. private boolean foo; to setFoo). * @@ -598,6 +623,15 @@ public class JavacHandlerUtil { return HandlerUtil.toSetterName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } + /** + * @return the likely setter name for the stated field. (e.g. private boolean foo; to setFoo). + * + * Convenient wrapper around {@link HandlerUtil#toSetterName(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static String toSetterName(JavacNode field, AnnotationValues accessors) { + return HandlerUtil.toSetterName(field.getAst(), accessors, field.getName(), isBoolean(field)); + } + /** * Translates the given field into all possible with names. * Convenient wrapper around {@link HandlerUtil#toAllWithNames(lombok.core.AnnotationValues, CharSequence, boolean)}. @@ -606,6 +640,14 @@ public class JavacHandlerUtil { return HandlerUtil.toAllWithNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } + /** + * Translates the given field into all possible with names. + * Convenient wrapper around {@link HandlerUtil#toAllWithNames(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static java.util.List toAllWithNames(JavacNode field, AnnotationValues accessors) { + return HandlerUtil.toAllWithNames(field.getAst(), accessors, field.getName(), isBoolean(field)); + } + /** * Translates the given field into all possible withBy names. * Convenient wrapper around {@link HandlerUtil#toAllWithByNames(lombok.core.AnnotationValues, CharSequence, boolean)}. @@ -614,6 +656,14 @@ public class JavacHandlerUtil { return HandlerUtil.toAllWithByNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } + /** + * Translates the given field into all possible withBy names. + * Convenient wrapper around {@link HandlerUtil#toAllWithByNames(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static java.util.List toAllWithByNames(JavacNode field, AnnotationValues accessors) { + return HandlerUtil.toAllWithByNames(field.getAst(), accessors, field.getName(), isBoolean(field)); + } + /** * @return the likely with name for the stated field. (e.g. private boolean foo; to withFoo). * @@ -623,6 +673,15 @@ public class JavacHandlerUtil { return HandlerUtil.toWithName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } + /** + * @return the likely with name for the stated field. (e.g. private boolean foo; to withFoo). + * + * Convenient wrapper around {@link HandlerUtil#toWithName(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static String toWithName(JavacNode field, AnnotationValues accessors) { + return HandlerUtil.toWithName(field.getAst(), accessors, field.getName(), isBoolean(field)); + } + /** * @return the likely withBy name for the stated field. (e.g. private boolean foo; to withFooBy). * @@ -632,18 +691,34 @@ public class JavacHandlerUtil { return HandlerUtil.toWithByName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } + /** + * @return the likely withBy name for the stated field. (e.g. private boolean foo; to withFooBy). + * + * Convenient wrapper around {@link HandlerUtil#toWithByName(lombok.core.AnnotationValues, CharSequence, boolean)}. + */ + public static String toWithByName(JavacNode field, AnnotationValues accessors) { + return HandlerUtil.toWithByName(field.getAst(), accessors, field.getName(), isBoolean(field)); + } + /** * When generating a setter, the setter either returns void (beanspec) or Self (fluent). * This method scans for the {@code Accessors} annotation to figure that out. */ - public static boolean shouldReturnThis(JavacNode field) { + public static boolean shouldReturnThis(JavacNode field, AnnotationValues accessors) { if ((((JCVariableDecl) field.get()).mods.flags & Flags.STATIC) != 0) return false; - AnnotationValues accessors = JavacHandlerUtil.getAccessorsForField(field); - return HandlerUtil.shouldReturnThis0(accessors, field.getAst()); } + /** + * When generating a setter/getter/wither, should it be made final? + */ + public static boolean shouldMakeFinal(JavacNode field, AnnotationValues accessors) { + if ((((JCVariableDecl) field.get()).mods.flags & Flags.STATIC) != 0) return false; + + return HandlerUtil.shouldMakeFinal0(accessors, field.getAst()); + } + public static JCExpression cloneSelfType(JavacNode childOfType) { JavacNode typeNode = childOfType; JavacTreeMaker maker = childOfType.getTreeMaker(); @@ -696,9 +771,12 @@ public class JavacHandlerUtil { } public static AnnotationValues getAccessorsForField(JavacNode field) { + AnnotationValues values = null; + for (JavacNode node : field.down()) { if (annotationTypeMatches(Accessors.class, node)) { - return createAnnotation(Accessors.class, node); + values = createAnnotation(Accessors.class, node); + break; } } @@ -706,13 +784,15 @@ public class JavacHandlerUtil { while (current != null) { for (JavacNode node : current.down()) { if (annotationTypeMatches(Accessors.class, node)) { - return createAnnotation(Accessors.class, node); + AnnotationValues onType = createAnnotation(Accessors.class, node); + values = values == null ? onType : values.integrate(onType); + break; } } current = current.up(); } - return AnnotationValues.of(Accessors.class, field); + return values == null ? AnnotationValues.of(Accessors.class, field) : values; } /** @@ -2185,7 +2265,7 @@ public class JavacHandlerUtil { Javac.setDocComment(cu, n, javadoc); } }); - return shouldReturnThis(node) ? addReturnsThisIfNeeded(out) : out; + return shouldReturnThis(node, JavacHandlerUtil.getAccessorsForField(node)) ? addReturnsThisIfNeeded(out) : out; } } diff --git a/test/transform/resource/after-delombok/AccessorsCascade.java b/test/transform/resource/after-delombok/AccessorsCascade.java new file mode 100644 index 00000000..ba4d13d4 --- /dev/null +++ b/test/transform/resource/after-delombok/AccessorsCascade.java @@ -0,0 +1,39 @@ +//CONF: lombok.Accessors.prefix += f +class AccessorsOuter { + private String fTest; + private String zTest2; + class AccessorsInner1 { + private String zTest3; + /** + * @return {@code this}. + */ + @java.lang.SuppressWarnings("all") + public AccessorsOuter.AccessorsInner1 setTest3(final String zTest3) { + this.zTest3 = zTest3; + return this; + } + } + class AccessorsInner2 { + private String fTest4; + @java.lang.SuppressWarnings("all") + public void setTest4(final String fTest4) { + this.fTest4 = fTest4; + } + } + /** + * @return {@code this}. + */ + @java.lang.SuppressWarnings("all") + public AccessorsOuter setTest(final String fTest) { + this.fTest = fTest; + return this; + } + /** + * @return {@code this}. + */ + @java.lang.SuppressWarnings("all") + public AccessorsOuter setTest2(final String zTest2) { + this.zTest2 = zTest2; + return this; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-delombok/AccessorsMakeFinal.java b/test/transform/resource/after-delombok/AccessorsMakeFinal.java new file mode 100644 index 00000000..d88e8616 --- /dev/null +++ b/test/transform/resource/after-delombok/AccessorsMakeFinal.java @@ -0,0 +1,11 @@ +class AccessorsMakeFinal1 { + private String test; + /** + * @return {@code this}. + */ + @java.lang.SuppressWarnings("all") + public final AccessorsMakeFinal1 test(final String test) { + this.test = test; + return this; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-delombok/AccessorsMakeFinalLombokConfig.java b/test/transform/resource/after-delombok/AccessorsMakeFinalLombokConfig.java new file mode 100644 index 00000000..61deedee --- /dev/null +++ b/test/transform/resource/after-delombok/AccessorsMakeFinalLombokConfig.java @@ -0,0 +1,7 @@ +class AccessorsMakeFinalLombokConfig { + private String test; + @java.lang.SuppressWarnings("all") + public final void setTest(final String test) { + this.test = test; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-delombok/AccessorsNoParamWarning.java b/test/transform/resource/after-delombok/AccessorsNoParamWarning.java new file mode 100644 index 00000000..3e9c4c5f --- /dev/null +++ b/test/transform/resource/after-delombok/AccessorsNoParamWarning.java @@ -0,0 +1,14 @@ +class AccessorsNoParams { + private String otherFieldWithOverride = ""; + @java.lang.SuppressWarnings("all") + public String otherFieldWithOverride() { + return this.otherFieldWithOverride; + } +} +class AccessorsNoParams2 { + private boolean foo; + @java.lang.SuppressWarnings("all") + public void setFoo(final boolean foo) { + this.foo = foo; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/Accessors.java b/test/transform/resource/after-ecj/Accessors.java index cdc7b146..149d16f9 100644 --- a/test/transform/resource/after-ecj/Accessors.java +++ b/test/transform/resource/after-ecj/Accessors.java @@ -16,7 +16,7 @@ class AccessorsFluent { } @lombok.experimental.Accessors(fluent = true) @lombok.Getter class AccessorsFluentOnClass { private @lombok.Setter String fieldName = ""; - private @lombok.experimental.Accessors String otherFieldWithOverride = ""; + private @lombok.experimental.Accessors(fluent = false) String otherFieldWithOverride = ""; AccessorsFluentOnClass() { super(); } diff --git a/test/transform/resource/after-ecj/AccessorsCascade.java b/test/transform/resource/after-ecj/AccessorsCascade.java new file mode 100644 index 00000000..cacd338f --- /dev/null +++ b/test/transform/resource/after-ecj/AccessorsCascade.java @@ -0,0 +1,43 @@ +@lombok.experimental.Accessors(chain = true) class AccessorsOuter { + class AccessorsInner1 { + private @lombok.experimental.Accessors(prefix = "z") @lombok.Setter String zTest3; + AccessorsInner1() { + super(); + } + /** + * @return {@code this}. + */ + public @java.lang.SuppressWarnings("all") AccessorsOuter.AccessorsInner1 setTest3(final String zTest3) { + this.zTest3 = zTest3; + return this; + } + } + @lombok.experimental.Accessors(chain = false) class AccessorsInner2 { + private @lombok.Setter String fTest4; + AccessorsInner2() { + super(); + } + public @java.lang.SuppressWarnings("all") void setTest4(final String fTest4) { + this.fTest4 = fTest4; + } + } + private @lombok.Setter String fTest; + private @lombok.experimental.Accessors(prefix = "z") @lombok.Setter String zTest2; + AccessorsOuter() { + super(); + } + /** + * @return {@code this}. + */ + public @java.lang.SuppressWarnings("all") AccessorsOuter setTest(final String fTest) { + this.fTest = fTest; + return this; + } + /** + * @return {@code this}. + */ + public @java.lang.SuppressWarnings("all") AccessorsOuter setTest2(final String zTest2) { + this.zTest2 = zTest2; + return this; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/AccessorsMakeFinal.java b/test/transform/resource/after-ecj/AccessorsMakeFinal.java new file mode 100644 index 00000000..c8ac4bbd --- /dev/null +++ b/test/transform/resource/after-ecj/AccessorsMakeFinal.java @@ -0,0 +1,13 @@ +@lombok.experimental.Accessors(makeFinal = true) class AccessorsMakeFinal1 { + private @lombok.Setter @lombok.experimental.Accessors(fluent = true) String test; + AccessorsMakeFinal1() { + super(); + } + /** + * @return {@code this}. + */ + public final @java.lang.SuppressWarnings("all") AccessorsMakeFinal1 test(final String test) { + this.test = test; + return this; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/AccessorsMakeFinalLombokConfig.java b/test/transform/resource/after-ecj/AccessorsMakeFinalLombokConfig.java new file mode 100644 index 00000000..f7c411e3 --- /dev/null +++ b/test/transform/resource/after-ecj/AccessorsMakeFinalLombokConfig.java @@ -0,0 +1,9 @@ +class AccessorsMakeFinalLombokConfig { + private @lombok.Setter String test; + AccessorsMakeFinalLombokConfig() { + super(); + } + public final @java.lang.SuppressWarnings("all") void setTest(final String test) { + this.test = test; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/AccessorsNoParamWarning.java b/test/transform/resource/after-ecj/AccessorsNoParamWarning.java new file mode 100644 index 00000000..e5d2d905 --- /dev/null +++ b/test/transform/resource/after-ecj/AccessorsNoParamWarning.java @@ -0,0 +1,18 @@ +@lombok.experimental.Accessors(fluent = true) class AccessorsNoParams { + private @lombok.Getter @lombok.experimental.Accessors String otherFieldWithOverride = ""; + AccessorsNoParams() { + super(); + } + public @java.lang.SuppressWarnings("all") String otherFieldWithOverride() { + return this.otherFieldWithOverride; + } +} +@lombok.experimental.Accessors class AccessorsNoParams2 { + private @lombok.Setter boolean foo; + AccessorsNoParams2() { + super(); + } + public @java.lang.SuppressWarnings("all") void setFoo(final boolean foo) { + this.foo = foo; + } +} \ No newline at end of file diff --git a/test/transform/resource/before/Accessors.java b/test/transform/resource/before/Accessors.java index 3ef8a02f..54430cd6 100644 --- a/test/transform/resource/before/Accessors.java +++ b/test/transform/resource/before/Accessors.java @@ -7,7 +7,7 @@ class AccessorsFluent { @lombok.Getter class AccessorsFluentOnClass { @lombok.Setter private String fieldName = ""; - @lombok.experimental.Accessors private String otherFieldWithOverride = ""; + @lombok.experimental.Accessors(fluent=false) private String otherFieldWithOverride = ""; } class AccessorsChain { diff --git a/test/transform/resource/before/AccessorsCascade.java b/test/transform/resource/before/AccessorsCascade.java new file mode 100644 index 00000000..8ad141f8 --- /dev/null +++ b/test/transform/resource/before/AccessorsCascade.java @@ -0,0 +1,23 @@ +//CONF: lombok.Accessors.prefix += f + +@lombok.experimental.Accessors(chain=true) +class AccessorsOuter { + @lombok.Setter + private String fTest; + + @lombok.experimental.Accessors(prefix="z") + @lombok.Setter + private String zTest2; + + class AccessorsInner1 { + @lombok.experimental.Accessors(prefix="z") + @lombok.Setter + private String zTest3; + } + + @lombok.experimental.Accessors(chain=false) + class AccessorsInner2 { + @lombok.Setter + private String fTest4; + } +} diff --git a/test/transform/resource/before/AccessorsMakeFinal.java b/test/transform/resource/before/AccessorsMakeFinal.java new file mode 100644 index 00000000..5c45873a --- /dev/null +++ b/test/transform/resource/before/AccessorsMakeFinal.java @@ -0,0 +1,5 @@ +@lombok.experimental.Accessors(makeFinal = true) +class AccessorsMakeFinal1 { + @lombok.Setter @lombok.experimental.Accessors(fluent = true) + private String test; +} diff --git a/test/transform/resource/before/AccessorsMakeFinalLombokConfig.java b/test/transform/resource/before/AccessorsMakeFinalLombokConfig.java new file mode 100644 index 00000000..8e948520 --- /dev/null +++ b/tes