From 1f58c92e44b30aef9eac1b83753718a4c8fabbd0 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Fri, 19 Jul 2019 15:55:26 +0200 Subject: [trivial][website] adding tidelift link to the normalizer htaccess list --- website/extra/htaccess | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/extra/htaccess b/website/extra/htaccess index d2c7a292..e5cb28fd 100644 --- a/website/extra/htaccess +++ b/website/extra/htaccess @@ -71,3 +71,6 @@ RewriteRule ^features/experimental/${pg?no_esc}(\.html)?/?$ /features/experiment RewriteRule ^presentations/7lessons$ /presentations/7lessons.html [L,END] RewriteRule ^presentations/7lessons(\.html)?/?$ /presentations/7lessons [NC,R=301] RewriteRule ^presentations/7mistakes(\.html)?/?$ /presentations/7lessons [NC,R=301] + +RewriteRule ^tidelift$ /tidelift.html [L,END] +RewriteRule ^tidelift(\.html)?/?$ /tidelift [NC,R=301] -- cgit 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 + src/core/lombok/javac/apt/LombokProcessor.java | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) 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) diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java index 79db5dec..ed6cdb65 100644 --- a/src/core/lombok/javac/apt/LombokProcessor.java +++ b/src/core/lombok/javac/apt/LombokProcessor.java @@ -389,7 +389,12 @@ public class LombokProcessor extends AbstractProcessor { } private JCCompilationUnit toUnit(Element element) { - TreePath path = trees == null ? null : trees.getPath(element); + TreePath path = null; + if (trees != null) { + try { + path = trees.getPath(element); + } catch (NullPointerException ignore) {} + } if (path == null) return null; return (JCCompilationUnit) path.getCompilationUnit(); -- cgit From ff1c01d9e9e66d898c30939b497490b04fe6163c Mon Sep 17 00:00:00 2001 From: Roel Spilker Date: Thu, 25 Jul 2019 13:20:58 +0200 Subject: add comments --- src/core/lombok/javac/apt/LombokProcessor.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java index ed6cdb65..c32e09d5 100644 --- a/src/core/lombok/javac/apt/LombokProcessor.java +++ b/src/core/lombok/javac/apt/LombokProcessor.java @@ -393,7 +393,11 @@ public class LombokProcessor extends AbstractProcessor { if (trees != null) { try { path = trees.getPath(element); - } catch (NullPointerException ignore) {} + } catch (NullPointerException ignore) { + // Happens if a package-info.java dowsn't conatin a package declaration. + // https://github.com/rzwitserloot/lombok/issues/2184 + // We can safely ignore those, since they do not need any processing + } } if (path == null) return null; -- 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(-) 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 9773530fa9d17260c9c5ed7da256110cb84eb3ee Mon Sep 17 00:00:00 2001 From: Steve Mägi Date: Wed, 14 Aug 2019 15:42:48 +0300 Subject: fix loading extensions: Lombok extensions in external jar files were not included by class loader --- src/launch/lombok/launch/ShadowClassLoader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java index 5489f3ea..f2a21a4e 100644 --- a/src/launch/lombok/launch/ShadowClassLoader.java +++ b/src/launch/lombok/launch/ShadowClassLoader.java @@ -436,14 +436,14 @@ class ShadowClassLoader extends ClassLoader { Enumeration sec = super.getResources(name); while (sec.hasMoreElements()) { URL item = sec.nextElement(); - if (!partOfShadow(item.toString(), name)) vector.add(item); + if (isPartOfShadowSuffix(item.toString(), name, sclSuffix)) vector.add(item); } if (altName != null) { Enumeration tern = super.getResources(altName); while (tern.hasMoreElements()) { URL item = tern.nextElement(); - if (!partOfShadow(item.toString(), altName)) vector.add(item); + if (isPartOfShadowSuffix(item.toString(), altName, sclSuffix)) vector.add(item); } } -- 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 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)}; +