From 867c3caf61fcc76b7cb8a7356e71858889f85ebf Mon Sep 17 00:00:00 2001 From: Roel Spilker Date: Wed, 24 Jul 2019 21:18:36 +0200 Subject: Fixes #2184: NPE on package-info.java without package declaration. --- doc/changelog.markdown | 1 + 1 file changed, 1 insertion(+) (limited to 'doc/changelog.markdown') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 0e489dd3..07b1b0aa 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -11,6 +11,7 @@ Lombok Changelog * BUGFIX: Delombok would turn something like `List...` in a method parameter to `List...` [Issue #2140](https://github.com/rzwitserloot/lombok/issues/2140) * BUGFIX: Javac would generate the wrong equals and hashCode if a type-use annotation was put on an array type field [Issue #2165](https://github.com/rzwitserloot/lombok/issues/2165) * BUGFIX: Eclipse 2019-06 + JDK-12 compatibility + an `@Singular` builder entry would produce a cascade of error dialogs. [Issue #2169](https://github.com/rzwitserloot/lombok/issues/2169) +* BUGFIX: Javac would throw a NullPointerException if the package-info.java did not contain a package declaration. [Issue #2184](https://github.com/rzwitserloot/lombok/issues/2184) * IMPROBABLE BREAKING CHANGE: Stricter validation of configuration keys dealing with identifiers and types (`lombok.log.fieldName`, `lombok.fieldNameConstants.innerTypeName`, `lombok.copyableAnnotations`). * IMPROBABLE BREAKING CHANGE: The fields generated inside builders for fields with defaults (with `@Builder` on a class with fields marked `@Default`) now have `$value` as the name; direct manipulation of these fields is not advised because there is an associated `$set` variable that also needs to be taken into account. [Issue #2115](https://github.com/rzwitserloot/lombok/issues/2115) -- cgit From 819d1956761f6077fdf897e77c4f6ac47e92104e Mon Sep 17 00:00:00 2001 From: Jacob Middag Date: Thu, 8 Aug 2019 21:14:58 +0200 Subject: [Fixes #2189] Clone type to correctly set annotated type on constructor, getter and setter. --- AUTHORS | 1 + doc/changelog.markdown | 1 + src/core/lombok/javac/handlers/HandleConstructor.java | 3 ++- src/core/lombok/javac/handlers/HandleGetter.java | 2 +- src/core/lombok/javac/handlers/HandleSetter.java | 3 ++- 5 files changed, 7 insertions(+), 3 deletions(-) (limited to 'doc/changelog.markdown') diff --git a/AUTHORS b/AUTHORS index 0bee77cd..62d677c1 100755 --- a/AUTHORS +++ b/AUTHORS @@ -9,6 +9,7 @@ Dave Brosius Dawid Rusin Emil Lundberg Enrique da Costa Cambio +Jacob Middag Jan Rieke Jappe van der Hel Kevin Chirls diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 07b1b0aa..13fc18d6 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -12,6 +12,7 @@ Lombok Changelog * BUGFIX: Javac would generate the wrong equals and hashCode if a type-use annotation was put on an array type field [Issue #2165](https://github.com/rzwitserloot/lombok/issues/2165) * BUGFIX: Eclipse 2019-06 + JDK-12 compatibility + an `@Singular` builder entry would produce a cascade of error dialogs. [Issue #2169](https://github.com/rzwitserloot/lombok/issues/2169) * BUGFIX: Javac would throw a NullPointerException if the package-info.java did not contain a package declaration. [Issue #2184](https://github.com/rzwitserloot/lombok/issues/2184) +* BUGFIX: Javac sets incorrect annotated type on constructor, getter and setter. [Issue #2189](https://github.com/rzwitserloot/lombok/issues/2189) * IMPROBABLE BREAKING CHANGE: Stricter validation of configuration keys dealing with identifiers and types (`lombok.log.fieldName`, `lombok.fieldNameConstants.innerTypeName`, `lombok.copyableAnnotations`). * IMPROBABLE BREAKING CHANGE: The fields generated inside builders for fields with defaults (with `@Builder` on a class with fields marked `@Default`) now have `$value` as the name; direct manipulation of these fields is not advised because there is an associated `$set` variable that also needs to be taken into account. [Issue #2115](https://github.com/rzwitserloot/lombok/issues/2115) diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index 096963f4..34d2cbfe 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -339,7 +339,8 @@ public class HandleConstructor { Name rawName = field.name; List copyableAnnotations = findCopyableAnnotations(fieldNode); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); - JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, copyableAnnotations), fieldName, field.vartype, null); + JCExpression pType = cloneType(fieldNode.getTreeMaker(), field.vartype, source.get(), source.getContext()); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, copyableAnnotations), fieldName, pType, null); params.append(param); if (hasNonNullAnnotations(fieldNode)) { JCStatement nullCheck = generateNullCheck(maker, param, source); diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 7a178f66..f17936eb 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -223,7 +223,7 @@ public class HandleGetter extends JavacAnnotationHandler { JCVariableDecl fieldNode = (JCVariableDecl) field.get(); // Remember the type; lazy will change it - JCExpression methodType = copyType(treeMaker, fieldNode); + JCExpression methodType = cloneType(treeMaker, copyType(treeMaker, fieldNode), source, field.getContext()); // Generate the methodName; lazy will change the field type Name methodName = field.toName(toGetterName(field)); diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index cd8b5d1c..560069ff 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -233,7 +233,8 @@ public class HandleSetter extends JavacAnnotationHandler { List annsOnParam = copyAnnotations(onParam).appendList(copyableAnnotations); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, field.getContext()); - JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(flags, annsOnParam), paramName, fieldDecl.vartype, null); + JCExpression pType = cloneType(treeMaker, fieldDecl.vartype, source.get(), source.getContext()); + JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(flags, annsOnParam), paramName, pType, null); if (!hasNonNullAnnotations(field) && !hasNonNullAnnotations(field, onParam)) { statements.append(treeMaker.Exec(assign)); -- cgit From 15df143df6d35dd64459d717a451a039eb26d761 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Tue, 13 Aug 2019 15:04:43 +0200 Subject: [checkerframework] A bit of a shadow feature because the checker framework folks need to do some work on their side. this update makes lombok generate a few checker framework annotations (if configured to do so) which let the checker framework add warnings and errors for example if you misuse builders, or ignore the return values of withers, etc. --- doc/changelog.markdown | 1 + src/core/lombok/ConfigurationKeys.java | 10 + .../configuration/CheckerFrameworkVersion.java | 86 +++++++++ .../eclipse/handlers/EclipseHandlerUtil.java | 33 +++- .../eclipse/handlers/EclipseSingularsRecipes.java | 17 +- .../lombok/eclipse/handlers/HandleBuilder.java | 93 ++++++++-- .../lombok/eclipse/handlers/HandleConstructor.java | 17 +- .../eclipse/handlers/HandleEqualsAndHashCode.java | 18 +- src/core/lombok/eclipse/handlers/HandleGetter.java | 13 +- src/core/lombok/eclipse/handlers/HandleSetter.java | 7 +- .../eclipse/handlers/HandleSuperBuilder.java | 106 ++++++++--- .../lombok/eclipse/handlers/HandleToString.java | 10 +- src/core/lombok/eclipse/handlers/HandleWither.java | 13 +- .../singulars/EclipseGuavaSingularizer.java | 21 ++- .../EclipseJavaUtilListSetSingularizer.java | 23 +-- .../singulars/EclipseJavaUtilMapSingularizer.java | 23 +-- src/core/lombok/javac/handlers/HandleBuilder.java | 69 +++++-- .../lombok/javac/handlers/HandleConstructor.java | 9 +- .../javac/handlers/HandleEqualsAndHashCode.java | 22 ++- src/core/lombok/javac/handlers/HandleGetter.java | 9 +- src/core/lombok/javac/handlers/HandleSetter.java | 11 +- .../lombok/javac/handlers/HandleSuperBuilder.java | 95 ++++++---- src/core/lombok/javac/handlers/HandleToString.java | 9 +- src/core/lombok/javac/handlers/HandleWither.java | 10 +- .../lombok/javac/handlers/JavacHandlerUtil.java | 8 +- .../javac/handlers/JavacSingularsRecipes.java | 40 ++-- .../handlers/singulars/JavacGuavaSingularizer.java | 5 +- .../JavacJavaUtilListSetSingularizer.java | 5 +- .../singulars/JavacJavaUtilMapSingularizer.java | 5 +- test/core/src/lombok/AbstractRunTests.java | 2 + test/core/src/lombok/CompilerMessageMatcher.java | 2 +- test/core/src/lombok/DirectoryRunner.java | 5 +- test/core/src/lombok/LombokTestSource.java | 15 +- .../after-delombok/CheckerFrameworkBasic.java | 72 ++++++++ .../after-delombok/CheckerFrameworkBuilder.java | 106 +++++++++++ .../CheckerFrameworkSuperBuilder.java | 202 +++++++++++++++++++++ .../resource/after-ecj/CheckerFrameworkBasic.java | 59 ++++++ .../after-ecj/CheckerFrameworkBuilder.java | 79 ++++++++ .../after-ecj/CheckerFrameworkSuperBuilder.java | 142 +++++++++++++++ .../resource/before/CheckerFrameworkBasic.java | 11 ++ .../resource/before/CheckerFrameworkBuilder.java | 12 ++ .../before/CheckerFrameworkSuperBuilder.java | 19 ++ .../CheckerFrameworkBasic.java.messages | 4 + .../CheckerFrameworkBuilder.java.messages | 1 + .../CheckerFrameworkSuperBuilder.java.messages | 3 + .../CheckerFrameworkBasic.java.messages | 1 + .../CheckerFrameworkBuilder.java.messages | 1 + .../CheckerFrameworkSuperBuilder.java.messages | 1 + .../CheckerFrameworkBasic.java.messages | 10 + 49 files changed, 1330 insertions(+), 205 deletions(-) create mode 100644 src/core/lombok/core/configuration/CheckerFrameworkVersion.java create mode 100644 test/transform/resource/after-delombok/CheckerFrameworkBasic.java create mode 100644 test/transform/resource/after-delombok/CheckerFrameworkBuilder.java create mode 100644 test/transform/resource/after-delombok/CheckerFrameworkSuperBuilder.java create mode 100644 test/transform/resource/after-ecj/CheckerFrameworkBasic.java create mode 100644 test/transform/resource/after-ecj/CheckerFrameworkBuilder.java create mode 100644 test/transform/resource/after-ecj/CheckerFrameworkSuperBuilder.java create mode 100644 test/transform/resource/before/CheckerFrameworkBasic.java create mode 100644 test/transform/resource/before/CheckerFrameworkBuilder.java create mode 100644 test/transform/resource/before/CheckerFrameworkSuperBuilder.java create mode 100644 test/transform/resource/messages-delombok/CheckerFrameworkBasic.java.messages create mode 100644 test/transform/resource/messages-delombok/CheckerFrameworkBuilder.java.messages create mode 100644 test/transform/resource/messages-delombok/CheckerFrameworkSuperBuilder.java.messages create mode 100644 test/transform/resource/messages-ecj/CheckerFrameworkBasic.java.messages create mode 100644 test/transform/resource/messages-ecj/CheckerFrameworkBuilder.java.messages create mode 100644 test/transform/resource/messages-ecj/CheckerFrameworkSuperBuilder.java.messages create mode 100644 test/transform/resource/messages-idempotent/CheckerFrameworkBasic.java.messages (limited to 'doc/changelog.markdown') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 07b1b0aa..d6464f15 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -8,6 +8,7 @@ Lombok Changelog * ENHANCEMENT: You can now configure the generated builder class name via the config system, using key `lombok.builder.className`. See the [Builder documentation](https://projectlombok.org/features/Builder) and [SuperBuilder documentation](https://projectlombok.org/features/experimental/SuperBuilder) * ENHANCEMENT: If you mix up eclipse's non-null support, such as `@NonNullByDefault`, with lombok's `@NonNull`, you get a bunch of warnings about dead code that are inappropriate. These warnings are now suppressed, thanks to a contribution from Till Brychcy! [Pull Request #2155](https://github.com/rzwitserloot/lombok/pull/2155) * ENHANCEMENT: `@NonNull` can now also generate checks using jdk's `Objects.requireNonNull` or Guava's `Preconditions.checkNotNull`. [Issue #1197](https://github.com/rzwitserloot/lombok/issues/1197) +* EXPERIMENT: Lombok is working together with [checkerframework](https://checkerframework.org/) to enable detection of improper builder use (such as forgetting to set a mandatory property prior to calling `build()`). This experiment can be turned on by adding `checkerframework = true` to your `lombok.config` file. * BUGFIX: Delombok would turn something like `List...` in a method parameter to `List...` [Issue #2140](https://github.com/rzwitserloot/lombok/issues/2140) * BUGFIX: Javac would generate the wrong equals and hashCode if a type-use annotation was put on an array type field [Issue #2165](https://github.com/rzwitserloot/lombok/issues/2165) * BUGFIX: Eclipse 2019-06 + JDK-12 compatibility + an `@Singular` builder entry would produce a cascade of error dialogs. [Issue #2169](https://github.com/rzwitserloot/lombok/issues/2169) diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index dda0b54b..d46889d6 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -24,6 +24,7 @@ package lombok; import java.util.List; import lombok.core.configuration.CallSuperType; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.configuration.ConfigurationKey; import lombok.core.configuration.LogDeclaration; import lombok.core.configuration.FlagUsageType; @@ -627,5 +628,14 @@ public class ConfigurationKeys { * Copy these annotations to getters, setters, withers, builder-setters, etc. */ public static final ConfigurationKey> COPYABLE_ANNOTATIONS = new ConfigurationKey>("lombok.copyableAnnotations", "Copy these annotations to getters, setters, withers, builder-setters, etc.") {}; + + /** + * lombok configuration: {@code checkerframework} = {@code true} | {@code false} | <String: MajorVer.MinorVer> (Default: false). + * + * If set, lombok will generate appropriate annotations from checkerframework.org on generated code. If set to {@code true}, all relevant annotations from the most recent version of + * checkerframework.org that lombok supports will be generated. If set to a specific major/minor version number, only checkerframework annotations introduced on or before the stated + * checkerframework.org version will be generated. + */ + public static final ConfigurationKey CHECKER_FRAMEWORK = new ConfigurationKey("checkerframework", "If set with the version of checkerframework.org (in major.minor, or just 'true' for the latest supported version), create relevant checkerframework.org annotations for code lombok generates (default: false).") {}; } diff --git a/src/core/lombok/core/configuration/CheckerFrameworkVersion.java b/src/core/lombok/core/configuration/CheckerFrameworkVersion.java new file mode 100644 index 00000000..68fc05a7 --- /dev/null +++ b/src/core/lombok/core/configuration/CheckerFrameworkVersion.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 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.core.configuration; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class CheckerFrameworkVersion implements ConfigurationValueType { + private final int version; + private static final int MAX_SUPPORTED = 3000; + + public static final String NAME__SIDE_EFFECT_FREE = "org.checkerframework.dataflow.qual.SideEffectFree"; + public static final String NAME__UNIQUE = "org.checkerframework.common.aliasing.qual.Unique"; + public static final String NAME__RETURNS_RECEIVER = "org.checkerframework.checker.builder.qual.ReturnsReceiver"; + public static final String NAME__NOT_CALLED = "org.checkerframework.checker.builder.qual.NotCalledMethods"; + public static final String NAME__CALLED = "org.checkerframework.checker.builder.qual.CalledMethods"; + + public static final CheckerFrameworkVersion NONE = new CheckerFrameworkVersion(0); + + private CheckerFrameworkVersion(int v) { + this.version = v; + } + + private static final Pattern VERSION = Pattern.compile("^(\\d+)(?:\\.(\\d+))?(?:\\.\\d+)*$"); + + public boolean generateSideEffectFree() { + return version > 0; + } + + public boolean generateUnique() { + return version > 0; + } + + public boolean generateReturnsReceiver() { + return version > 2999; + } + + public boolean generateCalledMethods() { + return version > 2999; + } + + public static CheckerFrameworkVersion valueOf(String versionString) { + if (versionString != null) versionString = versionString.trim(); + if (versionString == null || versionString.equalsIgnoreCase("false") || versionString.equals("0")) return new CheckerFrameworkVersion(0); + if (versionString.equalsIgnoreCase("true")) return new CheckerFrameworkVersion(MAX_SUPPORTED); + Matcher m = VERSION.matcher(versionString); + if (!m.matches()) throw new IllegalArgumentException("Expected 'true' or 'false' or a major/minor version, such as '2.9'"); + int major = Integer.parseInt(m.group(1)); + int minor = (m.group(2) != null && !m.group(2).isEmpty()) ? Integer.parseInt(m.group(2)) : 0; + if (minor > 999) throw new IllegalArgumentException("Minor version must be between 0 and 999"); + int v = major * 1000 + minor; + if (v > MAX_SUPPORTED) { + String s = (v / 1000) + "." + (v % 1000); + throw new IllegalArgumentException("Lombok supports at most v" + s + "; reduce the value of key 'checkerframework' to " + s); + } + return new CheckerFrameworkVersion(v); + } + + public static String description() { + return "checkerframework-version"; + } + + public static String exampleValue() { + String s = (MAX_SUPPORTED / 1000) + "." + (MAX_SUPPORTED % 1000); + return "major.minor (example: 2.9 - and no higher than " + s + ") or true or false"; + } +} diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 11a2b9bd..782c6a29 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -111,6 +111,7 @@ import lombok.core.AnnotationValues; import lombok.core.AnnotationValues.AnnotationValue; import lombok.core.LombokImmutableList; import lombok.core.TypeResolver; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.configuration.NullCheckExceptionType; import lombok.core.configuration.TypeName; import lombok.core.debug.ProblemReporter; @@ -184,6 +185,23 @@ public class EclipseHandlerUtil { return ma; } + public static MarkerAnnotation generateNamedAnnotation(ASTNode source, String typeName) { + char[][] cc = fromQualifiedName(typeName); + QualifiedTypeReference qtr = new QualifiedTypeReference(cc, poss(source, cc.length)); + setGeneratedBy(qtr, source); + MarkerAnnotation ma = new MarkerAnnotation(qtr, source.sourceStart); + // No matter what value you input for sourceEnd, the AST->DOM converter of eclipse will reparse to find the end, and will fail as + // it can't find code that isn't really there. This results in the end position being set to 2 or 0 or some weird magic value, and thus, + // length, as calculated by end-start, is all screwed up, resulting in IllegalArgumentException during a setSourceRange call MUCH later in the process. + // We solve it by going with a voodoo magic source start value such that the calculated length so happens to exactly be 0. 0 lengths are accepted + // by eclipse. For some reason. + // TL;DR: Don't change 1. 1 is sacred. Trust the 1. + // issue: #408. + ma.sourceStart = 1; + setGeneratedBy(ma, source); + return ma; + } + public static boolean isFieldDeprecated(EclipseNode fieldNode) { if (!(fieldNode.get() instanceof FieldDeclaration)) return false; FieldDeclaration field = (FieldDeclaration) fieldNode.get(); @@ -199,6 +217,11 @@ public class EclipseHandlerUtil { return false; } + public static CheckerFrameworkVersion getCheckerFrameworkVersion(EclipseNode node) { + CheckerFrameworkVersion cfv = node.getAst().readConfiguration(ConfigurationKeys.CHECKER_FRAMEWORK); + return cfv != null ? cfv : CheckerFrameworkVersion.NONE; + } + /** * Checks if the given TypeReference node is likely to be a reference to the provided class. * @@ -1908,11 +1931,13 @@ public class EclipseHandlerUtil { * Create an annotation of the given name, and is marked as being generated by the given source. */ public static MarkerAnnotation makeMarkerAnnotation(char[][] name, ASTNode source) { - long pos = (long)source.sourceStart << 32 | source.sourceEnd; - TypeReference typeRef = new QualifiedTypeReference(name, new long[] {pos, pos, pos}); + long pos = (long) source.sourceStart << 32 | source.sourceEnd; + long[] poss = new long[name.length]; + Arrays.fill(poss, pos); + TypeReference typeRef = new QualifiedTypeReference(name, poss); setGeneratedBy(typeRef, source); - MarkerAnnotation ann = new MarkerAnnotation(typeRef, (int)(pos >> 32)); - ann.declarationSourceEnd = ann.sourceEnd = ann.statementEnd = (int)pos; + MarkerAnnotation ann = new MarkerAnnotation(typeRef, (int) (pos >> 32)); + ann.declarationSourceEnd = ann.sourceEnd = ann.statementEnd = (int) pos; setGeneratedBy(ann, source); return ann; } diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index a8a780d6..da0bf471 100755 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java @@ -33,6 +33,7 @@ import java.util.Map; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; @@ -62,6 +63,7 @@ import lombok.AccessLevel; import lombok.core.LombokImmutableList; import lombok.core.SpiLoadUtil; import lombok.core.TypeLibrary; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.eclipse.EclipseNode; public class EclipseSingularsRecipes { @@ -235,7 +237,7 @@ public class EclipseSingularsRecipes { * If you need more control over the return type and value, use * {@link #generateMethods(SingularData, boolean, EclipseNode, boolean, TypeReferenceMaker, StatementMaker)}. */ - public void generateMethods(SingularData data, boolean deprecate, final EclipseNode builderType, boolean fluent, final boolean chain, AccessLevel access) { + public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, final EclipseNode builderType, boolean fluent, final boolean chain, AccessLevel access) { TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() { @Override public TypeReference make() { return chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); @@ -248,14 +250,14 @@ public class EclipseSingularsRecipes { } }; - generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker, access); + generateMethods(cfv, data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker, access); } /** * Generates the singular, plural, and clear methods for the given {@link SingularData}. * Uses the given {@code returnTypeMaker} and {@code returnStatementMaker} for the generated methods. */ - public abstract void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access); + public abstract void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access); public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName, String builderVariable); @@ -272,6 +274,15 @@ public class EclipseSingularsRecipes { // -- Utility methods -- + protected Annotation[] generateSelfReturnAnnotations(boolean deprecate, CheckerFrameworkVersion cfv, ASTNode source) { + Annotation deprecated = deprecate ? generateDeprecatedAnnotation(source) : null; + Annotation returnsReceiver = cfv.generateReturnsReceiver() ? generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER) : null; + if (deprecated == null && returnsReceiver == null) return null; + if (deprecated == null) return new Annotation[] {returnsReceiver}; + if (returnsReceiver == null) return new Annotation[] {deprecated}; + return new Annotation[] {deprecated, returnsReceiver}; + } + /** * Adds the requested number of type arguments to the provided type, copying each argument in {@code typeArgs}. If typeArgs is too long, the extra elements are ignored. * If {@code typeArgs} is null or too short, {@code java.lang.Object} will be substituted for each missing type argument. diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index d2b1b823..8bfdeb65 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -37,6 +37,7 @@ import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; @@ -57,9 +58,11 @@ import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; +import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TrueLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; @@ -84,6 +87,7 @@ import lombok.core.handlers.HandlerUtil; import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; @@ -155,7 +159,8 @@ public class HandleBuilder extends EclipseAnnotationHandler { @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); - + CheckerFrameworkVersion cfv = getCheckerFrameworkVersion(annotationNode); + long p = (long) ast.sourceStart << 32 | ast.sourceEnd; Builder builderInstance = annotation.getInstance(); @@ -487,14 +492,14 @@ public class HandleBuilder extends EclipseAnnotationHandler { } for (BuilderFieldData bfd : builderFields) { - makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain, accessForInners, bfd.originalFieldNode); + makeSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, fluent, chain, accessForInners, bfd.originalFieldNode); } { MemberExistsResult methodExists = methodExists(buildMethodName, builderType, -1); if (methodExists == MemberExistsResult.EXISTS_BY_LOMBOK) methodExists = methodExists(buildMethodName, builderType, 0); if (methodExists == MemberExistsResult.NOT_EXISTS) { - MethodDeclaration md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast, accessForInners); + MethodDeclaration md = generateBuildMethod(cfv, tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast, accessForInners); if (md != null) injectMethod(builderType, md); } } @@ -517,7 +522,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; if (generateBuilderMethod) { - MethodDeclaration md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast, accessForOuters); + MethodDeclaration md = generateBuilderMethod(cfv, isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast, accessForOuters); if (md != null) injectMethod(tdParent, md); } @@ -534,7 +539,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { tps[i].name = typeArgsForToBuilder.get(i); } } - MethodDeclaration md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast, accessForOuters); + MethodDeclaration md = generateToBuilderMethod(cfv, toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast, accessForOuters); if (md != null) injectMethod(tdParent, md); } @@ -547,7 +552,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { } private static final char[] BUILDER_TEMP_VAR = {'b', 'u', 'i', 'l', 'd', 'e', 'r'}; - private MethodDeclaration generateToBuilderMethod(String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List builderFields, boolean fluent, ASTNode source, AccessLevel access) { + private MethodDeclaration generateToBuilderMethod(CheckerFrameworkVersion cfv, String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List builderFields, boolean fluent, ASTNode source, AccessLevel access) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; @@ -623,6 +628,10 @@ public class HandleBuilder extends EclipseAnnotationHandler { out.statements = new Statement[] {new ReturnStatement(receiver, pS, pE)}; } + if (cfv.generateUnique()) { + out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)}; + } + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); return out; @@ -650,7 +659,35 @@ public class HandleBuilder extends EclipseAnnotationHandler { return decl; } - public MethodDeclaration generateBuildMethod(EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source, AccessLevel access) { + static Argument[] generateBuildArgs(CheckerFrameworkVersion cfv, EclipseNode type, List builderFields, ASTNode source) { + if (!cfv.generateCalledMethods()) return null; + + List mandatories = new ArrayList(); + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData == null && bfd.nameOfSetFlag == null) mandatories.add(bfd.name); + } + + if (mandatories.size() == 0) return null; + char[][] nameCalled = fromQualifiedName(CheckerFrameworkVersion.NAME__CALLED); + SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameCalled, poss(source, nameCalled.length)), source.sourceStart); + if (mandatories.size() == 1) { + ann.memberValue = new StringLiteral(mandatories.get(0), 0, 0, 0); + } else { + ArrayInitializer arr = new ArrayInitializer(); + arr.sourceStart = source.sourceStart; + arr.sourceEnd = source.sourceEnd; + arr.expressions = new Expression[mandatories.size()]; + for (int i = 0; i < arr.expressions.length; i++) { + arr.expressions[i] = new StringLiteral(mandatories.get(i), source.sourceStart, source.sourceEnd, 0); + } + ann.memberValue = arr; + } + Argument arg = new Argument(new char[] { 't', 'h', 'i', 's' }, 0, new SingleTypeReference(type.getName().toCharArray(), source.sourceStart), Modifier.FINAL); + arg.annotations = new Annotation[] {ann}; + return new Argument[] {arg}; + } + + public MethodDeclaration generateBuildMethod(CheckerFrameworkVersion cfv, EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source, AccessLevel access) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List statements = new ArrayList(); @@ -723,6 +760,10 @@ public class HandleBuilder extends EclipseAnnotationHandler { } } out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[0]); + if (cfv.generateSideEffectFree()) { + out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE)}; + } + out.arguments = generateBuildArgs(cfv, type, builderFields, source); out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return out; } @@ -754,7 +795,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { return out; } - public MethodDeclaration generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source, AccessLevel access) { + public MethodDeclaration generateBuilderMethod(CheckerFrameworkVersion cfv, boolean isStatic, String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source, AccessLevel access) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; @@ -768,7 +809,15 @@ public class HandleBuilder extends EclipseAnnotationHandler { AllocationExpression invoke = new AllocationExpression(); invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)}; - + Annotation uniqueAnn = cfv.generateUnique() ? generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE) : null; + Annotation sefAnn = cfv.generateSideEffectFree() ? generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) : null; + if (uniqueAnn != null && sefAnn != null) { + out.annotations = new Annotation[] {uniqueAnn, sefAnn}; + } else if (uniqueAnn != null) { + out.annotations = new Annotation[] {uniqueAnn}; + } else if (sefAnn != null) { + out.annotations = new Annotation[] {sefAnn}; + } out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); return out; } @@ -813,18 +862,19 @@ public class HandleBuilder extends EclipseAnnotationHandler { private static final AbstractMethodDeclaration[] EMPTY = {}; - public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain, AccessLevel access, EclipseNode originalFieldNode) { + public void makeSetterMethodsForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain, AccessLevel access, EclipseNode originalFieldNode) { boolean deprecate = isFieldDeprecated(bfd.originalFieldNode); if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, sourceNode, fluent, chain, bfd.annotations, access, originalFieldNode); + makeSimpleSetterMethodForBuilder(cfv, builderType, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, sourceNode, fluent, chain, bfd.annotations, access, originalFieldNode); } else { - bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, fluent, chain, access); + bfd.singularData.getSingularizer().generateMethods(cfv, bfd.singularData, deprecate, builderType, fluent, chain, access); } } - private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain, Annotation[] annotations, AccessLevel access, EclipseNode originalFieldNode) { + private void makeSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain, Annotation[] annotations, AccessLevel access, EclipseNode originalFieldNode) { TypeDeclaration td = (TypeDeclaration) builderType.get(); AbstractMethodDeclaration[] existing = td.methods; + ASTNode source = sourceNode.get(); if (existing == null) existing = EMPTY; int len = existing.length; FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); @@ -838,11 +888,20 @@ public class HandleBuilder extends EclipseAnnotationHandler { String setterName = fluent ? new String(paramName) : HandlerUtil.buildAccessorName("set", new String(paramName)); - List methodAnnsList = Collections.emptyList(); - Annotation[] methodAnns = EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); - if (methodAnns != null && methodAnns.length > 0) methodAnnsList = Arrays.asList(methodAnns); + List methodAnnsList = Arrays.asList(EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode)); MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, paramName, nameOfSetFlag, chain, toEclipseModifier(access), - sourceNode, methodAnnsList, annotations != null ? Arrays.asList(copyAnnotations(sourceNode.get(), annotations)) : Collections.emptyList()); + sourceNode, methodAnnsList, annotations != null ? Arrays.asList(copyAnnotations(source, annotations)) : Collections.emptyList()); + if (cfv.generateCalledMethods()) { + Argument[] arr = setter.arguments == null ? new Argument[0] : setter.arguments; + Argument[] newArr = new Argument[arr.length + 1]; + System.arraycopy(arr, 0, newArr, 1, arr.length); + newArr[0] = new Argument(new char[] { 't', 'h', 'i', 's' }, 0, new SingleTypeReference(builderType.getName().toCharArray(), 0), Modifier.FINAL); + char[][] nameNotCalled = fromQualifiedName(CheckerFrameworkVersion.NAME__NOT_CALLED); + SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameNotCalled, poss(source, nameNotCalled.length)), source.sourceStart); + ann.memberValue = new StringLiteral(setterName.toCharArray(), 0, 0, 0); + newArr[0].annotations = new Annotation[] {ann}; + setter.arguments = newArr; + } injectMethod(builderType, setter); } diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index c6b51042..8f981c1a 100755 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2018 The Project Lombok Authors. + * Copyright (C) 2010-2019 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,6 +39,7 @@ import lombok.ConfigurationKeys; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.core.AST.Kind; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; @@ -456,14 +457,14 @@ public class HandleConstructor { constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[0]); /* Generate annotations that must be put on the generated method, and attach them. */ { - Annotation[] constructorProperties = null; - if (addConstructorProperties && !isLocalType(type)) { - constructorProperties = createConstructorProperties(source, fieldsToParam); - } + Annotation[] constructorProperties = null, checkerFramework = null; + if (addConstructorProperties && !isLocalType(type)) constructorProperties = createConstructorProperties(source, fieldsToParam); + if (getCheckerFrameworkVersion(type).generateUnique()) checkerFramework = new Annotation[] { generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE) }; constructor.annotations = copyAnnotations(source, onConstructor.toArray(new Annotation[0]), - constructorProperties); + constructorProperties, + checkerFramework); } constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); @@ -551,7 +552,9 @@ public class HandleConstructor { assigns.add(nameRef); Argument parameter = new Argument(field.name, fieldPos, copyType(field.type, source), Modifier.FINAL); - parameter.annotations = copyAnnotations(source, findCopyableAnnotations(fieldNode)); + Annotation[] checkerFramework = null; + if (getCheckerFrameworkVersion(fieldNode).generateUnique()) checkerFramework = new Annotation[] { generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE) }; + parameter.annotations = copyAnnotations(source, findCopyableAnnotations(fieldNode), checkerFramework); params.add(parameter); } diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index 046b197f..1bca4767 100755 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2018 The Project Lombok Authors. + * Copyright (C) 2009-2019 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 @@ -42,6 +42,7 @@ import lombok.core.handlers.InclusionExclusionUtils; import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.core.AnnotationValues; import lombok.core.configuration.CallSuperType; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; @@ -235,7 +236,12 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler { EclipseHandlerUtil.registerCreatedLazyGetter((FieldDeclaration) fieldNode.get(), method.selector, returnType); - /* Generate annotations that must be put on the generated method, and attach them. */ { - Annotation[] deprecated = null; - if (isFieldDeprecated(fieldNode)) { - deprecated = new Annotation[] { generateDeprecatedAnnotation(source) }; - } + /* Generate annotations that must be put on the generated method, and attach them. */ { + Annotation[] deprecated = null, checkerFramework = null; + if (isFieldDeprecated(fieldNode)) deprecated = new Annotation[] { generateDeprecatedAnnotation(source) }; + if (getCheckerFrameworkVersion(fieldNode).generateSideEffectFree()) checkerFramework = new Annotation[] { generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) }; method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), findCopyableAnnotations(fieldNode), findDelegatesAndMarkAsHandled(fieldNode), + checkerFramework, deprecated); } diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index bb704ead..bfa60db0 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -36,6 +36,7 @@ import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.Setter; import lombok.core.AST.Kind; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; @@ -200,7 +201,11 @@ public class HandleSetter extends EclipseAnnotationHandler { returnThis = new ReturnStatement(thisRef, pS, pE); } - return createSetter(parent, deprecate, fieldNode, name, paramName, booleanFieldToSet, returnType, returnThis, modifier, sourceNode, onMethod, onParam); + MethodDeclaration d = createSetter(parent, deprecate, fieldNode, name, paramName, booleanFieldToSet, returnType, returnThis, modifier, sourceNode, onMethod, onParam); + if (shouldReturnThis && getCheckerFrameworkVersion(sourceNode).generateReturnsReceiver()) { + d.annotations = copyAnnotations(source, d.annotations, new Annotation[] { generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER) }); + } + return d; } static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] paramName, char[] booleanFieldToSet, TypeReference returnType, Statement returnStatement, int modifier, EclipseNode sourceNode, List onMethod, List onParam) { diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 88479911..8f0ef338 100755 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -57,9 +57,11 @@ import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; +import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.SuperReference; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; @@ -81,6 +83,7 @@ import lombok.ConfigurationKeys; import lombok.Singular; import lombok.ToString; import lombok.core.AST.Kind; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.handlers.HandlerUtil; import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.core.AnnotationValues; @@ -119,6 +122,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); + CheckerFrameworkVersion cfv = getCheckerFrameworkVersion(annotationNode); long p = (long) ast.sourceStart << 32 | ast.sourceEnd; @@ -269,7 +273,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // If there is no superclass, superclassBuilderClassExpression is still == null at this point. // You can use it to check whether to inherit or not. - generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, + generateBuilderBasedConstructor(cfv, tdParent, typeParams, builderFields, annotationNode, builderClassName, superclassBuilderClass != null); // Create the abstract builder class, or reuse an existing one. @@ -334,12 +338,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } // Generate abstract self() and build() methods in the abstract builder. - injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClass != null, builderGenericName)); - injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClass != null, classGenericName, ast)); + injectMethod(builderType, generateAbstractSelfMethod(cfv, tdParent, superclassBuilderClass != null, builderGenericName)); + injectMethod(builderType, generateAbstractBuildMethod(cfv, builderType, buildMethodName, builderFields, superclassBuilderClass != null, classGenericName, ast)); // Create the setter methods in the abstract builder. for (BuilderFieldData bfd : builderFields) { - generateSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName); + generateSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, builderGenericName); } // Create the toString() method for the abstract builder. @@ -386,22 +390,22 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { annotationNode.addWarning("Not generating toBuilder() as it already exists."); break; case NOT_EXISTS: - injectMethod(tdParent, generateToBuilderMethod(builderClassName, builderImplClassName, tdParent, typeParams, ast)); + injectMethod(tdParent, generateToBuilderMethod(cfv, builderClassName, builderImplClassName, tdParent, typeParams, ast)); default: // Should not happen. } } // Create the self() and build() methods in the BuilderImpl. - injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams, p)); + injectMethod(builderImplType, generateSelfMethod(cfv, builderImplType, typeParams, p)); if (methodExists(buildMethodName, builderImplType, -1) == MemberExistsResult.NOT_EXISTS) { - injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast)); + injectMethod(builderImplType, generateBuildMethod(cfv, builderImplType, buildMethodName, returnType, builderFields, ast)); } // Add the builder() method to the annotated class. if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; if (generateBuilderMethod) { - MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, tdParent, typeParams, ast); + MethodDeclaration md = generateBuilderMethod(cfv, builderMethodName, builderClassName, builderImplClassName, tdParent, typeParams, ast); if (md != null) injectMethod(tdParent, md); } @@ -483,6 +487,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { * the type (with the {@code @Builder} annotation) for which a * constructor should be generated. * @param typeParams + * @param cfv Settings for generating checker framework annotations * @param builderFields a list of fields in the builder which should be assigned to new instances. * @param source the annotation (used for setting source code locations for the generated code). * @param callBuilderBasedSuperConstructor @@ -490,7 +495,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { * constructor with the builder as argument. Requires * {@code builderClassAsParameter != null}. */ - private void generateBuilderBasedConstructor(EclipseNode typeNode, TypeParameter[] typeParams, List builderFields, + private void generateBuilderBasedConstructor(CheckerFrameworkVersion cfv, EclipseNode typeNode, TypeParameter[] typeParams, List builderFields, EclipseNode sourceNode, String builderClassName, boolean callBuilderBasedSuperConstructor) { ASTNode source = sourceNode.get(); @@ -501,6 +506,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); constructor.modifiers = toEclipseModifier(AccessLevel.PROTECTED); + if (cfv.generateUnique()) constructor.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)}; constructor.selector = typeDeclaration.name; if (callBuilderBasedSuperConstructor) { constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); @@ -572,7 +578,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { injectMethod(typeNode, constructor); } - private MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { + private MethodDeclaration generateBuilderMethod(CheckerFrameworkVersion cfv, String builderMethodName, String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; @@ -591,6 +597,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { invoke.type = namePlusTypeParamsToTypeReference(builderImplClassName.toCharArray(), typeParams, p); out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)}; + if (cfv.generateUnique()) out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)}; + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); return out; } @@ -603,7 +611,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { * } * */ - private MethodDeclaration generateToBuilderMethod(String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { + private MethodDeclaration generateToBuilderMethod(CheckerFrameworkVersion cfv, String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; @@ -623,6 +631,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { invokeFillMethod.arguments = new Expression[] {new ThisReference(0, 0)}; out.statements = new Statement[] {new ReturnStatement(invokeFillMethod, pS, pE)}; + if (cfv.generateUnique()) out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)}; + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); return out; } @@ -755,44 +765,64 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return ms; } - private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) { + private MethodDeclaration generateAbstractSelfMethod(CheckerFrameworkVersion cfv, EclipseNode tdParent, boolean override, String builderGenericName) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.selector = SELF_METHOD_NAME; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected | ExtraCompilerModifiers.AccSemicolonBody; - if (override) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())}; + Annotation overrideAnn = override ? makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get()) : null; + Annotation rrAnn = cfv.generateReturnsReceiver() ? generateNamedAnnotation(tdParent.get(), CheckerFrameworkVersion.NAME__RETURNS_RECEIVER): null; + Annotation sefAnn = cfv.generateSideEffectFree() ? generateNamedAnnotation(tdParent.get(), CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE): null; + if (overrideAnn != null && rrAnn != null && sefAnn != null) out.annotations = new Annotation[] {overrideAnn, rrAnn, sefAnn}; + else if (overrideAnn != null && rrAnn != null) out.annotations = new Annotation[] {overrideAnn, rrAnn}; + else if (overrideAnn != null && sefAnn != null) out.annotations = new Annotation[] {overrideAnn, sefAnn}; + else if (overrideAnn != null) out.annotations = new Annotation[] {overrideAnn}; + else if (rrAnn != null && sefAnn != null) out.annotations = new Annotation[] {rrAnn, sefAnn}; + else if (rrAnn != null) out.annotations = new Annotation[] {rrAnn}; + else if (sefAnn != null) out.annotations = new Annotation[] {sefAnn}; out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0); return out; } - private MethodDeclaration generateSelfMethod(EclipseNode builderImplType, TypeParameter[] typeParams, long p) { + private MethodDeclaration generateSelfMethod(CheckerFrameworkVersion cfv, EclipseNode builderImplType, TypeParameter[] typeParams, long p) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderImplType.top().get()).compilationResult); out.selector = SELF_METHOD_NAME; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.modifiers = ClassFileConstants.AccProtected; - out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, builderImplType.get())}; + Annotation overrideAnn = makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, builderImplType.get()); + Annotation rrAnn = cfv.generateReturnsReceiver() ? generateNamedAnnotation(builderImplType.get(), CheckerFrameworkVersion.NAME__RETURNS_RECEIVER): null; + Annotation sefAnn = cfv.generateSideEffectFree() ? generateNamedAnnotation(builderImplType.get(), CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE): null; + if (rrAnn != null && sefAnn != null) out.annotations = new Annotation[] {overrideAnn, rrAnn, sefAnn}; + else if (rrAnn != null) out.annotations = new Annotation[] {overrideAnn, rrAnn}; + else if (sefAnn != null) out.annotations = new Annotation[] {overrideAnn, sefAnn}; + else out.annotations = new Annotation[] {overrideAnn}; out.returnType = namePlusTypeParamsToTypeReference(builderImplType.getName().toCharArray(), typeParams, p); out.statements = new Statement[] {new ReturnStatement(new ThisReference(0, 0), 0, 0)}; return out; } - private MethodDeclaration generateAbstractBuildMethod(EclipseNode tdParent, String methodName, boolean override, + private MethodDeclaration generateAbstractBuildMethod(CheckerFrameworkVersion cfv, EclipseNode builderType, String methodName, List builderFields, boolean override, String classGenericName, ASTNode source) { - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract | ExtraCompilerModifiers.AccSemicolonBody; out.selector = methodName.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = new SingleTypeReference(classGenericName.toCharArray(), 0); - if (override) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source)}; + Annotation overrideAnn = override ? makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source) : null; + Annotation sefAnn = cfv.generateSideEffectFree() ? generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE): null; + if (overrideAnn != null && sefAnn != null) out.annotations = new Annotation[] {overrideAnn, sefAnn}; + else if (overrideAnn != null) out.annotations = new Annotation[] {overrideAnn}; + else if (sefAnn != null) out.annotations = new Annotation[] {sefAnn}; + out.arguments = HandleBuilder.generateBuildArgs(cfv, builderType, builderFields, source); out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return out; } - private MethodDeclaration generateBuildMethod(EclipseNode tdParent, String name, TypeReference returnType, ASTNode source) { - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); + private MethodDeclaration generateBuildMethod(CheckerFrameworkVersion cfv, EclipseNode builderType, String name, TypeReference returnType, List builderFields, ASTNode source) { + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List statements = new ArrayList(); @@ -800,7 +830,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { out.selector = name.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = returnType; - out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source)}; + Annotation overrideAnn = makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source); + Annotation sefAnn = cfv.generateSideEffectFree() ? generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE): null; + if (sefAnn != null) out.annotations = new Annotation[] {overrideAnn, sefAnn}; + else out.annotations = new Annotation[] {overrideAnn}; AllocationExpression allocationStatement = new AllocationExpression(); allocationStatement.type = copyType(out.returnType); @@ -809,6 +842,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { statements.add(new ReturnStatement(allocationStatement, 0, 0)); out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[0]); out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + out.arguments = HandleBuilder.generateBuildArgs(cfv, builderType, builderFields, source); return out; } @@ -872,7 +906,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } } - private void generateSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, final String builderGenericName) { + private void generateSetterMethodsForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, final String builderGenericName) { boolean deprecate = isFieldDeprecated(bfd.originalFieldNode); TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() { @@ -891,14 +925,15 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { }; if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - generateSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, true, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode, bfd.annotations, bfd.originalFieldNode); + generateSimpleSetterMethodForBuilder(cfv, builderType, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, true, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode, bfd.annotations, bfd.originalFieldNode); } else { - bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC); + bfd.singularData.getSingularizer().generateMethods(cfv, bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC); } } - private void generateSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, boolean fluent, TypeReference returnType, Statement returnStatement, EclipseNode sourceNode, Annotation[] annosOnParam, EclipseNode originalFieldNode) { + private void generateSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, boolean fluent, TypeReference returnType, Statement returnStatement, EclipseNode sourceNode, Annotation[] annosOnParam, EclipseNode originalFieldNode) { TypeDeclaration td = (TypeDeclaration) builderType.get(); + ASTNode source = sourceNode.get(); AbstractMethodDeclaration[] existing = td.methods; if (existing == null) existing = EMPTY_METHODS; int len = existing.length; @@ -913,11 +948,24 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { String setterName = fluent ? new String(paramName) : HandlerUtil.buildAccessorName("set", new String(paramName)); - List methodAnnsList = Collections.emptyList(); - Annotation[] methodAnns = EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); - if (methodAnns != null && methodAnns.length > 0) methodAnnsList = Arrays.asList(methodAnns); + List methodAnnsList = Arrays.asList(EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode)); + if (cfv.generateReturnsReceiver()) { + methodAnnsList = new ArrayList(methodAnnsList); + methodAnnsList.add(generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER)); + } MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, paramName, nameOfSetFlag, returnType, returnStatement, ClassFileConstants.AccPublic, - sourceNode, methodAnnsList, annosOnParam != null ? Arrays.asList(copyAnnotations(sourceNode.get(), annosOnParam)) : Collections.emptyList()); + sourceNode, methodAnnsList, annosOnParam != null ? Arrays.asList(copyAnnotations(source, annosOnParam)) : Collections.emptyList()); + if (cfv.generateCalledMethods()) { + Argument[] arr = setter.arguments == null ? new Argument[0] : setter.arguments; + Argument[] newArr = new Argument[arr.length + 1]; + System.arraycopy(arr, 0, newArr, 1, arr.length); + newArr[0] = new Argument(new char[] { 't', 'h', 'i', 's' }, 0, new SingleTypeReference(builderType.getName().toCharArray(), 0), Modifier.FINAL); + char[][] nameNotCalled = fromQualifiedName(CheckerFrameworkVersion.NAME__NOT_CALLED); + SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameNotCalled, poss(source, nameNotCalled.length)), source.sourceStart); + ann.memberValue = new StringLiteral(setterName.toCharArray(), 0, 0, 0); + newArr[0].annotations = new Annotation[] {ann}; + setter.arguments = newArr; + } injectMethod(builderType, setter); } diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index bb52feb5..39fd5937 100644 --- a/src/core/lombok/eclipse/handlers/HandleToString.java +++ b/src/core/lombok/eclipse/handlers/HandleToString.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2018 The Project Lombok Authors. + * Copyright (C) 2009-2019 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 @@ -60,6 +60,7 @@ import lombok.ToString; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.configuration.CallSuperType; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.handlers.HandlerUtil.FieldAccess; import lombok.core.handlers.InclusionExclusionUtils; import lombok.core.handlers.InclusionExclusionUtils.Included; @@ -300,7 +301,12 @@ public class HandleToString extends EclipseAnnotationHandler { method.modifiers = toEclipseModifier(AccessLevel.PUBLIC); method.returnType = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p, p, p}); setGeneratedBy(method.returnType, source); - method.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source)}; + Annotation overrideAnnotation = makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source); + if (getCheckerFrameworkVersion(type).generateSideEffectFree()) { + method.annotations = new Annotation[] { overrideAnnotation, generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) }; + } else { + method.annotations = new Annotation[] { overrideAnnotation }; + } method.arguments = null; method.selector = "toString".toCharArray(); method.thrownExceptions = null; diff --git a/src/core/lombok/eclipse/handlers/HandleWither.java b/src/core/lombok/eclipse/handlers/HandleWither.java index a99789a6..8be08cfe 100644 --- a/src/core/lombok/eclipse/handlers/HandleWither.java +++ b/src/core/lombok/eclipse/handlers/HandleWither.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2019 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.List; import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.core.AST.Kind; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; @@ -226,11 +227,11 @@ public class HandleWither extends EclipseAnnotationHandler { method.returnType = cloneSelfType(fieldNode, source); if (method.returnType == null) return null; - Annotation[] deprecated = null; - if (isFieldDeprecated(fieldNode)) { - deprecated = new Annotation[] { generateDeprecatedAnnotation(source) }; - } - method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated); + Annotation[] deprecated = null, checkerFramework = null; + if (isFieldDeprecated(fieldNode)) deprecated = new Annotation[] { generateDeprecatedAnnotation(source) }; + if (getCheckerFrameworkVersion(fieldNode).generateSideEffectFree()) checkerFramework = new Annotation[] { generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) }; + + method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), checkerFramework, deprecated); Argument param = new Argument(field.name, p, copyType(field.type, source), ClassFileConstants.AccFinal); param.sourceStart = pS; param.sourceEnd = pE; method.arguments = new Argument[] { param }; diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index 642ba9b7..338f5eab 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java @@ -31,6 +31,7 @@ import java.util.List; import lombok.AccessLevel; import lombok.core.GuavaTypeMap; import lombok.core.LombokImmutableList; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.handlers.HandlerUtil; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.HandleNonNull; @@ -111,13 +112,13 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { - generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); - generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); - generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, access); + @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + generateSingularMethod(cfv, deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); + generatePluralMethod(cfv, deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); + generateClearMethod(cfv, deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, access); } - void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, AccessLevel access) { + void generateClearMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, AccessLevel access) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = toEclipseModifier(access); @@ -128,13 +129,13 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { md.selector = HandlerUtil.buildAccessorName("clear", new String(data.getPluralName())).toCharArray(); md.statements = returnStatement != null ? new Statement[] {a, returnStatement} : new Statement[] {a}; md.returnType = returnType; - md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); injectMethod(builderType, md); } - void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { + void generateSingularMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { LombokImmutableList suffixes = getArgumentSuffixes(); char[][] names = new char[suffixes.size()][]; for (int i = 0; i < suffixes.size(); i++) { @@ -172,13 +173,13 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { } md.returnType = returnType; md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName(getAddMethodName(), new String(data.getSingularName())).toCharArray(); - md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); HandleNonNull.INSTANCE.fix(injectMethod(builderType, md)); } - void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { + void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = toEclipseModifier(access); @@ -204,7 +205,7 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { md.arguments = new Argument[] {param}; md.returnType = returnType; md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName(getAddMethodName() + "All", new String(data.getPluralName())).toCharArray(); - md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); injectMethod(builderType, md); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java index 73d6fe9b..53ea15a6 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.List; import lombok.AccessLevel; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.handlers.HandlerUtil; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.HandleNonNull; @@ -91,18 +92,18 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker, access); + guavaListSetSingularizer.generateMethods(cfv, data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker, access); return; } - generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); - generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); - generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, access); + generateSingularMethod(cfv, deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); + generatePluralMethod(cfv, deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); + generateClearMethod(cfv, deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, access); } - private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, AccessLevel access) { + private void generateClearMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, AccessLevel access) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = toEclipseModifier(access); @@ -118,13 +119,13 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula Statement clearStatement = new IfStatement(new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL), clearMsg, 0, 0); md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement}; md.returnType = returnType; - md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); injectMethod(builderType, md); } - void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { + void generateSingularMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = toEclipseModifier(access); @@ -150,13 +151,13 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula md.arguments = new Argument[] {param}; md.returnType = returnType; md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("add", new String(data.getSingularName())).toCharArray(); - md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); HandleNonNull.INSTANCE.fix(injectMethod(builderType, md)); } - void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { + void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = toEclipseModifier(access); @@ -181,7 +182,7 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula md.arguments = new Argument[] {param}; md.returnType = returnType; md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName("addAll", new String(data.getPluralName())).toCharArray(); - md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); injectMethod(builderType, md); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index b642522f..8684987f 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -54,6 +54,7 @@ import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; import lombok.core.LombokImmutableList; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.handlers.HandlerUtil; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; @@ -150,18 +151,18 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker, access); + guavaMapSingularizer.generateMethods(cfv, data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker, access); return; } - generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); - generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); - generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, access); + generateSingularMethod(cfv, deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); + generatePluralMethod(cfv, deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access); + generateClearMethod(cfv, deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, access); } - private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, AccessLevel access) { + private void generateClearMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, AccessLevel access) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = toEclipseModifier(access); @@ -188,13 +189,13 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer Statement clearStatement = new IfStatement(new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL), clearMsgs, 0, 0); md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement}; md.returnType = returnType; - md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); injectMethod(builderType, md); } - private void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { + private void generateSingularMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = toEclipseModifier(access); @@ -245,13 +246,13 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer md.arguments = new Argument[] {keyParam, valueParam}; md.returnType = returnType; md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("put", new String(data.getSingularName())).toCharArray(); - md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); HandleNonNull.INSTANCE.fix(injectMethod(builderType, md)); } - private void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { + private void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = toEclipseModifier(access); @@ -309,7 +310,7 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer md.arguments = new Argument[] {param}; md.returnType = returnType; md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName("putAll", new String(data.getPluralName())).toCharArray(); - md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); injectMethod(builderType, md); diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 35a6dc26..de2bdde1 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -58,6 +58,7 @@ import lombok.ToString; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.handlers.HandlerUtil; import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.experimental.NonFinal; @@ -102,6 +103,7 @@ public class HandleBuilder extends JavacAnnotationHandler { @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); + CheckerFrameworkVersion cfv = getCheckerFrameworkVersion(annotationNode); Builder builderInstance = annotation.getInstance(); AccessLevel accessForOuters = builderInstance.access(); @@ -434,14 +436,14 @@ public class HandleBuilder extends JavacAnnotationHandler { } for (BuilderFieldData bfd : builderFields) { - makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain, accessForInners); + makeSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, fluent, chain, accessForInners); } { MemberExistsResult methodExists = methodExists(buildMethodName, builderType, -1); if (methodExists == MemberExistsResult.EXISTS_BY_LOMBOK) methodExists = methodExists(buildMethodName, builderType, 0); if (methodExists == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning, accessForInners); + JCMethodDecl md = generateBuildMethod(cfv, tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning, accessForInners); if (md != null) { injectMethod(builderType, md); recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); @@ -465,7 +467,7 @@ public class HandleBuilder extends JavacAnnotationHandler { if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; if (generateBuilderMethod) { - JCMethodDecl md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, annotationNode, tdParent, typeParams, accessForOuters); + JCMethodDecl md = generateBuilderMethod(cfv, isStatic, builderMethodName, builderClassName, annotationNode, tdParent, typeParams, accessForOuters); recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); if (md != null) injectMethod(tdParent, md); } @@ -485,7 +487,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } tps = lb.toList(); } - JCMethodDecl md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast, accessForOuters); + JCMethodDecl md = generateToBuilderMethod(cfv, toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast, accessForOuters); if (md != null) { recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); injectMethod(tdParent, md); @@ -534,7 +536,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } private static final String BUILDER_TEMP_VAR = "builder"; - private JCMethodDecl generateToBuilderMethod(String toBuilderMethodName, String builderClassName, JavacNode type, List typeParams, java.util.List builderFields, boolean fluent, JCAnnotation ast, AccessLevel access) { + private JCMethodDecl generateToBuilderMethod(CheckerFrameworkVersion cfv, String toBuilderMethodName, String builderClassName, JavacNode type, List typeParams, java.util.List builderFields, boolean fluent, JCAnnotation ast, AccessLevel access) { // return new ThingieBuilder().setA(this.a).setB(this.b); JavacTreeMaker maker = type.getTreeMaker(); @@ -585,7 +587,8 @@ public class HandleBuilder extends JavacAnnotationHandler { statements.append(maker.Return(invoke)); } JCBlock body = maker.Block(0, statements.toList()); - return maker.MethodDef(maker.Modifiers(toJavacModifier(access)), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), List.nil(), List.nil(), body, null); + List annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__UNIQUE), List.nil())) : List.nil(); + return maker.MethodDef(maker.Modifiers(toJavacModifier(access), annsOnMethod), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), List.nil(), List.nil(), body, null); } private JCMethodDecl generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) { @@ -617,7 +620,30 @@ public class HandleBuilder extends JavacAnnotationHandler { */ } - private JCMethodDecl generateBuildMethod(JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List builderFields, JavacNode type, List thrownExceptions, JCTree source, boolean addCleaning, AccessLevel access) { + static List generateBuildArgs(CheckerFrameworkVersion cfv, JavacNode type, java.util.List builderFields) { + if (!cfv.generateCalledMethods()) return List.nil(); + + ArrayList mandatories = new ArrayList(); + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData == null && bfd.nameOfSetFlag == null) mandatories.add(bfd.name.toString()); + } + + JCExpression arg; + JavacTreeMaker maker = type.getTreeMaker(); + if (mandatories.size() == 0) return List.nil(); + if (mandatories.size() == 1) arg = maker.Literal(mandatories.get(0)); + else { + List elems = List.nil(); + for (int i = mandatories.size() - 1; i >= 0; i--) elems = elems.prepend(maker.Literal(mandatories.get(i))); + arg = maker.NewArray(null, List.nil(), elems); + } + JCAnnotation recvAnno = maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__CALLED), List.of(arg)); + JCClassDecl builderTypeNode = (JCClassDecl) type.get(); + JCVariableDecl recv = maker.VarDef(maker.Modifiers(0L, List.of(recvAnno)), type.toName("this"), maker.Ident(builderTypeNode.name), null); + return List.of(recv); + } + + private JCMethodDecl generateBuildMethod(CheckerFrameworkVersion cfv, JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List builderFields, JavacNode type, List thrownExceptions, JCTree source, boolean addCleaning, AccessLevel access) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; @@ -670,7 +696,9 @@ public class HandleBuilder extends JavacAnnotationHandler { JCBlock body = maker.Block(0, statements.toList()); - return maker.MethodDef(maker.Modifiers(toJavacModifier(access)), type.toName(buildName), returnType, List.nil(), List.nil(), thrownExceptions, body, null); + List annsOnMethod = cfv.generateSideEffectFree() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())) : List.nil(); + List params = generateBuildArgs(cfv, type, builderFields); + return maker.MethodDef(maker.Modifiers(toJavacModifier(access), annsOnMethod), type.toName(buildName), returnType, List.nil(), params, thrownExceptions, body, null); } public static JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode, List params) { @@ -685,7 +713,7 @@ public class HandleBuilder extends JavacAnnotationHandler { return maker.MethodDef(maker.Modifiers(modifiers), methodName, cloneType(maker, field.vartype, field, fieldNode.getContext()), copyTypeParams(fieldNode, params), List.nil(), List.nil(), body, null); } - public JCMethodDecl generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, JavacNode source, JavacNode type, List typeParams, AccessLevel access) { + public JCMethodDecl generateBuilderMethod(CheckerFrameworkVersion cfv, boolean isStatic, String builderMethodName, String builderClassName, JavacNode source, JavacNode type, List typeParams, AccessLevel access) { JavacTreeMaker maker = type.getTreeMaker(); ListBuffer typeArgs = new ListBuffer(); @@ -699,7 +727,14 @@ public class HandleBuilder extends JavacAnnotationHandler { JCBlock body = maker.Block(0, List.of(statement)); int modifiers = toJavacModifier(access); if (isStatic) modifiers |= Flags.STATIC; - return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(source, typeParams), List.nil(), List.nil(), body, null); + JCAnnotation annUnique = cfv.generateUnique() ? maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__UNIQUE), List.nil()) : null; + JCAnnotation annSef = cfv.generateSideEffectFree() ? maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil()) : null; + List annsOnMethod; + if (annUnique != null && annSef != null) annsOnMethod = List.of(annUnique, annSef); + else if (annUnique != null) annsOnMethod = List.of(annUnique); + else if (annSef != null) annsOnMethod = List.of(annSef); + else annsOnMethod = List.nil(); + return maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(source, typeParams), List.nil(), List.nil(), body, null); } public void generateBuilderFields(JavacNode builderType, java.util.List builderFields, JCTree source) { @@ -741,16 +776,16 @@ public class HandleBuilder extends JavacAnnotationHandler { for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, source, builderType.getContext()); } - public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain, AccessLevel access) { + public void makeSetterMethodsForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain, AccessLevel access) { boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, source, fluent, chain, fieldNode.annotations, fieldNode.originalFieldNode, access); + makeSimpleSetterMethodForBuilder(cfv, builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, source, fluent, chain, fieldNode.annotations, fieldNode.originalFieldNode, access); } else { - fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain, access); + fieldNode.singularData.getSingularizer().generateMethods(cfv, fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain, access); } } - private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, List annosOnParam, JavacNode originalFieldNode, AccessLevel access) { + private void makeSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, List annosOnParam, JavacNode originalFieldNode, AccessLevel access) { Name fieldName = ((JCVariableDecl) fieldNode.get()).name; for (JavacNode child : builderType.down()) { @@ -766,6 +801,12 @@ public class HandleBuilder extends JavacAnnotationHandler { List methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); JCMethodDecl newMethod = HandleSetter.createSetter(toJavacModifier(access), deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, chain, source, methodAnns, annosOnParam); + if (cfv.generateCalledMethods()) { + JCAnnotation ncAnno = maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__NOT_CALLED), List.of(maker.Literal(newMethod.getName().toString()))); + JCClassDecl builderTypeNode = (JCClassDecl) builderType.get(); + JCVariableDecl recv = maker.VarDef(maker.Modifiers(0L, List.of(ncAnno)), builderType.toName("this"), maker.Ident(builderTypeNode.name), null); + newMethod.params = List.of(recv, newMethod.params.get(0)); + } recursiveSetGeneratedBy(newMethod, source.get(), builderType.getContext()); copyJavadoc(originalFieldNode, newMethod, CopyJavadoc.SETTER); diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index 096963f4..9af124b5 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2018 The Project Lombok Authors. + * Copyright (C) 2010-2019 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 @@ -56,6 +56,7 @@ import lombok.RequiredArgsConstructor; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.LombokNode; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.delombok.LombokOptionsFactory; import lombok.javac.Javac; import lombok.javac.JavacAST; @@ -222,7 +223,6 @@ public class HandleConstructor { boolean staticConstrRequired = staticName != null && !staticName.equals(""); if (skipIfConstructorExists != SkipIfConstructorExists.NO) { - for (JavacNode child : typeNode.down()) { if (child.getKind() == Kind.ANNOTATION) { boolean skipGeneration = annotationTypeMatches(NoArgsConstructor.class, child) || @@ -247,7 +247,7 @@ public class HandleConstructor { } if (noArgs && noArgsConstructorExists(typeNode)) return; - + ListBuffer argTypes = new ListBuffer(); for (JavacNode fieldNode : fields) { Type mirror = getMirrorForFieldType(fieldNode); @@ -373,7 +373,7 @@ public class HandleConstructor { addConstructorProperties(mods, typeNode, fieldsToParam); } if (onConstructor != null) mods.annotations = mods.annotations.appendList(copyAnnotations(onConstructor)); - + if (getCheckerFrameworkVersion(source).generateUnique()) mods.annotations = mods.annotations.prepend(maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__UNIQUE), List.nil())); return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName(""), null, List.nil(), params.toList(), List.nil(), maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext()); @@ -452,6 +452,7 @@ public class HandleConstructor { JCClassDecl type = (JCClassDecl) typeNode.get(); JCModifiers mods = maker.Modifiers(Flags.STATIC | toJavacModifier(level)); + if (getCheckerFrameworkVersion(typeNode).generateUnique()) mods.annotations = mods.annotations.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__UNIQUE), List.nil())); JCExpression returnType, constructorType; diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index e4d7fa7f..2981bfa7 100644 --- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2018 The Project Lombok Authors. + * Copyright (C) 2009-2019 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 @@ -59,6 +59,7 @@ import lombok.EqualsAndHashCode; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.configuration.CallSuperType; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.handlers.HandlerUtil; import lombok.core.handlers.HandlerUtil.FieldAccess; import lombok.core.handlers.InclusionExclusionUtils; @@ -203,7 +204,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil()); - JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation)); + List annsOnMethod = List.of(overrideAnnotation); + CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode); + if (checkerFramework.generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())); + JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod); JCExpression returnType = maker.TypeIdent(CTC_INT); ListBuffer statements = new ListBuffer(); @@ -378,7 +382,12 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil()); - JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation)); + List annsOnMethod = List.of(overrideAnnotation); + CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode); + if (checkerFramework.generateSideEffectFree()) { + annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())); + } + JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod); JCExpression objectType = genJavaLangTypeRef(typeNode, "Object"); JCExpression returnType = maker.TypeIdent(CTC_BOOLEAN); @@ -497,7 +506,12 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil()); + List annsOnMethod = List.nil(); + CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode); + if (checkerFramework.generateSideEffectFree()) { + annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())); + } + JCModifiers mods = maker.Modifiers(Flags.PROTECTED, annsOnMethod); JCExpression returnType = maker.TypeIdent(CTC_BOOLEAN); Name canEqualName = typeNode.toName("canEqual"); JCExpression objectType = genJavaLangTypeRef(typeNode, "Object"); diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 7a178f66..f4ac2584 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2019 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.experimental.Delegate; import lombok.Getter; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -167,6 +168,7 @@ public class HandleGetter extends JavacAnnotationHandler { public void createGetterForField(AccessLevel level, JavacNode fieldNode, JavacNode source, boolean whineIfExists, boolean lazy, List onMethod) { + if (fieldNode.getKind() != Kind.FIELD) { source.addError("@Getter is only supported on a class or a field."); return; @@ -246,9 +248,8 @@ public class HandleGetter extends JavacAnnotationHandler { List copyableAnnotations = findCopyableAnnotations(field); List delegates = findDelegatesAndRemoveFromField(field); List annsOnMethod = copyAnnotations(onMethod).appendList(copyableAnnotations); - if (isFieldDeprecated(field)) { - annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.nil())); - } + if (getCheckerFrameworkVersion(field).generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genTypeRef(field, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())); + if (isFieldDeprecated(field)) annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.nil())); JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType, methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source, field.getContext()); diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index cd8b5d1c..26820fe6 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -32,6 +32,7 @@ import lombok.ConfigurationKeys; import lombok.Setter; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; @@ -214,7 +215,15 @@ public class HandleSetter extends JavacAnnotationHandler { returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this"))); } - return createSetter(access, deprecate, field, treeMaker, setterName, paramName, booleanFieldToSet, returnType, returnStatement, source, onMethod, onParam); + JCMethodDecl d = createSetter(access, deprecate, field, treeMaker, setterName, paramName, booleanFieldToSet, returnType, returnStatement, source, onMethod, onParam); + if (shouldReturnThis && getCheckerFrameworkVersion(source).generateReturnsReceiver()) { + List annotations = d.mods.annotations; + if (annotations == null) annotations = List.nil(); + JCAnnotation anno = treeMaker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.nil()); + recursiveSetGeneratedBy(anno, source.get(), field.getContext()); + d.mods.annotations = annotations.prepend(anno); + } + return d; } public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name paramName, Name booleanFieldToSet, JCExpression methodType, JCStatement returnStatement, JavacNode source, List onMethod, List onParam) { diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index a5c492a1..0f809571 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -62,6 +62,7 @@ import lombok.ConfigurationKeys; import lombok.Singular; import lombok.ToString; import lombok.core.AST.Kind; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.core.handlers.HandlerUtil; @@ -93,7 +94,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); - + CheckerFrameworkVersion cfv = getCheckerFrameworkVersion(annotationNode); SuperBuilder superbuilderAnnotation = annotation.getInstance(); deleteAnnotationIfNeccessary(annotationNode, SuperBuilder.class); @@ -284,16 +285,16 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } // Generate abstract self() and build() methods in the abstract builder. - JCMethodDecl asm = generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName); + JCMethodDecl asm = generateAbstractSelfMethod(cfv, tdParent, superclassBuilderClassExpression != null, builderGenericName); recursiveSetGeneratedBy(asm, ast, annotationNode.getContext()); injectMethod(builderType, asm); - JCMethodDecl abm = generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassExpression != null, classGenericName); + JCMethodDecl abm = generateAbstractBuildMethod(cfv, tdParent, buildMethodName, builderFields, superclassBuilderClassExpression != null, classGenericName); recursiveSetGeneratedBy(abm, ast, annotationNode.getContext()); injectMethod(builderType, abm); // Create the setter methods in the abstract builder. for (BuilderFieldData bfd : builderFields) { - generateSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName); + generateSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, builderGenericName); } // Create the toString() method for the abstract builder. @@ -339,18 +340,18 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (cd != null) injectMethod(builderImplType, cd); // Create the self() and build() methods in the BuilderImpl. - JCMethodDecl selfMethod = generateSelfMethod(builderImplType, typeParams); + JCMethodDecl selfMethod = generateSelfMethod(cfv, builderImplType, typeParams); recursiveSetGeneratedBy(selfMethod, ast, annotationNode.getContext()); injectMethod(builderImplType, selfMethod); if (methodExists(buildMethodName, builderImplType, -1) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl buildMethod = generateBuildMethod(buildMethodName, tdParent, builderImplType, thrownExceptions); + JCMethodDecl buildMethod = generateBuildMethod(cfv, buildMethodName, tdParent, builderImplType, builderFields, thrownExceptions); recursiveSetGeneratedBy(buildMethod, ast, annotationNode.getContext()); injectMethod(builderImplType, buildMethod); } } // Generate a constructor in the annotated class that takes a builder as argument. - generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, + generateBuilderBasedConstructor(cfv, tdParent, typeParams, builderFields, annotationNode, builderClassName, superclassBuilderClassExpression != null); if (isAbstract) { @@ -362,7 +363,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Allow users to specify their own builder() methods, e.g., to provide default values. if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; if (generateBuilderMethod) { - JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); + JCMethodDecl builderMethod = generateBuilderMethod(cfv, builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext()); if (builderMethod != null) injectMethod(tdParent, builderMethod); } @@ -374,7 +375,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { annotationNode.addWarning("Not generating toBuilder() as it already exists."); return; case NOT_EXISTS: - JCMethodDecl md = generateToBuilderMethod(builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); + JCMethodDecl md = generateToBuilderMethod(cfv, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); if (md != null) { recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); injectMethod(tdParent, md); @@ -483,7 +484,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { * constructor with the builder as argument. Requires * {@code builderClassAsParameter != null}. */ - private void generateBuilderBasedConstructor(JavacNode typeNode, List typeParams, java.util.List builderFields, JavacNode source, String builderClassName, boolean callBuilderBasedSuperConstructor) { + private void generateBuilderBasedConstructor(CheckerFrameworkVersion cfv, JavacNode typeNode, List typeParams, java.util.List builderFields, JavacNode source, String builderClassName, boolean callBuilderBasedSuperConstructor) { JavacTreeMaker maker = typeNode.getTreeMaker(); AccessLevel level = AccessLevel.PROTECTED; @@ -519,7 +520,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } - JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.nil()); + List annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())) : List.nil(); + JCModifiers mods = maker.Modifiers(toJavacModifier(level), annsOnMethod); // Create a constructor that has just the builder as parameter. ListBuffer params = new ListBuffer(); @@ -551,7 +553,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); } - private JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List typeParams) { + private JCMethodDecl generateBuilderMethod(CheckerFrameworkVersion cfv, String builderMethodName, String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List typeParams) { JavacTreeMaker maker = type.getTreeMaker(); ListBuffer typeArgs = new ListBuffer(); @@ -573,7 +575,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { typeParameterNames.add(wildcard); JCTypeApply returnType = maker.TypeApply(maker.Ident(type.toName(builderClassName)), typeParameterNames.toList()); - return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.nil(), List.nil(), body, null); + List annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())) : List.nil(); + return maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.nil(), List.nil(), body, null); } /** @@ -584,7 +587,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { * } * */ - private JCMethodDecl generateToBuilderMethod(String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List typeParams) { + private JCMethodDecl generateToBuilderMethod(CheckerFrameworkVersion cfv, String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List typeParams) { JavacTreeMaker maker = type.getTreeMaker(); ListBuffer typeArgs = new ListBuffer(); @@ -607,7 +610,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { typeParameterNames.add(wildcard); JCTypeApply returnType = maker.TypeApply(maker.Ident(type.toName(builderClassName)), typeParameterNames.toList()); - return maker.MethodDef(maker.Modifiers(modifiers), type.toName(TO_BUILDER_METHOD_NAME), returnType, List.nil(), List.nil(), List.nil(), body, null); + List annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())) : List.nil(); + return maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(TO_BUILDER_METHOD_NAME), returnType, List.nil(), List.nil(), List.nil(), body, null); } /** @@ -737,25 +741,34 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return exec; } - private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) { + private JCMethodDecl generateAbstractSelfMethod(CheckerFrameworkVersion cfv, JavacNode type, boolean override, String builderGenericName) { JavacTreeMaker maker = type.getTreeMaker(); List annotations = List.nil(); - if (override) { - JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.nil()); - annotations = List.of(overrideAnnotation); - } + JCAnnotation overrideAnnotation = override ? maker.Annotation(genJavaLangTypeRef(type, "Override"), List.nil()) : null; + JCAnnotation rrAnnotation = cfv.generateReturnsReceiver() ? maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.nil()) : null; + JCAnnotation sefAnnotation = cfv.generateSideEffectFree() ? maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil()) : null; + if (sefAnnotation != null) annotations = annotations.prepend(sefAnnotation); + if (rrAnnotation != null) annotations = annotations.prepend(rrAnnotation); + if (overrideAnnotation != null) annotations = annotations.prepend(overrideAnnotation); JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations); Name name = type.toName(SELF_METHOD); JCExpression returnType = maker.Ident(type.toName(builderGenericName)); - + return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); } - private JCMethodDecl generateSelfMethod(JavacNode builderImplType, List typeParams) { + private JCMethodDecl generateSelfMethod(CheckerFrameworkVersion cfv, JavacNode builderImplType, List typeParams) { JavacTreeMaker maker = builderImplType.getTreeMaker(); JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(builderImplType, "Override"), List.nil()); - JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED, List.of(overrideAnnotation)); + JCAnnotation rrAnnotation = cfv.generateReturnsReceiver() ? maker.Annotation(genTypeRef(builderImplType, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.nil()) : null; + JCAnnotation sefAnnotation = cfv.generateSideEffectFree() ? maker.Annotation(genTypeRef(builderImplType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil()) : null; + List annsOnMethod = List.nil(); + if (sefAnnotation != null) annsOnMethod = annsOnMethod.prepend(sefAnnotation); + if (rrAnnotation != null) annsOnMethod = annsOnMethod.prepend(rrAnnotation); + annsOnMethod = annsOnMethod.prepend(overrideAnnotation); + + JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED, annsOnMethod); Name name = builderImplType.toName(SELF_METHOD); JCExpression returnType = namePlusTypeParamsToTypeReference(maker, builderImplType.toName(builderImplType.getName()), typeParams); @@ -765,21 +778,23 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), body, null); } - private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName, boolean override, String classGenericName) { + private JCMethodDecl generateAbstractBuildMethod(CheckerFrameworkVersion cfv, JavacNode type, String methodName, java.util.List builderFields, boolean override, String classGenericName) { JavacTreeMaker maker = type.getTreeMaker(); List annotations = List.nil(); if (override) { JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.nil()); annotations = List.of(overrideAnnotation); } + if (cfv.generateSideEffectFree()) annotations = annotations.prepend(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())); JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT, annotations); Name name = type.toName(methodName); JCExpression returnType = maker.Ident(type.toName(classGenericName)); - return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); + List params = HandleBuilder.generateBuildArgs(cfv, type, builderFields); + return maker.MethodDef(modifiers, name, returnType, List.nil(), params, List.nil(), null, null); } - private JCMethodDecl generateBuildMethod(String buildName, JavacNode returnType, JavacNode type, List thrownExceptions) { + private JCMethodDecl generateBuildMethod(CheckerFrameworkVersion cfv, String buildName, JavacNode returnType, JavacNode type, java.util.List builderFields, List thrownExceptions) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; @@ -793,9 +808,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JCBlock body = maker.Block(0, statements.toList()); JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.nil()); - JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation)); + List annsOnMethod = List.of(overrideAnnotation); + if (cfv.generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())); + JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, annsOnMethod); - return maker.MethodDef(modifiers, type.toName(buildName), cloneSelfType(returnType), List.nil(), List.nil(), thrownExceptions, body, null); + List params = HandleBuilder.generateBuildArgs(cfv, type, builderFields); + return maker.MethodDef(modifiers, type.toName(buildName), cloneSelfType(returnType), List.nil(), params, thrownExceptions, body, null); } private JCMethodDecl generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) { @@ -852,7 +870,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, source, builderType.getContext()); } - private void generateSetterMethodsForBuilder(final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) { + private void generateSetterMethodsForBuilder(CheckerFrameworkVersion cfv, final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) { boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); final JavacTreeMaker maker = builderType.getTreeMaker(); ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() { @@ -864,13 +882,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { }}; if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, source, true, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations, fieldNode.originalFieldNode); + generateSimpleSetterMethodForBuilder(cfv, builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, source, true, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations, fieldNode.originalFieldNode); } else { - fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC); + fieldNode.singularData.getSingularizer().generateMethods(cfv, fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC); } } - private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, boolean fluent, JCExpression returnType, JCStatement returnStatement, List annosOnParam, JavacNode originalFieldNode) { + private void generateSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, boolean fluent, JCExpression returnType, JCStatement returnStatement, List annosOnParam, JavacNode originalFieldNode) { Name fieldName = ((JCVariableDecl) fieldNode.get()).name; for (JavacNode child : builderType.down()) { @@ -886,6 +904,19 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { List methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, returnType, returnStatement, source, methodAnns, annosOnParam); + if (cfv.generateCalledMethods()) { + JCAnnotation ncAnno = maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__NOT_CALLED), List.of(maker.Literal(newMethod.getName().toString()))); + JCClassDecl builderTypeNode = (JCClassDecl) builderType.get(); + JCVariableDecl recv = maker.VarDef(maker.Modifiers(0L, List.of(ncAnno)), builderType.toName("this"), maker.Ident(builderTypeNode.name), null); + newMethod.params = List.of(recv, newMethod.params.get(0)); + } + if (cfv.generateReturnsReceiver()) { + List annotations = newMethod.mods.annotations; + if (annotations == null) annotations = List.nil(); + JCAnnotation anno = maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.nil()); + recursiveSetGeneratedBy(anno, source.get(), builderType.getContext()); + newMethod.mods.annotations = annotations.prepend(anno); + } injectMethod(builderType, newMethod); } diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index 3780da79..d0d36e06 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2018 The Project Lombok Authors. + * Copyright (C) 2009-2019 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.ConfigurationKeys; import lombok.ToString; import lombok.core.AnnotationValues; import lombok.core.configuration.CallSuperType; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.AST.Kind; import lombok.core.handlers.InclusionExclusionUtils; import lombok.core.handlers.InclusionExclusionUtils.Included; @@ -93,7 +94,7 @@ public class HandleToString extends JavacAnnotationHandler { 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(); + includeFieldNames = configuration != null ? configuration : ((Boolean) ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); } catch (Exception ignore) {} Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); @@ -160,7 +161,9 @@ public class HandleToString extends JavacAnnotationHandler { JavacTreeMaker maker = typeNode.getTreeMaker(); JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.nil()); - JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation)); + List annsOnMethod = List.of(overrideAnnotation); + if (getCheckerFrameworkVersion(typeNode).generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())); + JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod); JCExpression returnType = genJavaLangTypeRef(typeNode, "String"); boolean first = true; diff --git a/src/core/lombok/javac/handlers/HandleWither.java b/src/core/lombok/javac/handlers/HandleWither.java index 33c4dec2..9c95cb78 100644 --- a/src/core/lombok/javac/handlers/HandleWither.java +++ b/src/core/lombok/javac/handlers/HandleWither.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2019 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.core.AST.Kind; import lombok.core.AnnotationValues; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.experimental.Wither; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; @@ -279,10 +280,11 @@ public class HandleWither extends JavacAnnotationHandler { JCExpression annotationMethodDefaultValue = null; List annsOnMethod = copyAnnotations(onMethod); + CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(source); + if (checkerFramework.generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil())); + + if (isFieldDeprecated(field)) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.nil())); - if (isFieldDeprecated(field)) { - annsOnMethod = annsOnMethod.prepend(maker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.nil())); - } if (makeAbstract) access = access | Flags.ABSTRACT; JCMethodDecl decl = recursiveSetGeneratedBy(maker.MethodDef(maker.Modifiers(access, annsOnMethod), methodName, returnType, methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source.get(), field.getContext()); diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 5f0f39b0..f23a894a 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -93,6 +93,7 @@ import lombok.core.AnnotationValues.AnnotationValue; import lombok.core.CleanupTask; import lombok.core.LombokImmutableList; import lombok.core.TypeResolver; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.configuration.NullCheckExceptionType; import lombok.core.configuration.TypeName; import lombok.core.handlers.HandlerUtil; @@ -330,6 +331,11 @@ public class JavacHandlerUtil { return false; } + public static CheckerFrameworkVersion getCheckerFrameworkVersion(JavacNode node) { + CheckerFrameworkVersion cfv = node.getAst().readConfiguration(ConfigurationKeys.CHECKER_FRAMEWORK); + return cfv == null ? CheckerFrameworkVersion.NONE : cfv; + } + /** * Returns if a node is marked deprecated (as picked up on by the parser). * @param node the node to check (type, method, or field decl). @@ -1266,7 +1272,7 @@ public class JavacHandlerUtil { } } - private static void addAnnotation(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context, String annotationTypeFqn, JCExpression arg) { + public static void addAnnotation(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context, String annotationTypeFqn, JCExpression arg) { boolean isJavaLangBased; String simpleName; { int idx = annotationTypeFqn.lastIndexOf('.'); diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index a5895951..341d44df 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -37,6 +37,7 @@ import lombok.ConfigurationKeys; import lombok.core.LombokImmutableList; import lombok.core.SpiLoadUtil; import lombok.core.TypeLibrary; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -163,9 +164,12 @@ public class JavacSingularsRecipes { return this; } - protected JCModifiers makeMods(JavacTreeMaker maker, JavacNode node, boolean deprecate, AccessLevel access) { - if (deprecate) return maker.Modifiers(toJavacModifier(access), List.of(maker.Annotation(genJavaLangTypeRef(node, "Deprecated"), List.nil()))); - return maker.Modifiers(toJavacModifier(access)); + protected JCModifiers makeMods(JavacTreeMaker maker, CheckerFrameworkVersion cfv, JavacNode node, boolean deprecate, AccessLevel access) { + JCAnnotation deprecateAnn = deprecate ? maker.Annotation(genJavaLangTypeRef(node, "Deprecated"), List.nil()) : null; + JCAnnotation rrAnn = cfv.generateReturnsReceiver() ? maker.Annotation(genTypeRef(node, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.nil()) : null; + + List annsOnMethod = (deprecateAnn != null && rrAnn != null) ? List.of(deprecateAnn, rrAnn) : deprecateAnn != null ? List.of(deprecateAnn) : rrAnn != null ? List.of(rrAnn) : List.nil(); + return maker.Modifiers(toJavacModifier(access), annsOnMethod); } /** Checks if any of the to-be-generated nodes (fields, methods) already exist. If so, errors on these (singulars don't support manually writing some of it, and returns true). */ @@ -220,7 +224,7 @@ public class JavacSingularsRecipes { * If you need more control over the return type and value, use * {@link #generateMethods(SingularData, boolean, JavacNode, JCTree, boolean, ExpressionMaker, StatementMaker)}. */ - public void generateMethods(SingularData data, boolean deprecate, final JavacNode builderType, JCTree source, boolean fluent, final boolean chain, AccessLevel access) { + public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, final JavacNode builderType, JCTree source, boolean fluent, final boolean chain, AccessLevel access) { final JavacTreeMaker maker = builderType.getTreeMaker(); ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() { @@ -233,26 +237,26 @@ public class JavacSingularsRecipes { return chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; }}; - generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); + generateMethods(cfv, data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); } /** * Generates the singular, plural, and clear methods for the given {@link SingularData}. * Uses the given {@code returnTypeMaker} and {@code returnStatementMaker} for the generated methods. */ - public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access); + public abstract void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access); - protected void doGenerateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + protected void doGenerateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { JavacTreeMaker maker = builderType.getTreeMaker(); - generateSingularMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent, access); - generatePluralMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent, access); - generateClearMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, access); + generateSingularMethod(cfv, deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent, access); + generatePluralMethod(cfv, deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent, access); + generateClearMethod(cfv, deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, access); } - private void finishAndInjectMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean deprecate, ListBuffer statements, Name methodName, List jcVariableDecls, AccessLevel access) { + private void finishAndInjectMethod(CheckerFrameworkVersion cfv, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean deprecate, ListBuffer statements, Name methodName, List jcVariableDecls, AccessLevel access) { if (returnStatement != null) statements.append(returnStatement); JCBlock body = maker.Block(0, statements.toList()); - JCModifiers mods = makeMods(maker, builderType, deprecate, access); + JCModifiers mods = makeMods(maker, cfv, builderType, deprecate, access); List typeParams = List.nil(); List thrown = List.nil(); JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, jcVariableDecls, thrown, body, null); @@ -260,25 +264,25 @@ public class JavacSingularsRecipes { injectMethod(builderType, method); } - private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, AccessLevel access) { + private void generateClearMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, AccessLevel access) { JCStatement clearStatement = generateClearStatements(maker, data, builderType); ListBuffer statements = new ListBuffer(); statements.add(clearStatement); Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString())); - finishAndInjectMethod(maker, returnType, returnStatement, data, builderType, source, deprecate, statements, methodName, List.nil(), access); + finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, methodName, List.nil(), access); } protected abstract JCStatement generateClearStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType); - private void generateSingularMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent, AccessLevel access) { + private void generateSingularMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent, AccessLevel access) { ListBuffer statements = generateSingularMethodStatements(maker, data, builderType, source); List params = generateSingularMethodParameters(maker, data, builderType, source); Name name = data.getSingularName(); if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName(), name.toString())); statements.prepend(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); - finishAndInjectMethod(maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, access); + finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, access); } protected JCVariableDecl generateSingularMethodParameter(int typeIndex, JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source, Name name) { @@ -300,7 +304,7 @@ public class JavacSingularsRecipes { protected abstract List generateSingularMethodParameters(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source); - private void generatePluralMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent, AccessLevel access) { + private void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent, AccessLevel access) { ListBuffer statements = generatePluralMethodStatements(maker, data, builderType, source); Name name = data.getPluralName(); if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName() + "All", name.toString())); @@ -309,7 +313,7 @@ public class JavacSingularsRecipes { long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext()); JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null); statements.prepend(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); - finishAndInjectMethod(maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, List.of(param), access); + finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, List.of(param), access); } protected ListBuffer generatePluralMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index 5b022206..546dc66e 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java @@ -29,6 +29,7 @@ import java.util.Collections; import lombok.AccessLevel; import lombok.core.GuavaTypeMap; import lombok.core.LombokImmutableList; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil; @@ -71,8 +72,8 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { - doGenerateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); + @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + doGenerateMethods(cfv, data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); } @Override diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java index 9d24f5d5..634a086d 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java @@ -27,6 +27,7 @@ import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Collections; import lombok.AccessLevel; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil; @@ -66,8 +67,8 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { - doGenerateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); + @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + doGenerateMethods(cfv, data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); } @Override diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index 3e498cac..8dc7ecff 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java @@ -28,6 +28,7 @@ import java.util.Arrays; import lombok.AccessLevel; import lombok.core.LombokImmutableList; +import lombok.core.configuration.CheckerFrameworkVersion; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil; @@ -97,8 +98,8 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { - doGenerateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); + @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + doGenerateMethods(cfv, data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); } @Override diff --git a/test/core/src/lombok/AbstractRunTests.java b/test/core/src/lombok/AbstractRunTests.java index fc05aea2..d223ae03 100644 --- a/test/core/src/lombok/AbstractRunTests.java +++ b/test/core/src/lombok/AbstractRunTests.java @@ -47,6 +47,7 @@ import lombok.core.configuration.ConfigurationKeysLoader; import lombok.core.configuration.ConfigurationResolver; import lombok.core.configuration.ConfigurationResolverFactory; import lombok.javac.CapturingDiagnosticListener.CompilerMessage; +import lombok.transform.TestLombokFilesIdempotent; public abstract class AbstractRunTests { private final File dumpActualFilesHere; @@ -74,6 +75,7 @@ public abstract class AbstractRunTests { if (expected.isIgnore()) return null; if (!expected.versionWithinLimit(params.getVersion())) return null; if (!expected.versionWithinLimit(version)) return null; + if (expected.isSkipIdempotent() && params instanceof TestLombokFilesIdempotent) return null; final LombokTestSource sourceDirectives_ = sourceDirectives; final AssertionError directiveFailure_ = directiveFailure; diff --git a/test/core/src/lombok/CompilerMessageMatcher.java b/test/core/src/lombok/CompilerMessageMatcher.java index 49c81b70..c00263f4 100644 --- a/test/core/src/lombok/CompilerMessageMatcher.java +++ b/test/core/src/lombok/CompilerMessageMatcher.java @@ -88,7 +88,7 @@ public class CompilerMessageMatcher { return out; } - private static final Pattern PATTERN = Pattern.compile("^(\\d+) (.*)$"); + private static final Pattern PATTERN = Pattern.compile("^(-?\\d+) (.*)$"); private static CompilerMessageMatcher read(String line) { line = line.trim(); diff --git a/test/core/src/lombok/DirectoryRunner.java b/test/core/src/lombok/DirectoryRunner.java index 9410b4c7..ffadba8f 100644 --- a/test/core/src/lombok/DirectoryRunner.java +++ b/test/core/src/lombok/DirectoryRunner.java @@ -36,7 +36,8 @@ import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; public class DirectoryRunner extends Runner { - private static final String DEBUG_FOCUS_ON_FILE = null; + /** Fill in a file name (or multiple, space separated) to reduce the testset to just the named file(s). */ + private static final String DEBUG_FOCUS_ON_FILE = ""; public enum Compiler { DELOMBOK { @@ -79,7 +80,7 @@ public class DirectoryRunner extends Runner { private static final FileFilter JAVA_FILE_FILTER = new FileFilter() { @Override public boolean accept(File file) { return file.isFile() && file.getName().endsWith(".java") && - (DEBUG_FOCUS_ON_FILE == null || file.getName().equals(DEBUG_FOCUS_ON_FILE)); + (DEBUG_FOCUS_ON_FILE.isEmpty() || (" " + DEBUG_FOCUS_ON_FILE + " ").contains(" " + file.getName() + " ")); } }; diff --git a/test/core/src/lombok/LombokTestSource.java b/test/core/src/lombok/LombokTestSource.java index 31f7db3e..ba0aa972 100644 --- a/test/core/src/lombok/LombokTestSource.java +++ b/test/core/src/lombok/LombokTestSource.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2015 The Project Lombok Authors. + * Copyright (C) 2014-2019 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 @@ -52,6 +52,7 @@ public class LombokTestSource { private final Map formatPreferences; private final boolean ignore; private final boolean skipCompareContent; + private final boolean skipIdempotent; private final boolean unchanged; private final int versionLowerLimit, versionUpperLimit; private final ConfigurationResolver configuration; @@ -92,6 +93,10 @@ public class LombokTestSource { return skipCompareContent; } + public boolean isSkipIdempotent() { + return skipIdempotent; + } + public String getSpecifiedEncoding() { return specifiedEncoding; } @@ -139,6 +144,7 @@ public class LombokTestSource { private static final Pattern IGNORE_PATTERN = Pattern.compile("^\\s*ignore\\s*(?:[-:].*)?$", Pattern.CASE_INSENSITIVE); private static final Pattern UNCHANGED_PATTERN = Pattern.compile("^\\s*unchanged\\s*(?:[-:].*)?$", Pattern.CASE_INSENSITIVE); private static final Pattern SKIP_COMPARE_CONTENT_PATTERN = Pattern.compile("^\\s*skip[- ]?compare[- ]?contents?\\s*(?:[-:].*)?$", Pattern.CASE_INSENSITIVE); + private static final Pattern SKIP_IDEMPOTENT_PATTERN = Pattern.compile("^\\s*skip[- ]?idempotent\\s*(?:[-:].*)?$", Pattern.CASE_INSENSITIVE); private LombokTestSource(File file, String content, List messages, List directives) { this.file = file; @@ -150,6 +156,7 @@ public class LombokTestSource { int versionUpper = Integer.MAX_VALUE; boolean ignore = false; boolean skipCompareContent = false; + boolean skipIdempotent = false; boolean unchanged = false; String encoding = null; Map formats = new HashMap(); @@ -173,6 +180,11 @@ public class LombokTestSource { continue; } + if (SKIP_IDEMPOTENT_PATTERN.matcher(directive).matches()) { + skipIdempotent = true; + continue; + } + if (lc.startsWith("platform ")) { String platformDesc = lc.substring("platform ".length()); int idx = platformDesc.indexOf(':'); @@ -223,6 +235,7 @@ public class LombokTestSource { this.versionUpperLimit = versionUpper; this.ignore = ignore; this.skipCompareContent = skipCompareContent; + this.skipIdempotent = skipIdempotent; this.unchanged = unchanged; this.platforms = platformLimit == null ? null : Arrays.asList(platformLimit); ConfigurationProblemReporter reporter = new ConfigurationProblemReporter() { diff --git a/test/transform/resource/after-delombok/CheckerFrameworkBasic.java b/test/transform/resource/after-delombok/CheckerFrameworkBasic.java new file mode 100644 index 00000000..b9d4e0fa --- /dev/null +++ b/test/transform/resource/after-delombok/CheckerFrameworkBasic.java @@ -0,0 +1,72 @@ +class CheckerFrameworkBasic { + private final int x; + private final int y; + private int z; + @org.checkerframework.common.aliasing.qual.Unique + @java.lang.SuppressWarnings("all") + public CheckerFrameworkBasic(final int x, final int y) { + this.x = x; + this.y = y; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + public int getX() { + return this.x; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + public int getY() { + return this.y; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + public int getZ() { + return this.z; + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public CheckerFrameworkBasic setZ(final int z) { + this.z = z; + return this; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.Override + @java.lang.SuppressWarnings("all") + public boolean equals(final java.lang.Object o) { + if (o == this) return true; + if (!(o instanceof CheckerFrameworkBasic)) return false; + final CheckerFrameworkBasic other = (CheckerFrameworkBasic) o; + if (!other.canEqual((java.lang.Object) this)) return false; + if (this.getX() != other.getX()) return false; + if (this.getY() != other.getY()) return false; + if (this.getZ() != other.getZ()) return false; + return true; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + protected boolean canEqual(final java.lang.Object other) { + return other instanceof CheckerFrameworkBasic; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.Override + @java.lang.SuppressWarnings("all") + public int hashCode() { + final int PRIME = 59; + int result = 1; + result = result * PRIME + this.getX(); + result = result * PRIME + this.getY(); + result = result * PRIME + this.getZ(); + return result; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "CheckerFrameworkBasic(x=" + this.getX() + ", y=" + this.getY() + ", z=" + this.getZ() + ")"; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + public CheckerFrameworkBasic withX(final int x) { + return this.x == x ? this : new CheckerFrameworkBasic(x, this.y, this.z); + } +} diff --git a/test/transform/resource/after-delombok/CheckerFrameworkBuilder.java b/test/transform/resource/after-delombok/CheckerFrameworkBuilder.java new file mode 100644 index 00000000..d21ad044 --- /dev/null +++ b/test/transform/resource/after-delombok/CheckerFrameworkBuilder.java @@ -0,0 +1,106 @@ +// skip-idempotent +import java.util.List; +class CheckerFrameworkBuilder { + int x; + int y; + int z; + List names; + @java.lang.SuppressWarnings("all") + private static int $default$x() { + return 5; + } + @org.checkerframework.common.aliasing.qual.Unique + @java.lang.SuppressWarnings("all") + CheckerFrameworkBuilder(final int x, final int y, final int z, final List names) { + this.x = x; + this.y = y; + this.z = z; + this.names = names; + } + @java.lang.SuppressWarnings("all") + public static class CheckerFrameworkBuilderBuilder { + @java.lang.SuppressWarnings("all") + private boolean x$set; + @java.lang.SuppressWarnings("all") + private int x$value; + @java.lang.SuppressWarnings("all") + private int y; + @java.lang.SuppressWarnings("all") + private int z; + @java.lang.SuppressWarnings("all") + private java.util.ArrayList names; + @org.checkerframework.common.aliasing.qual.Unique + @java.lang.SuppressWarnings("all") + CheckerFrameworkBuilderBuilder() { + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public CheckerFrameworkBuilderBuilder x(@org.checkerframework.checker.builder.qual.NotCalledMethods("x") CheckerFrameworkBuilderBuilder this, final int x) { + this.x$value = x; + x$set = true; + return this; + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public CheckerFrameworkBuilderBuilder y(@org.checkerframework.checker.builder.qual.NotCalledMethods("y") CheckerFrameworkBuilderBuilder this, final int y) { + this.y = y; + return this; + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public CheckerFrameworkBuilderBuilder z(@org.checkerframework.checker.builder.qual.NotCalledMethods("z") CheckerFrameworkBuilderBuilder this, final int z) { + this.z = z; + return this; + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public CheckerFrameworkBuilderBuilder name(final String name) { + if (this.names == null) this.names = new java.util.ArrayList(); + this.names.add(name); + return this; + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public CheckerFrameworkBuilderBuilder names(final java.util.Collection names) { + if (this.names == null) this.names = new java.util.ArrayList(); + this.names.addAll(names); + return this; + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public CheckerFrameworkBuilderBuilder clearNames() { + if (this.names != null) this.names.clear(); + return this; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + public CheckerFrameworkBuilder build(@org.checkerframework.checker.builder.qual.CalledMethods({"y", "z"}) CheckerFrameworkBuilderBuilder this) { + java.util.List names; + switch (this.names == null ? 0 : this.names.size()) { + case 0: + names = java.util.Collections.emptyList(); + break; + case 1: + names = java.util.Collections.singletonList(this.names.get(0)); + break; + default: + names = java.util.Collections.unmodifiableList(new java.util.ArrayList(this.names)); + } + int x$value = this.x$value; + if (!x$set) x$value = CheckerFrameworkBuilder.$default$x(); + return new CheckerFrameworkBuilder(x$value, y, z, names); + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "CheckerFrameworkBuilder.CheckerFrameworkBuilderBuilder(x$value=" + this.x$value + ", y=" + this.y + ", z=" + this.z + ", names=" + this.names + ")"; + } + } + @org.checkerframework.common.aliasing.qual.Unique + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + public static CheckerFrameworkBuilderBuilder builder() { + return new CheckerFrameworkBuilderBuilder(); + } +} diff --git a/test/transform/resource/after-delombok/CheckerFrameworkSuperBuilder.java b/test/transform/resource/after-delombok/CheckerFrameworkSuperBuilder.java new file mode 100644 index 00000000..40876369 --- /dev/null +++ b/test/transform/resource/after-delombok/CheckerFrameworkSuperBuilder.java @@ -0,0 +1,202 @@ +// skip-idempotent +import java.util.List; +class CheckerFrameworkSuperBuilder { + public static class Parent { + int x; + int y; + int z; + List names; + @java.lang.SuppressWarnings("all") + private static int $default$x() { + return 5; + } + @java.lang.SuppressWarnings("all") + public static abstract class ParentBuilder> { + @java.lang.SuppressWarnings("all") + private boolean x$set; + @java.lang.SuppressWarnings("all") + private int x$value; + @java.lang.SuppressWarnings("all") + private int y; + @java.lang.SuppressWarnings("all") + private int z; + @java.lang.SuppressWarnings("all") + private java.util.ArrayList names; + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + protected abstract B self(); + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + public abstract C build(@org.checkerframework.checker.builder.qual.CalledMethods({"y", "z"}) Parent this); + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public B x(@org.checkerframework.checker.builder.qual.NotCalledMethods("x") ParentBuilder this, final int x) { + this.x$value = x; + x$set = true; + return self(); + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public B y(@org.checkerframework.checker.builder.qual.NotCalledMethods("y") ParentBuilder this, final int y) { + this.y = y; + return self(); + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public B z(@org.checkerframework.checker.builder.qual.NotCalledMethods("z") ParentBuilder this, final int z) { + this.z = z; + return self(); + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public B name(final String name) { + if (this.names == null) this.names = new java.util.ArrayList(); + this.names.add(name); + return self(); + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public B names(final java.util.Collection names) { + if (this.names == null) this.names = new java.util.ArrayList(); + this.names.addAll(names); + return self(); + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public B clearNames() { + if (this.names != null) this.names.clear(); + return self(); + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "CheckerFrameworkSuperBuilder.Parent.ParentBuilder(x$value=" + this.x$value + ", y=" + this.y + ", z=" + this.z + ", names=" + this.names + ")"; + } + } + @java.lang.SuppressWarnings("all") + private static final class ParentBuilderImpl extends ParentBuilder { + @org.checkerframework.common.aliasing.qual.Unique + @java.lang.SuppressWarnings("all") + private ParentBuilderImpl() { + } + @java.lang.Override + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + protected ParentBuilderImpl self() { + return this; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.Override + @java.lang.SuppressWarnings("all") + public Parent build(@org.checkerframework.checker.builder.qual.CalledMethods({"y", "z"}) ParentBuilderImpl this) { + return new Parent(this); + } + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + protected Parent(final ParentBuilder b) { + if (b.x$set) this.x = b.x$value; + else this.x = Parent.$default$x(); + this.y = b.y; + this.z = b.z; + java.util.List names; + switch (b.names == null ? 0 : b.names.size()) { + case 0: + names = java.util.Collections.emptyList(); + break; + case 1: + names = java.util.Collections.singletonList(b.names.get(0)); + break; + default: + names = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.names)); + } + this.names = names; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + public static ParentBuilder builder() { + return new ParentBuilderImpl(); + } + } + public static class Child extends Parent { + int a; + int b; + @java.lang.SuppressWarnings("all") + private static int $default$a() { + return 1; + } + @java.lang.SuppressWarnings("all") + public static abstract class ChildBuilder> extends Parent.ParentBuilder { + @java.lang.SuppressWarnings("all") + private boolean a$set; + @java.lang.SuppressWarnings("all") + private int a$value; + @java.lang.SuppressWarnings("all") + private int b; + @java.lang.Override + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + protected abstract B self(); + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.Override + @java.lang.SuppressWarnings("all") + public abstract C build(@org.checkerframework.checker.builder.qual.CalledMethods("b") Child this); + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public B a(@org.checkerframework.checker.builder.qual.NotCalledMethods("a") ChildBuilder this, final int a) { + this.a$value = a; + a$set = true; + return self(); + } + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @java.lang.SuppressWarnings("all") + public B b(@org.checkerframework.checker.builder.qual.NotCalledMethods("b") ChildBuilder this, final int b) { + this.b = b; + return self(); + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "CheckerFrameworkSuperBuilder.Child.ChildBuilder(super=" + super.toString() + ", a$value=" + this.a$value + ", b=" + this.b + ")"; + } + } + @java.lang.SuppressWarnings("all") + private static final class ChildBuilderImpl extends ChildBuilder { + @org.checkerframework.common.aliasing.qual.Unique + @java.lang.SuppressWarnings("all") + private ChildBuilderImpl() { + } + @java.lang.Override + @org.checkerframework.checker.builder.qual.ReturnsReceiver + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + protected ChildBuilderImpl self() { + return this; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.Override + @java.lang.SuppressWarnings("all") + public Child build(@org.checkerframework.checker.builder.qual.CalledMethods("b") ChildBuilderImpl this) { + return new Child(this); + } + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + protected Child(final ChildBuilder b) { + super(b); + if (b.a$set) this.a = b.a$value; + else this.a = Child.$default$a(); + this.b = b.b; + } + @org.checkerframework.dataflow.qual.SideEffectFree + @java.lang.SuppressWarnings("all") + public static ChildBuilder builder() { + return new ChildBuilderImpl(); + } + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/CheckerFrameworkBasic.java b/test/transform/resource/after-ecj/CheckerFrameworkBasic.java new file mode 100644 index 00000000..03313c6d --- /dev/null +++ b/test/transform/resource/after-ecj/CheckerFrameworkBasic.java @@ -0,0 +1,59 @@ +import lombok.Data; +import lombok.experimental.Accessors; +import lombok.experimental.Wither; +@Data @Accessors(chain = true) class CheckerFrameworkBasic { + private final @Wither int x; + private final int y; + private int z; + public @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") CheckerFrameworkBasic withX(final int x) { + return ((this.x == x) ? this : new CheckerFrameworkBasic(x, this.y, this.z)); + } + public @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") int getX() { + return this.x; + } + public @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") int getY() { + return this.y; + } + public @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") int getZ() { + return this.z; + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") CheckerFrameworkBasic setZ(final int z) { + this.z = z; + return this; + } + public @java.lang.Override @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) { + if ((o == this)) + return true; + if ((! (o instanceof CheckerFrameworkBasic))) + return false; + final CheckerFrameworkBasic other = (CheckerFrameworkBasic) o; + if ((! other.canEqual((java.lang.Object) this))) + return false; + if ((this.getX() != other.getX())) + return false; + if ((this.getY() != other.getY())) + return false; + if ((this.getZ() != other.getZ())) + return false; + return true; + } + protected @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) { + return (other instanceof CheckerFrameworkBasic); + } + public @java.lang.Override @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") int hashCode() { + final int PRIME = 59; + int result = 1; + result = ((result * PRIME) + this.getX()); + result = ((result * PRIME) + this.getY()); + result = ((result * PRIME) + this.getZ()); + return result; + } + public @java.lang.Override @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((((("CheckerFrameworkBasic(x=" + this.getX()) + ", y=") + this.getY()) + ", z=") + this.getZ()) + ")"); + } + public @org.checkerframework.common.aliasing.qual.Unique @java.lang.SuppressWarnings("all") CheckerFrameworkBasic(final int x, final int y) { + super(); + this.x = x; + this.y = y; + } +} diff --git a/test/transform/resource/after-ecj/CheckerFrameworkBuilder.java b/test/transform/resource/after-ecj/CheckerFrameworkBuilder.java new file mode 100644 index 00000000..2baad9d9 --- /dev/null +++ b/test/transform/resource/after-ecj/CheckerFrameworkBuilder.java @@ -0,0 +1,79 @@ +import java.util.List; +import lombok.Builder; +import lombok.Singular; +@Builder class CheckerFrameworkBuilder { + public static @java.lang.SuppressWarnings("all") class CheckerFrameworkBuilderBuilder { + private @java.lang.SuppressWarnings("all") int x$value; + private @java.lang.SuppressWarnings("all") boolean x$set; + private @java.lang.SuppressWarnings("all") int y; + private @java.lang.SuppressWarnings("all") int z; + private @java.lang.SuppressWarnings("all") java.util.ArrayList names; + @org.checkerframework.common.aliasing.qual.Unique @java.lang.SuppressWarnings("all") CheckerFrameworkBuilderBuilder() { + super(); + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") CheckerFrameworkBuilderBuilder x(final @org.checkerframework.checker.builder.qual.NotCalledMethods("x") CheckerFrameworkBuilderBuilder this, final int x) { + this.x$value = x; + x$set = true; + return this; + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") CheckerFrameworkBuilderBuilder y(final @org.checkerframework.checker.builder.qual.NotCalledMethods("y") CheckerFrameworkBuilderBuilder this, final int y) { + this.y = y; + return this; + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") CheckerFrameworkBuilderBuilder z(final @org.checkerframework.checker.builder.qual.NotCalledMethods("z") CheckerFrameworkBuilderBuilder this, final int z) { + this.z = z; + return this; + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") CheckerFrameworkBuilderBuilder name(final String name) { + if ((this.names == null)) + this.names = new java.util.ArrayList(); + this.names.add(name); + return this; + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") CheckerFrameworkBuilderBuilder names(final java.util.Collection names) { + if ((this.names == null)) + this.names = new java.util.ArrayList(); + this.names.addAll(names); + return this; + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") CheckerFrameworkBuilderBuilder clearNames() { + if ((this.names != null)) + this.names.clear(); + return this; + } + public @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") CheckerFrameworkBuilder build(final @org.checkerframework.checker.builder.qual.CalledMethods({"y", "z"}) CheckerFrameworkBuilderBuilder this) { + java.util.List names; + switch (((this.names == null) ? 0 : this.names.size())) { + case 0 : + names = java.util.Collections.emptyList(); + break; + case 1 : + names = java.util.Collections.singletonList(this.names.get(0)); + break; + default : + names = java.util.Collections.unmodifiableList(new java.util.ArrayList(this.names)); + } + return new CheckerFrameworkBuilder((x$set ? x$value : CheckerFrameworkBuilder.$default$x()), y, z, names); + } + public @java.lang.Override @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((((((("CheckerFrameworkBuilder.CheckerFrameworkBuilderBuilder(x$value=" + this.x$value) + ", y=") + this.y) + ", z=") + this.z) + ", names=") + this.names) + ")"); + } + } + @Builder.Default int x; + int y; + int z; + @Singular List names; + private static @java.lang.SuppressWarnings("all") int $default$x() { + return 5; + } + @org.checkerframework.common.aliasing.qual.Unique @java.lang.SuppressWarnings("all") CheckerFrameworkBuilder(final int x, final int y, final int z, final List names) { + super(); + this.x = x; + this.y = y; + this.z = z; + this.names = names; + } + public static @org.checkerframework.common.aliasing.qual.Unique @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") CheckerFrameworkBuilderBuilder builder() { + return new CheckerFrameworkBuilderBuilder(); + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/CheckerFrameworkSuperBuilder.java b/test/transform/resource/after-ecj/CheckerFrameworkSuperBuilder.java new file mode 100644 index 00000000..3901bfce --- /dev/null +++ b/test/transform/resource/after-ecj/CheckerFrameworkSuperBuilder.java @@ -0,0 +1,142 @@ +import java.util.List; +import lombok.Singular; +class CheckerFrameworkSuperBuilder { + public static @lombok.experimental.SuperBuilder class Parent { + public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder> { + private @java.lang.SuppressWarnings("all") int x$value; + private @java.lang.SuppressWarnings("all") boolean x$set; + private @java.lang.SuppressWarnings("all") int y; + private @java.lang.SuppressWarnings("all") int z; + private @java.lang.SuppressWarnings("all") java.util.ArrayList names; + public ParentBuilder() { + } + protected abstract @org.checkerframework.checker.builder.qual.ReturnsReceiver @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") B self(); + public abstract @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") C build(final @org.checkerframework.checker.builder.qual.CalledMethods({"y", "z"}) ParentBuilder this); + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") B x(final @org.checkerframework.checker.builder.qual.NotCalledMethods("x") ParentBuilder this, final int x) { + this.x$value = x; + x$set = true; + return self(); + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") B y(final @org.checkerframework.checker.builder.qual.NotCalledMethods("y") ParentBuilder this, final int y) { + this.y = y; + return self(); + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") B z(final @org.checkerframework.checker.builder.qual.NotCalledMethods("z") ParentBuilder this, final int z) { + this.z = z; + return self(); + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") B name(final String name) { + if ((this.names == null)) + this.names = new java.util.ArrayList(); + this.names.add(name); + return self(); + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") B names(final java.util.Collection names) { + if ((this.names == null)) + this.names = new java.util.ArrayList(); + this.names.addAll(names); + return self(); + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") B clearNames() { + if ((this.names != null)) + this.names.clear(); + return self(); + } + public @java.lang.Override @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((((((("CheckerFrameworkSuperBuilder.Parent.ParentBuilder(x$value=" + this.x$value) + ", y=") + this.y) + ", z=") + this.z) + ", names=") + this.names) + ")"); + } + } + private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder { + private ParentBuilderImpl() { + } + protected @java.lang.Override @org.checkerframework.checker.builder.qual.ReturnsReceiver @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") ParentBuilderImpl self() { + return this; + } + public @java.lang.Override @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") Parent build(final @org.checkerframework.checker.builder.qual.CalledMethods({"y", "z"}) ParentBuilderImpl this) { + return new Parent(this); + } + } + @lombok.Builder.Default int x; + int y; + int z; + @Singular List names; + private static @java.lang.SuppressWarnings("all") int $default$x() { + return 5; + } + protected @org.checkerframework.common.aliasing.qual.Unique @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { + super(); + if (b.x$set) + this.x = b.x$value; + else + this.x = Parent.$default$x(); + this.y = b.y; + this.z = b.z; + java.util.List names; + switch (((b.names == null) ? 0 : b.names.size())) { + case 0 : + names = java.util.Collections.emptyList(); + break; + case 1 : + names = java.util.Collections.singletonList(b.names.get(0)); + break; + default : + names = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.names)); + } + this.names = names; + } + public static @org.checkerframework.common.aliasing.qual.Unique @java.lang.SuppressWarnings("all") ParentBuilder builder() { + return new ParentBuilderImpl(); + } + } + public static @lombok.experimental.SuperBuilder class Child extends Parent { + public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder> extends Parent.ParentBuilder { + private @java.lang.SuppressWarnings("all") int a$value; + private @java.lang.SuppressWarnings("all") boolean a$set; + private @java.lang.SuppressWarnings("all") int b; + public ChildBuilder() { + } + protected abstract @java.lang.Override @org.checkerframework.checker.builder.qual.ReturnsReceiver @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") B self(); + protected abstract @java.lang.Override @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") C build(final @org.checkerframework.checker.builder.qual.CalledMethods("b") ChildBuilder this); + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") B a(final @org.checkerframework.checker.builder.qual.NotCalledMethods("a") ChildBuilder this, final int a) { + this.a$value = a; + a$set = true; + return self(); + } + public @org.checkerframework.checker.builder.qual.ReturnsReceiver @java.lang.SuppressWarnings("all") B b(final @org.checkerframework.checker.builder.qual.NotCalledMethods("b") ChildBuilder this, final int b) { + this.b = b; + return self(); + } + public @java.lang.Override @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((((("CheckerFrameworkSuperBuilder.Child.ChildBuilder(super=" + super.toString()) + ", a$value=") + this.a$value) + ", b=") + this.b) + ")"); + } + } + private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder { + private ChildBuilderImpl() { + } + protected @java.lang.Override @org.checkerframework.checker.builder.qual.ReturnsReceiver @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") ChildBuilderImpl self() { + return this; + } + public @java.lang.Override @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") Child build(final @org.checkerframework.checker.builder.qual.CalledMethods("b") ChildBuilderImpl this) { + return new Child(this); + } + } + @lombok.Builder.Default int a; + int b; + private static @java.lang.SuppressWarnings("all") int $default$a() { + return 1; + } + protected @org.checkerframework.common.aliasing.qual.Unique @java.lang.SuppressWarnings("all") Child(final ChildBuilder b) { + super(b); + if (b.a$set) + this.a = b.a$value; + else + this.a = Child.$default$a(); + this.b = b.b; + } + public static @org.checkerframework.common.aliasing.qual.Unique @java.lang.SuppressWarnings("all") ChildBuilder builder() { + return new ChildBuilderImpl(); + } + } + CheckerFrameworkSuperBuilder() { + } +} diff --git a/test/transform/resource/before/CheckerFrameworkBasic.java b/test/transform/resource/before/CheckerFrameworkBasic.java new file mode 100644 index 00000000..5b59165a --- /dev/null +++ b/test/transform/resource/before/CheckerFrameworkBasic.java @@ -0,0 +1,11 @@ +//CONF: checkerframework = 3.0 +import lombok.Data; +import lombok.experimental.Accessors; +import lombok.experimental.Wither; + +@Data @Accessors(chain = true) +class CheckerFrameworkBasic { + @Wither private final int x; + private final int y; + private int z; +} diff --git a/test/transform/resource/before/CheckerFrameworkBuilder.java b/test/transform/resource/before/CheckerFrameworkBuilder.java new file mode 100644 index 00000000..4b501ab8 --- /dev/null +++ b/test/transform/resource/before/CheckerFrameworkBuilder.java @@ -0,0 +1,12 @@ +//CONF: checkerframework = true +import java.util.List; +import lombok.Builder; +import lombok.Singular; + +@Builder +class CheckerFrameworkBuilder { + @Builder.Default int x = 5; + int y; + int z; + @Singular List names; +} diff --git a/test/transform/resource/before/CheckerFrameworkSuperBuilder.java b/test/transform/resource/before/CheckerFrameworkSuperBuilder.java new file mode 100644 index 00000000..74d50ef1 --- /dev/null +++ b/test/transform/resource/before/CheckerFrameworkSuperBuilder.java @@ -0,0 +1,19 @@ +//CONF: checkerframework = true +import java.util.List; +import lombok.Singular; + +class CheckerFrameworkSuperBuilder { + @lombok.experimental.SuperBuilder + public static class Parent { + @lombok.Builder.Default int x = 5; + int y; + int z; + @Singular List names; + } + + @lombok.experimental.SuperBuilder + public static class Child extends Parent { + @lombok.Builder.Default int a = 1; + int b; + } +} diff --git a/test/transform/resource/messages-delombok/CheckerFrameworkBasic.java.messages b/test/transform/resource/messages-delombok/CheckerFrameworkBasic.java.messages new file mode 100644 index 00000000..9ee959a4 --- /dev/null +++ b/test/transform/resource/messages-delombok/CheckerFrameworkBasic.java.messages @@ -0,0 +1,4 @@ +6 package org.checkerframework.common.aliasing.qual does not exist +8 package org.checkerframework.dataflow.qual does not exist +9 package org.checkerframework.dataflow.qual does not exist +10 package org.checkerframework.dataflow.qual does not exist diff --git a/test/transform/resource/messages-delombok/CheckerFrameworkBuilder.java.messages b/test/transform/resource/messages-delombok/CheckerFrameworkBuilder.java.messages new file mode 100644 index 00000000..8c736705 --- /dev/null +++ b/test/transform/resource/messages-delombok/CheckerFrameworkBuilder.java.messages @@ -0,0 +1 @@ +6 package org.checkerframework.common.aliasing.qual does not exist diff --git a/test/transform/resource/messages-delombok/CheckerFrameworkSuperBuilder.java.messages b/test/transform/resource/messages-delombok/CheckerFrameworkSuperBuilder.java.messages new file mode 100644 index 00000000..5dd6251a --- /dev/null +++ b/test/transform/resource/messages-delombok/CheckerFrameworkSuperBuilder.java.messages @@ -0,0 +1,3 @@ +6 package org.checkerframework.dataflow.qual does not exist +-1 package org.checkerframework.checker.builder.qual does not exist +14 package org.checkerframework.dataflow.qual does not exist diff --git a/test/transform/resource/messages-ecj/CheckerFrameworkBasic.java.messages b/test/transform/resource/messages-ecj/CheckerFrameworkBasic.java.messages new file mode 100644 index 00000000..9bfcba0c --- /dev/null +++ b/test/transform/resource/messages-ecj/CheckerFrameworkBasic.java.messages @@ -0,0 +1 @@ +8 org.checkerframework cannot be resolved to a type diff --git a/test/transform/resource/messages-ecj/CheckerFrameworkBuilder.java.messages b/test/transform/resource/messages-ecj/CheckerFrameworkBuilder.java.messages new file mode 100644 index 00000000..d385a95c --- /dev/null +++ b/test/transform/resource/messages-ecj/CheckerFrameworkBuilder.java.messages @@ -0,0 +1 @@ +6 org.checkerframework cannot be resolved to a type diff --git a/test/transform/resource/messages-ecj/CheckerFrameworkSuperBuilder.java.messages b/test/transform/resource/messages-ecj/CheckerFrameworkSuperBuilder.java.messages new file mode 100644 index 00000000..d385a95c --- /dev/null +++ b/test/transform/resource/messages-ecj/CheckerFrameworkSuperBuilder.java.messages @@ -0,0 +1 @@ +6 org.checkerframework cannot be resolved to a type diff --git a/test/transform/resource/messages-idempotent/CheckerFrameworkBasic.java.messages b/test/transform/resource/messages-idempotent/CheckerFrameworkBasic.java.messages new file mode 100644 index 00000000..80694a06 --- /dev/null +++ b/test/transform/resource/messages-idempotent/CheckerFrameworkBasic.java.messages @@ -0,0 +1,10 @@ +5 package org.checkerframework.common.aliasing.qual does not exist +11 package org.checkerframework.dataflow.qual does not exist +16 package org.checkerframework.dataflow.qual does not exist +21 package org.checkerframework.dataflow.qual does not exist +26 package org.checkerframework.checker.builder.qual does not exist +32 package org.checkerframework.dataflow.qual does not exist +45 package org.checkerframework.dataflow.qual does not exist +50 package org.checkerframework.dataflow.qual does not exist +61 package org.checkerframework.dataflow.qual does not exist +67 package org.checkerframework.dataflow.qual does not exist -- cgit From ed3c8a67e2d757b0f15c5f2b53b7d6497f49db06 Mon Sep 17 00:00:00 2001 From: Roel Spilker Date: Mon, 26 Aug 2019 20:08:19 +0200 Subject: Fixes #2156: also clone the annotations on the setter. --- doc/changelog.markdown | 1 + src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java | 9 --------- src/core/lombok/eclipse/handlers/HandleSetter.java | 2 +- 3 files changed, 2 insertions(+), 10 deletions(-) (limited to 'doc/changelog.markdown') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 02ebe60c..c684a764 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -9,6 +9,7 @@ Lombok Changelog * ENHANCEMENT: If you mix up eclipse's non-null support, such as `@NonNullByDefault`, with lombok's `@NonNull`, you get a bunch of warnings about dead code that are inappropriate. These warnings are now suppressed, thanks to a contribution from Till Brychcy! [Pull Request #2155](https://github.com/rzwitserloot/lombok/pull/2155) * ENHANCEMENT: `@NonNull` can now also generate checks using jdk's `Objects.requireNonNull` or Guava's `Preconditions.checkNotNull`. [Issue #1197](https://github.com/rzwitserloot/lombok/issues/1197) * EXPERIMENT: Lombok is working together with [checkerframework](https://checkerframework.org/) to enable detection of improper builder use (such as forgetting to set a mandatory property prior to calling `build()`). This experiment can be turned on by adding `checkerframework = true` to your `lombok.config` file. +* BUGFIX: Using `@JsonProperty` or `@JsonValue` on a field in combination with `@Setter` or `@Data` would sometimes throw a ClassCastException during compilation. [Issue #2156](https://github.com/rzwitserloot/lombok/issues/2156) * BUGFIX: Delombok would turn something like `List...` in a method parameter to `List...` [Issue #2140](https://github.com/rzwitserloot/lombok/issues/2140) * BUGFIX: Javac would generate the wrong equals and hashCode if a type-use annotation was put on an array type field [Issue #2165](https://github.com/rzwitserloot/lombok/issues/2165) * BUGFIX: Eclipse 2019-06 + JDK-12 compatibility + an `@Singular` builder entry would produce a cascade of error dialogs. [Issue #2169](https://github.com/rzwitserloot/lombok/issues/2169) diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 782c6a29..6a6d4cca 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -675,15 +675,6 @@ public class EclipseHandlerUtil { return result == null ? null : result.toArray(new Annotation[0]); } - public static Annotation[] mergeAnnotations(Annotation[] a, Annotation[] b) { - if (a == null || a.length == 0) return (b == null || b.length == 0) ? null : b; - if (b == null || b.length == 0) return a.length == 0 ? null : a; - Annotation[] c = new Annotation[a.length + b.length]; - System.arraycopy(a, 0, c, 0, a.length); - System.arraycopy(b, 0, c, a.length, b.length); - return c; - } - public static boolean hasAnnotation(Class type, EclipseNode node) { if (node == null) return false; if (type == null) return false; diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index bfa60db0..8180c3fa 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -226,7 +226,7 @@ public class HandleSetter extends EclipseAnnotationHandler { if (isFieldDeprecated(fieldNode) || deprecate) { deprecated = new Annotation[] { generateDeprecatedAnnotation(source) }; } - method.annotations = mergeAnnotations(copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated), findCopyableToSetterAnnotations(fieldNode)); + method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated, findCopyableToSetterAnnotations(fieldNode)); Argument param = new Argument(paramName, p, copyType(field.type, source), Modifier.FINAL); param.sourceStart = pS; param.sourceEnd = pE; method.arguments = new Argument[] { param }; -- cgit From da5d9a12e72dc19888b9c1f69b34e73b2b25590c Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Mon, 26 Aug 2019 22:46:34 +0200 Subject: [With] updated documentation for Wither/With --- doc/changelog.markdown | 1 + website/extra/htaccess | 1 + website/templates/features/With.html | 53 +++++++++++++++++ .../templates/features/experimental/Wither.html | 66 ---------------------- website/usageExamples/WithExample_post.jpage | 21 +++++++ website/usageExamples/WithExample_pre.jpage | 14 +++++ .../experimental/WitherExample_post.jpage | 21 ------- .../experimental/WitherExample_pre.jpage | 14 ----- 8 files changed, 90 insertions(+), 101 deletions(-) create mode 100644 website/templates/features/With.html delete mode 100644 website/templates/features/experimental/Wither.html create mode 100644 website/usageExamples/WithExample_post.jpage create mode 100644 website/usageExamples/WithExample_pre.jpage delete mode 100644 website/usageExamples/experimental/WitherExample_post.jpage delete mode 100644 website/usageExamples/experimental/WitherExample_pre.jpage (limited to 'doc/changelog.markdown') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index c684a764..b36c4119 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -2,6 +2,7 @@ Lombok Changelog ---------------- ### v1.18.9 "Edgy Guinea Pig" +* PROMOTION: `@Wither` has been promoted to the main package, renamed to `@With`. Otherwise, no changes have been made to the annotation. The old experimenetal annotation will remain for a few versions as a deprecated annotation. If you had `lombok.config` configuration for this annotation, the configuration keys for this feature have been renamed. * FEATURE: You can now configure a custom logger framework using the new `@CustomLog` annotation in combination with the `lombok.log.custom.declaration` configuration key. See the [log documentation](https://projectlombok.org/features/Log) for more information. [Pullrequest #2086](https://github.com/rzwitserloot/lombok/pull/2086) with thanks to Adam Juraszek. * ENHANCEMENT: Thanks to Mark Haynes, the `staticConstructor` will now also be generated if a (private) constructor already exists. [Issue #2100](https://github.com/rzwitserloot/lombok/issues/2100) * ENHANCEMENT: `val` is now capable of decoding the type of convoluted expressions (particularly if the right hand side involves lambdas and conditional (ternary) expressions). [Pull Request #2109](https://github.com/rzwitserloot/lombok/pull/2109) and [Pull Request #2138](https://github.com/rzwitserloot/lombok/pull/2138) with thanks to Alexander Bulgakov. diff --git a/website/extra/htaccess b/website/extra/htaccess index 48df7772..3539950f 100644 --- a/website/extra/htaccess +++ b/website/extra/htaccess @@ -59,6 +59,7 @@ RewriteRule ^features/${pg?no_esc}(\.html)?/?$ /features/${pg?no_esc} [NC,R=301] RewriteRule ^features/experimental/Builder(\.html)?/?$ /features/Builder [NC,R=301] RewriteRule ^features/experimental/Value(\.html)?/?$ /features/Value [NC,R=301] RewriteRule ^features/experimental/var(\.html)?/?$ /features/var [NC,R=301] +RewriteRule ^features/experimental/Wither(\.html)?/?$ /features/With [NC,R=301] RewriteRule ^features/experimental/all$ /features/experimental/index.html [L,END] RewriteRule ^features/experimental(/all)?/?$ /features/experimental/all [NC,R=301] diff --git a/website/templates/features/With.html b/website/templates/features/With.html new file mode 100644 index 00000000..35c52dab --- /dev/null +++ b/website/templates/features/With.html @@ -0,0 +1,53 @@ +<#import "_features.html" as f> + +<@f.scaffold title="@With" logline="Immutable 'setters' - methods that create a clone but with one changed field."> + <@f.history> +

+ @Wither was introduced as experimental feature in lombok v0.11.4. +

+ @Wither was renamed to @With, and moved out of experimental and into the core package, in lombok v1.18.10. + + + <@f.overview> +

+ The next best alternative to a setter for an immutable property is to construct a clone of the object, but with a new value for this one field. A method to generate this clone is precisely what @With generates: a withFieldName(newValue) method which produces a clone except for the new value for the associated field. +

+ For example, if you create public class Point { private final int x, y; }, setters make no sense because the fields are final. @With can generate a withX(int newXValue) method for you which will return a new point with the supplied value for x and the same value for y. +

+ Like @Setter, you can specify an access level in case you want the generated with method to be something other than public:
@With(level = AccessLevel.PROTECTED). Also like @Setter, you can also put a @With annotation on a type, which means a with method is generated for each field (even non-final fields). +

+ To put annotations on the generated method, you can use onMethod=@__({@AnnotationsHere}). Be careful though! This is an experimental feature. For more details see the documentation on the onX feature. +

+ javadoc on the field will be copied to generated with methods. Normally, all text is copied, and @param is moved to the with method, whilst @return lines are stripped from the with method's javadoc. Moved means: Deleted from the field's javadoc. It is also possible to define unique text for the with method's javadoc. To do that, you create a 'section' named WITH. A section is a line in your javadoc containing 2 or more dashes, then the text 'WITH', followed by 2 or more dashes, and nothing else on the line. If you use sections, @return and @param stripping / copying for that section is no longer done (move the @param line into the section). +

+ If you have a hierarchical immutable data structure, the @WithBy feature might be more suitable than @With + + + <@f.snippets name="With" /> + + <@f.confKeys> +

+ lombok.with.flagUsage = [warning | error] (default: not set) +
+ Lombok will flag any usage of @With as a warning or error if configured. +
+ + + <@f.smallPrint> +

+ With methods cannot be generated for static fields because that makes no sense. +

+ With methods can be generated for abstract classes, but this generates an abstract method with the appropriate signature. +

+ When applying @With to a type, static fields and fields whose name start with a $ are skipped. +

+ For generating the method names, the first character of the field, if it is a lowercase character, is title-cased, otherwise, it is left unmodified. Then, with is prefixed. +

+ No method is generated if any method already exists with the same name (case insensitive) and same parameter count. For example, withX(int x) will not be generated if there's already a method withX(String... x) even though it is technically possible to make the method. This caveat exists to prevent confusion. If the generation of a method is skipped for this reason, a warning is emitted instead. Varargs count as 0 to N parameters. +

+ For boolean fields that start with is immediately followed by a title-case letter, nothing is prefixed to generate the wither name. +

+ Various well known annotations about nullity cause null checks to be inserted and will be copied to the parameter. See Getter/Setter documentation's small print for more information. +

+ + diff --git a/website/templates/features/experimental/Wither.html b/website/templates/features/experimental/Wither.html deleted file mode 100644 index 00dc10b1..00000000 --- a/website/templates/features/experimental/Wither.html +++ /dev/null @@ -1,66 +0,0 @@ -<#import "../_features.html" as f> - -<@f.scaffold title="@Wither" logline="Immutable 'setters' - methods that create a clone but with one changed field."> - <@f.history> -

- @Wither was introduced as experimental feature in lombok v0.11.4. -

- - - <@f.experimental> -
    -
  • - Still not sure that @Wither is an appropriate name for this feature. -
  • - Should there be an option to supply a way of cloning the input somehow? -
  • - Should the way that the clone is created by configurable? -
  • - Should we replace @Wither entirely with a builder class? -
  • -
- Current status: neutral - More feedback requires on the items in the above list before promotion to the main package is warranted. - - - <@f.overview> -

- The next best alternative to a setter for an immutable property is to construct a clone of the object, but with a new value for this one field. A method to generate this clone is precisely what @Wither generates: a withFieldName(newValue) method which produces a clone except for the new value for the associated field. -

- For example, if you create public class Point { private final int x, y; }, setters make no sense because the fields are final. @Wither can generate a withX(int newXValue) method for you which will return a new point with the supplied value for x and the same value for y. -

- Like @Setter, you can specify an access level in case you want the generated wither to be something other than public:
@Wither(level = AccessLevel.PROTECTED). Also like @Setter, you can also put a @Wither annotation on a type, which means a 'wither' is generated for each field (even non-final fields). -

- To put annotations on the generated method, you can use onMethod=@__({@AnnotationsHere}); to put annotations on the only parameter of a generated wither method, you can use onParam=@__({@AnnotationsHere}). Be careful though! This is an experimental feature. For more details see the documentation on the onX feature. -

- NEW in lombok v1.12.0: javadoc on the field will now be copied to generated withers. Normally, all text is copied, and @param is moved to the wither, whilst @return lines are stripped from the wither's javadoc. Moved means: Deleted from the field's javadoc. It is also possible to define unique text for the wither's javadoc. To do that, you create a 'section' named WITHER. A section is a line in your javadoc containing 2 or more dashes, then the text 'WITHER', followed by 2 or more dashes, and nothing else on the line. If you use sections, @return and @param stripping / copying for that section is no longer done (move the @param line into the section). -

- - - <@f.snippets name="experimental/Wither" /> - - <@f.confKeys> -
- lombok.wither.flagUsage = [warning | error] (default: not set) -
- Lombok will flag any usage of @Wither as a warning or error if configured. -
- - - <@f.smallPrint> -

- Withers cannot be generated for static fields because that makes no sense. -

- Withers can be generated for abstract classes, but this generates an abstract method with the appropriate signature. -

- When applying @Wither to a type, static fields and fields whose name start with a $ are skipped. -

- For generating the method names, the first character of the field, if it is a lowercase character, is title-cased, otherwise, it is left unmodified. Then, with is prefixed. -

- No method is generated if any method already exists with the same name (case insensitive) and same parameter count. For example, withX(int x) will not be generated if there's already a method withX(String... x) even though it is technically possible to make the method. This caveat exists to prevent confusion. If the generation of a method is skipped for this reason, a warning is emitted instead. Varargs count as 0 to N parameters. -

- For boolean fields that start with is immediately followed by a title-case letter, nothing is prefixed to generate the wither name. -

- Various well known annotations about nullity cause null checks to be inserted and will be copied to the parameter. See Getter/Setter documentation's small print for more information. -

- - diff --git a/website/usageExamples/WithExample_post.jpage b/website/usageExamples/WithExample_post.jpage new file mode 100644 index 00000000..a881ed8d --- /dev/null +++ b/website/usageExamples/WithExample_post.jpage @@ -0,0 +1,21 @@ +import lombok.NonNull; + +public class WithExample { + private final int age; + private @NonNull final String name; + + public WithExample(String name, int age) { + if (name == null) throw new NullPointerException(); + this.name = name; + this.age = age; + } + + public WithExample withAge(int age) { + return this.age == age ? this : new WithExample(name, age); + } + + protected WithExample withName(@NonNull String name) { + if (name == null) throw new java.lang.NullPointerException("name"); + return this.name == name ? this : new WithExample(name, age); + } +} diff --git a/website/usageExamples/WithExample_pre.jpage b/website/usageExamples/WithExample_pre.jpage new file mode 100644 index 00000000..3c76204e --- /dev/null +++ b/website/usageExamples/WithExample_pre.jpage @@ -0,0 +1,14 @@ +import lombok.AccessLevel; +import lombok.NonNull; +import lombok.With; + +public class WithExample { + @With private final int age; + @With(AccessLevel.PROTECTED) @NonNull private final String name; + + public WithExample(String name, int age) { + if (name == null) throw new NullPointerException(); + this.name = name; + this.age = age; + } +} diff --git a/website/usageExamples/experimental/WitherExample_post.jpage b/website/usageExamples/experimental/WitherExample_post.jpage deleted file mode 100644 index 3447192a..00000000 --- a/website/usageExamples/experimental/WitherExample_post.jpage +++ /dev/null @@ -1,21 +0,0 @@ -import lombok.NonNull; - -public class WitherExample { - private final int age; - private @NonNull final String name; - - public WitherExample(String name, int age) { - if (name == null) throw new NullPointerException(); - this.name = name; - this.age = age; - } - - public WitherExample withAge(int age) { - return this.age == age ? this : new WitherExample(name, age); - } - - protected WitherExample withName(@NonNull String name) { - if (name == null) throw new java.lang.NullPointerException("name"); - return this.name == name ? this : new WitherExample(name, age); - } -} \ No newline at end of file diff --git a/website/usageExamples/experimental/WitherExample_pre.jpage b/website/usageExamples/experimental/WitherExample_pre.jpage deleted file mode 100644 index 5db799fc..00000000 --- a/website/usageExamples/experimental/WitherExample_pre.jpage +++ /dev/null @@ -1,14 +0,0 @@ -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.experimental.Wither; - -public class WitherExample { - @Wither private final int age; - @Wither(AccessLevel.PROTECTED) @NonNull private final String name; - - public WitherExample(String name, int age) { - if (name == null) throw new NullPointerException(); - this.name = name; - this.age = age; - } -} -- cgit From 9a82f1c26bf8125aaba755f9f57a7ee6ef9b1f4a Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Tue, 27 Aug 2019 00:40:50 +0200 Subject: [trivial] typo --- doc/changelog.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'doc/changelog.markdown') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index b36c4119..5c09060f 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -2,7 +2,7 @@ Lombok Changelog ---------------- ### v1.18.9 "Edgy Guinea Pig" -* PROMOTION: `@Wither` has been promoted to the main package, renamed to `@With`. Otherwise, no changes have been made to the annotation. The old experimenetal annotation will remain for a few versions as a deprecated annotation. If you had `lombok.config` configuration for this annotation, the configuration keys for this feature have been renamed. +* PROMOTION: `@Wither` has been promoted to the main package, renamed to `@With`. Otherwise, no changes have been made to the annotation. The old experimental annotation will remain for a few versions as a deprecated annotation. If you had `lombok.config` configuration for this annotation, the configuration keys for this feature have been renamed. * FEATURE: You can now configure a custom logger framework using the new `@CustomLog` annotation in combination with the `lombok.log.custom.declaration` configuration key. See the [log documentation](https://projectlombok.org/features/Log) for more information. [Pullrequest #2086](https://github.com/rzwitserloot/lombok/pull/2086) with thanks to Adam Juraszek. * ENHANCEMENT: Thanks to Mark Haynes, the `staticConstructor` will now also be generated if a (private) constructor already exists. [Issue #2100](https://github.com/rzwitserloot/lombok/issues/2100) * ENHANCEMENT: `val` is now capable of decoding the type of convoluted expressions (particularly if the right hand side involves lambdas and conditional (ternary) expressions). [Pull Request #2109](https://github.com/rzwitserloot/lombok/pull/2109) and [Pull Request #2138](https://github.com/rzwitserloot/lombok/pull/2138) with thanks to Alexander Bulgakov. -- cgit From dbbb4c77ff3f8220c870617ad9b51b8a13cca28e Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Tue, 10 Sep 2019 23:04:33 +0200 Subject: [trivial] With PR 2138 reverted, lets not name it in the changelogs. --- doc/changelog.markdown | 2 +- .../about.txt | 35 ---------------------- .../about.txt | 35 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 36 deletions(-) delete mode 100644 test/manual/knownIssue-1976_2114-valPlusDelegateVsEclipseErrors/about.txt create mode 100644 test/manual/knownIssue-1976_2138-valPlusDelegateVsEclipseErrors/about.txt (limited to 'doc/changelog.markdown') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 5c09060f..0c90cb20 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -5,7 +5,7 @@ Lombok Changelog * PROMOTION: `@Wither` has been promoted to the main package, renamed to `@With`. Otherwise, no changes have been made to the annotation. The old experimental annotation will remain for a few versions as a deprecated annotation. If you had `lombok.config` configuration for this annotation, the configuration keys for this feature have been renamed. * FEATURE: You can now configure a custom logger framework using the new `@CustomLog` annotation in combination with the `lombok.log.custom.declaration` configuration key. See the [log documentation](https://projectlombok.org/features/Log) for more information. [Pullrequest #2086](https://github.com/rzwitserloot/lombok/pull/2086) with thanks to Adam Juraszek. * ENHANCEMENT: Thanks to Mark Haynes, the `staticConstructor` will now also be generated if a (private) constructor already exists. [Issue #2100](https://github.com/rzwitserloot/lombok/issues/2100) -* ENHANCEMENT: `val` is now capable of decoding the type of convoluted expressions (particularly if the right hand side involves lambdas and conditional (ternary) expressions). [Pull Request #2109](https://github.com/rzwitserloot/lombok/pull/2109) and [Pull Request #2138](https://github.com/rzwitserloot/lombok/pull/2138) with thanks to Alexander Bulgakov. +* ENHANCEMENT: `val` is now capable of decoding the type of convoluted expressions (particularly if the right hand side involves lambdas and conditional (ternary) expressions). [Pull Request #2109](https://github.com/rzwitserloot/lombok/pull/2109) with thanks to Alexander Bulgakov. * ENHANCEMENT: You can now configure the generated builder class name via the config system, using key `lombok.builder.className`. See the [Builder documentation](https://projectlombok.org/features/Builder) and [SuperBuilder documentation](https://projectlombok.org/features/experimental/SuperBuilder) * ENHANCEMENT: If you mix up eclipse's non-null support, such as `@NonNullByDefault`, with lombok's `@NonNull`, you get a bunch of warnings about dead code that are inappropriate. These warnings are now suppressed, thanks to a contribution from Till Brychcy! [Pull Request #2155](https://github.com/rzwitserloot/lombok/pull/2155) * ENHANCEMENT: `@NonNull` can now also generate checks using jdk's `Objects.requireNonNull` or Guava's `Preconditions.checkNotNull`. [Issue #1197](https://github.com/rzwitserloot/lombok/issues/1197) diff --git a/test/manual/knownIssue-1976_2114-valPlusDelegateVsEclipseErrors/about.txt b/test/manual/knownIssue-1976_2114-valPlusDelegateVsEclipseErrors/about.txt deleted file mode 100644 index 58b3df3b..00000000 --- a/test/manual/knownIssue-1976_2114-valPlusDelegateVsEclipseErrors/about.txt +++ /dev/null @@ -1,35 +0,0 @@ -PullRequest https://github.com/rzwitserloot/lombok/pull/2114 tries to fix issue https://github.com/rzwitserloot/lombok/issues/1976 but causes problems. - -The problem is that the fail-fast resolver applied by PR 2114 causes the following issue: - -Given: - -A.java: - - public class A { - interface Inner { - default void example() { - lombok.val v = doesNotExist(); - } - } - B b = new B(); - } - -B.java: - - import com.foo.pkg.A.Inner; - - public class B implements Inner { - void foo() { - lombok.val v = doesNotExist(); - } - } - -will cause A.java to be parsed such that nothing in it exists, in turn causing B to consider A.Inner to not exist at all, -even though the error on the 'implements Inner' line offers 'import Inner' as a quickfix. This quickfix won't, obviously, fix it. - -In addition, enough 'A refers to B refers to A' loops with errors like this and eclipse 2019-06 will eventually run out of heap and crash. - -The key commit that attempts to fix 1976 but causes the above issue is commit 089f2ec5f45567c8c12e9d13bf9be8fa5c107c18. - -The commit that undoes this, re-introducing 1976 but avoiding the problem above, is commit 877a169727a4c8078c43a4465929247c3390c897. diff --git a/test/manual/knownIssue-1976_2138-valPlusDelegateVsEclipseErrors/about.txt b/test/manual/knownIssue-1976_2138-valPlusDelegateVsEclipseErrors/about.txt new file mode 100644 index 00000000..58b3df3b --- /dev/null +++ b/test/manual/knownIssue-1976_2138-valPlusDelegateVsEclipseErrors/about.txt @@ -0,0 +1,35 @@ +PullRequest https://github.com/rzwitserloot/lombok/pull/2114 tries to fix issue https://github.com/rzwitserloot/lombok/issues/1976 but causes problems. + +The problem is that the fail-fast resolver applied by PR 2114 causes the following issue: + +Given: + +A.java: + + public class A { + interface Inner { + default void example() { + lombok.val v = doesNotExist(); + } + } + B b = new B(); + } + +B.java: + + import com.foo.pkg.A.Inner; + + public class B implements Inner { + void foo() { + lombok.val v = doesNotExist(); + } + } + +will cause A.java to be parsed such that nothing in it exists, in turn causing B to consider A.Inner to not exist at all, +even though the error on the 'implements Inner' line offers 'import Inner' as a quickfix. This quickfix won't, obviously, fix it. + +In addition, enough 'A refers to B refers to A' loops with errors like this and eclipse 2019-06 will eventually run out of heap and crash. + +The key commit that attempts to fix 1976 but causes the above issue is commit 089f2ec5f45567c8c12e9d13bf9be8fa5c107c18. + +The commit that undoes this, re-introducing 1976 but avoiding the problem above, is commit 877a169727a4c8078c43a4465929247c3390c897. -- cgit From 88d5d7d38d4069213cfc15fe0b3ce3479b4888eb Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Tue, 10 Sep 2019 23:58:25 +0200 Subject: pre-release version bump --- doc/changelog.markdown | 2 +- src/core/lombok/core/Version.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'doc/changelog.markdown') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 0c90cb20..e0b4bd80 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -1,7 +1,7 @@ Lombok Changelog ---------------- -### v1.18.9 "Edgy Guinea Pig" +### v1.18.10 (September 10th, 2019) * PROMOTION: `@Wither` has been promoted to the main package, renamed to `@With`. Otherwise, no changes have been made to the annotation. The old experimental annotation will remain for a few versions as a deprecated annotation. If you had `lombok.config` configuration for this annotation, the configuration keys for this feature have been renamed. * FEATURE: You can now configure a custom logger framework using the new `@CustomLog` annotation in combination with the `lombok.log.custom.declaration` configuration key. See the [log documentation](https://projectlombok.org/features/Log) for more information. [Pullrequest #2086](https://github.com/rzwitserloot/lombok/pull/2086) with thanks to Adam Juraszek. * ENHANCEMENT: Thanks to Mark Haynes, the `staticConstructor` will now also be generated if a (private) constructor already exists. [Issue #2100](https://github.com/rzwitserloot/lombok/issues/2100) diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java index 0a4f7e3d..f2ded6ce 100644 --- a/src/core/lombok/core/Version.java +++ b/src/core/lombok/core/Version.java @@ -30,9 +30,9 @@ public class Version { // ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries). // Note: In 'X.Y.Z', if Z is odd, its a snapshot build built from the repository, so many different 0.10.3 versions can exist, for example. // Official builds always end in an even number. (Since 0.10.2). - private static final String VERSION = "1.18.9"; - private static final String RELEASE_NAME = "Edgy Guinea Pig"; -// private static final String RELEASE_NAME = "Envious Ferret"; + private static final String VERSION = "1.18.10"; +// private static final String RELEASE_NAME = "Edgy Guinea Pig"; + private static final String RELEASE_NAME = "Envious Ferret"; // Named version history: // Angry Butterfly -- cgit From 8c32769f18361ed626ebd06962d924c288950d26 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 11 Sep 2019 00:36:07 +0200 Subject: post-release version bump --- doc/changelog.markdown | 3 +++ src/core/lombok/core/Version.java | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'doc/changelog.markdown') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index e0b4bd80..818ced1a 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -1,6 +1,9 @@ Lombok Changelog ---------------- +### v1.18.9 "Edgy Guinea Pig" +* No changes yet. + ### v1.18.10 (September 10th, 2019) * PROMOTION: `@Wither` has been promoted to the main package, renamed to `@With`. Otherwise, no changes have been made to the annotation. The old experimental annotation will remain for a few versions as a deprecated annotation. If you had `lombok.config` configuration for this annotation, the configuration keys for this feature have been renamed. * FEATURE: You can now configure a custom logger framework using the new `@CustomLog` annotation in combination with the `lombok.log.custom.declaration` configuration key. See the [log documentation](https://projectlombok.org/features/Log) for more information. [Pullrequest #2086](https://github.com/rzwitserloot/lombok/pull/2086) with thanks to Adam Juraszek. diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java index f2ded6ce..28c79ac2 100644 --- a/src/core/lombok/core/Version.java +++ b/src/core/lombok/core/Version.java @@ -30,9 +30,9 @@ public class Version { // ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries). // Note: In 'X.Y.Z', if Z is odd, its a snapshot build built from the repository, so many different 0.10.3 versions can exist, for example. // Official builds always end in an even number. (Since 0.10.2). - private static final String VERSION = "1.18.10"; -// private static final String RELEASE_NAME = "Edgy Guinea Pig"; - private static final String RELEASE_NAME = "Envious Ferret"; + private static final String VERSION = "1.18.11"; + private static final String RELEASE_NAME = "Edgy Guinea Pig"; +// private static final String RELEASE_NAME = "Envious Ferret"; // Named version history: // Angry Butterfly -- cgit