aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorReinier Zwitserloot <r.zwitserloot@projectlombok.org>2020-01-20 15:25:08 +0100
committerReinier Zwitserloot <r.zwitserloot@projectlombok.org>2020-01-28 16:21:39 +0100
commite95680a76733c22ee5937a586ee50c703d5ba621 (patch)
tree3eaefce07c41760468c3c2a17c86297e2304a730 /src
parentfa70b194aa7db62bdbc4cc759a606f97fe50fc92 (diff)
downloadlombok-e95680a76733c22ee5937a586ee50c703d5ba621.tar.gz
lombok-e95680a76733c22ee5937a586ee50c703d5ba621.tar.bz2
lombok-e95680a76733c22ee5937a586ee50c703d5ba621.zip
[issue #2221] [issue #788] Lombok now adds nullity annotations.
Which 'flavour' is defined in lombok.config; applied to toString, equals, canEqual, and plural-form of `@Singular`.
Diffstat (limited to 'src')
-rw-r--r--src/core/lombok/ConfigurationKeys.java27
-rw-r--r--src/core/lombok/core/configuration/NullAnnotationLibrary.java157
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java14
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java97
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java6
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleToString.java3
-rw-r--r--src/core/lombok/eclipse/handlers/HandleWith.java4
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java12
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java9
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java9
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java21
-rw-r--r--src/core/lombok/javac/handlers/HandleToString.java8
-rw-r--r--src/core/lombok/javac/handlers/HandleWith.java3
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java99
-rw-r--r--src/core/lombok/javac/handlers/JavacSingularsRecipes.java22
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java11
17 files changed, 450 insertions, 54 deletions
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java
index 691346cd..559fff92 100644
--- a/src/core/lombok/ConfigurationKeys.java
+++ b/src/core/lombok/ConfigurationKeys.java
@@ -30,6 +30,7 @@ import lombok.core.configuration.ConfigurationKey;
import lombok.core.configuration.FlagUsageType;
import lombok.core.configuration.IdentifierName;
import lombok.core.configuration.LogDeclaration;
+import lombok.core.configuration.NullAnnotationLibrary;
import lombok.core.configuration.NullCheckExceptionType;
import lombok.core.configuration.TypeName;
@@ -89,6 +90,32 @@ public class ConfigurationKeys {
*/
public static final ConfigurationKey<Boolean> ADD_FINDBUGS_SUPPRESSWARNINGS_ANNOTATIONS = new ConfigurationKey<Boolean>("lombok.extern.findbugs.addSuppressFBWarnings", "Generate @edu.umd.cs.findbugs.annotations.SuppressFBWarnings on all generated code (default: false).") {};
+ /**
+ * lombok configuration: {@code lombok.addNullAnnotations = one of: [{@code none}, {@code javax}, {@code eclipse}, {@code jetbrains}, {@code netbeans}, {@code androidx}, {@code android.support}, {@code checkerframework}, {@code findbugs}, {@code spring}, {@code JML}, or a custom set of fully qualified annotation types].
+ *
+ * Lombok generally copies relevant nullity annotations from your source code to the right places. However, sometimes lombok generates code where the nullability of some node is not dependent on something in your source code. You can configure lombok to add an appropriate nullity annotation in this case.<ul>
+ * <li>{@code none} (the default) - no annotations are added.</li>
+ * <li>{@code javax} - The annotations {@code javax.annotation.NonNull} and {@code javax.annotation.Nullable} are used.</li>
+ * <li>{@code eclipse} - The annotations {@code org.eclipse.jdt.annotation.NonNull} and {@code org.eclipse.jdt.annotation.Nullable} are used.</li>
+ * <li>{@code jetbrains} - The annotations {@code org.jetbrains.annotations.NotNull} and {@code org.jetbrains.annotations.Nullable} are used.</li>
+ * <li>{@code netbeans} - The annotations {@code org.netbeans.api.annotations.common.NonNull} and {@code org.netbeans.api.annotations.common.NullAllowed} are used.</li>
+ * <li>{@code androidx} - The annotations {@code androidx.annotation.NonNull} and {@code androidx.annotation.Nullable} are used.</li>
+ * <li>{@code android.support} - The annotations {@code android.support.annotation.NonNull} and {@code android.support.annotation.Nullable} are used.</li>
+ * <li>{@code checkerframework} - The annotations {@code org.checkerframework.checker.nullness.qual.NonNull} and {@code org.checkerframework.checker.nullness.qual.Nullable} are used.</li>
+ * <li>{@code findbugs} - The annotations {@code edu.umd.cs.findbugs.annotations.NonNull} and {@code edu.umd.cs.findbugs.annotations.Nullable} are used.</li>
+ * <li>{@code spring} - The annotations {@code org.springframework.lang.NonNull} and {@code org.springframework.lang.Nullable} are used.</li>
+ * <li>{@code jml} - The annotations {@code org.jmlspecs.annotation.NonNull} and {@code org.jmlspecs.annotation.Nullable} are used.</li>
+ * <li><code>CUSTOM:<em>fully.qualified.nonnull.annotation</em>:<em>fully.qualified.nullable.annotation</em></code> to configure your own types; the nullable annotation (and the colon) are optional.</li>
+ * </ul>
+ * <p>
+ * Lombok will not put these annotations on the classpath for you; your project must be set up such that these annotations are available.
+ * <p>
+ * Current features which use this configuration:<ul>
+ * <li>{@code @Builder.Singular} makes methods that accept a collection, all of which must be added. The parameter to this 'plural form' method is annotated.</li>
+ * </ul>
+ */
+ public static final ConfigurationKey<NullAnnotationLibrary> ADD_NULL_ANNOTATIONS = new ConfigurationKey<NullAnnotationLibrary>("lombok.addNullAnnotations", "Generate some style of null annotation for generated code where this is relevant. (default: none).") {};
+
// ----- *ArgsConstructor -----
/**
diff --git a/src/core/lombok/core/configuration/NullAnnotationLibrary.java b/src/core/lombok/core/configuration/NullAnnotationLibrary.java
new file mode 100644
index 00000000..68760e6f
--- /dev/null
+++ b/src/core/lombok/core/configuration/NullAnnotationLibrary.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 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.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public final class NullAnnotationLibrary implements ConfigurationValueType {
+ private final String key;
+ private final String nonNullAnnotation;
+ private final String nullableAnnotation;
+ private final boolean typeUse;
+
+ private NullAnnotationLibrary(String key, String nonNullAnnotation, String nullableAnnotation, boolean typeUse) {
+ this.key = key;
+ this.nonNullAnnotation = nonNullAnnotation;
+ this.nullableAnnotation = nullableAnnotation;
+ this.typeUse = typeUse;
+ }
+
+ /**
+ * Returns the fully qualified annotation name to apply to non-null elements. If {@code null} is returned, apply no annotation.
+ */
+ public String getNonNullAnnotation() {
+ return nonNullAnnotation;
+ }
+
+ /**
+ * Returns the fully qualified annotation name to apply to nullable elements. If {@code null} is returned, apply no annotation.
+ */
+ public String getNullableAnnotation() {
+ return nullableAnnotation;
+ }
+
+ /**
+ * If {@code true}, the annotation can only be used in TYPE_USE form, otherwise, prefer to annotate the parameter, not the type of the parameter (or the method, not the return type, etc).
+ */
+ public boolean isTypeUse() {
+ return typeUse;
+ }
+
+ public static final NullAnnotationLibrary NONE = new NullAnnotationLibrary("none", null, null, false);
+ public static final NullAnnotationLibrary JAVAX = new NullAnnotationLibrary("javax", "javax.annotation.Nonnull", "javax.annotation.Nullable", false);
+ public static final NullAnnotationLibrary ECLIPSE = new NullAnnotationLibrary("eclipse", "org.eclipse.jdt.annotation.NonNull", "org.eclipse.jdt.annotation.Nullable", true);
+ public static final NullAnnotationLibrary JETBRAINS = new NullAnnotationLibrary("jetbrains", "org.jetbrains.annotations.NotNull", "org.jetbrains.annotations.Nullable", false);
+ public static final NullAnnotationLibrary NETBEANS = new NullAnnotationLibrary("netbeans", "org.netbeans.api.annotations.common.NonNull", "org.netbeans.api.annotations.common.NullAllowed", false);
+ public static final NullAnnotationLibrary ANDROIDX = new NullAnnotationLibrary("androidx", "androidx.annotation.NonNull", "androidx.annotation.Nullable", false);
+ public static final NullAnnotationLibrary ANDROID_SUPPORT = new NullAnnotationLibrary("android.support", "android.support.annotation.NonNull", "android.support.annotation.Nullable", false);
+ public static final NullAnnotationLibrary CHECKERFRAMEWORK = new NullAnnotationLibrary("checkerframework", "org.checkerframework.checker.nullness.qual.NonNull", "org.checkerframework.checker.nullness.qual.Nullable", true);
+ public static final NullAnnotationLibrary FINDBUGS = new NullAnnotationLibrary("findbugs", "edu.umd.cs.findbugs.annotations.NonNull", "edu.umd.cs.findbugs.annotations.Nullable", false);
+ public static final NullAnnotationLibrary SPRING = new NullAnnotationLibrary("spring", "org.springframework.lang.NonNull", "org.springframework.lang.Nullable", false);
+ public static final NullAnnotationLibrary JML = new NullAnnotationLibrary("jml", "org.jmlspecs.annotation.NonNull", "org.jmlspecs.annotation.Nullable", false);
+
+ private static final List<NullAnnotationLibrary> ALL_AVAILABLE;
+ private static final String EXAMPLE_VALUE;
+
+ static {
+ ArrayList<NullAnnotationLibrary> out = new ArrayList<NullAnnotationLibrary>();
+ StringBuilder example = new StringBuilder();
+ for (Field f : NullAnnotationLibrary.class.getDeclaredFields()) {
+ if (f.getType() != NullAnnotationLibrary.class || !Modifier.isStatic(f.getModifiers()) || !Modifier.isPublic(f.getModifiers())) continue;
+ try {
+ NullAnnotationLibrary nal = (NullAnnotationLibrary) f.get(null);
+ out.add(nal);
+ example.append(nal.key).append(" | ");
+ } catch (IllegalAccessException e) {
+ continue;
+ }
+ }
+ out.trimToSize();
+ example.append("CUSTOM:com.foo.my.nonnull.annotation:com.foo.my.nullable.annotation");
+ ALL_AVAILABLE = Collections.unmodifiableList(out);
+ EXAMPLE_VALUE = example.toString();
+ }
+
+ public static NullAnnotationLibrary custom(String nonNullAnnotation, String nullableAnnotation, boolean typeUse) {
+ if (nonNullAnnotation == null && nullableAnnotation == null) return NONE;
+ String typeUseStr = typeUse ? "TYPE_USE:" : "";
+ if (nullableAnnotation == null) return new NullAnnotationLibrary("custom:" + typeUseStr + nonNullAnnotation, nonNullAnnotation, null, typeUse);
+ if (nonNullAnnotation == null) return new NullAnnotationLibrary("custom::" + typeUseStr + nullableAnnotation, null, nullableAnnotation, typeUse);
+ return new NullAnnotationLibrary("custom:" + typeUseStr + nonNullAnnotation + ":" + nullableAnnotation, nonNullAnnotation, nullableAnnotation, typeUse);
+ }
+
+ public static String description() {
+ return "nullity-annotation-library";
+ }
+
+ public static String exampleValue() {
+ return EXAMPLE_VALUE;
+ }
+
+ public static NullAnnotationLibrary valueOf(String in) {
+ String ci = in == null ? "" : in.toLowerCase();
+ if (ci.length() == 0) return NONE;
+ for (NullAnnotationLibrary nal : ALL_AVAILABLE) if (nal.key.equals(ci)) return nal;
+ if (!ci.startsWith("custom:")) {
+ StringBuilder out = new StringBuilder("Invalid null annotation library. Valid options: ");
+ for (NullAnnotationLibrary nal : ALL_AVAILABLE) out.append(nal.key).append(", ");
+ out.setLength(out.length() - 2);
+ out.append(" or CUSTOM:[TYPE_USE:]nonnull.annotation.type:nullable.annotation.type");
+ throw new IllegalArgumentException(out.toString());
+ }
+ boolean typeUse = ci.startsWith("custom:type_use:");
+ int start = typeUse ? 16 : 7;
+ int split = ci.indexOf(':', start);
+ if (split == -1) {
+ String nonNullAnnotation = ci.substring(start);
+ return custom(verifyTypeName(nonNullAnnotation), null, typeUse);
+ }
+ String nonNullAnnotation = ci.substring(start, split);
+ String nullableAnnotation = ci.substring(split + 1);
+ return custom(verifyTypeName(nonNullAnnotation), verifyTypeName(nullableAnnotation), typeUse);
+ }
+
+ private static final String MSG = "Not an identifier (provide a fully qualified type for custom: nullity annotations): ";
+ private static String verifyTypeName(String fqn) {
+ boolean atStart = true;
+ for (int i = 0; i < fqn.length(); i++) {
+ char c = fqn.charAt(i);
+ if (Character.isJavaIdentifierStart(c)) {
+ atStart = false;
+ continue;
+ }
+ if (atStart) throw new IllegalArgumentException(MSG + fqn);
+ if (c == '.') {
+ atStart = true;
+ continue;
+ }
+ if (Character.isJavaIdentifierPart(c)) continue;
+ throw new IllegalArgumentException(MSG + fqn);
+ }
+ if (atStart) throw new IllegalArgumentException(MSG + fqn);
+ return fqn;
+ }
+}
diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java
index 883b1a29..bfc7b690 100644
--- a/src/core/lombok/core/handlers/HandlerUtil.java
+++ b/src/core/lombok/core/handlers/HandlerUtil.java
@@ -79,7 +79,7 @@ public class HandlerUtil {
public static final List<String> NONNULL_ANNOTATIONS, BASE_COPYABLE_ANNOTATIONS, COPY_TO_SETTER_ANNOTATIONS;
static {
NONNULL_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
- "android.annotation.NonNull",
+ "androidx.annotation.NonNull",
"android.support.annotation.NonNull",
"com.sun.istack.internal.NotNull",
"edu.umd.cs.findbugs.annotations.NonNull",
@@ -95,6 +95,8 @@ public class HandlerUtil {
"org.springframework.lang.NonNull",
}));
BASE_COPYABLE_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
+ "androidx.annotation.NonNull",
+ "androidx.annotation.Nullable",
"android.support.annotation.NonNull",
"android.support.annotation.Nullable",
"edu.umd.cs.findbugs.annotations.NonNull",
@@ -104,6 +106,8 @@ public class HandlerUtil {
"javax.annotation.Nonnull",
"javax.annotation.Nullable",
"lombok.NonNull",
+ "org.jmlspecs.annotation.NonNull",
+ "org.jmlspecs.annotation.Nullable",
// To update Checker Framework annotations, run:
// grep --recursive --files-with-matches -e '^@Target\b.*TYPE_USE' $CHECKERFRAMEWORK/checker/src/main/java $CHECKERFRAMEWORK/framework/src/main/java | grep '\.java$' | sed 's/.*\/java\//\t\t\t"/' | sed 's/\.java$/",/' | sed 's/\//./g' | sort
// Only add new annotations, do not remove annotations that have been removed from the lastest version of the Checker Framework.
@@ -304,11 +308,13 @@ public class HandlerUtil {
"org.jetbrains.annotations.Nullable",
"org.springframework.lang.NonNull",
"org.springframework.lang.Nullable",
+ "org.netbeans.api.annotations.common.NonNull",
+ "org.netbeans.api.annotations.common.NullAllowed",
}));
COPY_TO_SETTER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
- "com.fasterxml.jackson.annotation.JsonProperty",
- "com.fasterxml.jackson.annotation.JsonSetter",
- }));
+ "com.fasterxml.jackson.annotation.JsonProperty",
+ "com.fasterxml.jackson.annotation.JsonSetter",
+ }));
}
/** Checks if the given name is a valid identifier.
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index aa48e000..bd0ad23b 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -43,6 +43,7 @@ import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
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.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
@@ -112,6 +113,7 @@ import lombok.core.AnnotationValues.AnnotationValue;
import lombok.core.LombokImmutableList;
import lombok.core.TypeResolver;
import lombok.core.configuration.CheckerFrameworkVersion;
+import lombok.core.configuration.NullAnnotationLibrary;
import lombok.core.configuration.NullCheckExceptionType;
import lombok.core.configuration.TypeName;
import lombok.core.debug.ProblemReporter;
@@ -2381,6 +2383,101 @@ public class EclipseHandlerUtil {
return p.equals("Object") || p.equals("java.lang.Object");
}
+ public static void createRelevantNullableAnnotation(EclipseNode typeNode, MethodDeclaration mth) {
+ NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
+ if (lib == null) return;
+
+ applyAnnotationToMethodDecl(typeNode, mth, lib.getNullableAnnotation(), lib.isTypeUse());
+ }
+
+ public static void createRelevantNonNullAnnotation(EclipseNode typeNode, MethodDeclaration mth) {
+ NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
+ if (lib == null) return;
+
+ applyAnnotationToMethodDecl(typeNode, mth, lib.getNonNullAnnotation(), lib.isTypeUse());
+ }
+
+ public static void createRelevantNullableAnnotation(EclipseNode typeNode, Argument arg) {
+ NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
+ if (lib == null) return;
+
+ applyAnnotationToVarDecl(typeNode, arg, lib.getNullableAnnotation(), lib.isTypeUse());
+ }
+
+ public static void createRelevantNonNullAnnotation(EclipseNode typeNode, Argument arg) {
+ NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
+ if (lib == null) return;
+
+ applyAnnotationToVarDecl(typeNode, arg, lib.getNonNullAnnotation(), lib.isTypeUse());
+ }
+
+ private static void applyAnnotationToMethodDecl(EclipseNode typeNode, MethodDeclaration mth, String annType, boolean typeUse) {
+ if (annType == null) return;
+
+ int partCount = 1;
+ for (int i = 0; i < annType.length(); i++) if (annType.charAt(i) == '.') partCount++;
+ long[] ps = new long[partCount];
+ Arrays.fill(ps, 0L);
+ Annotation ann = new MarkerAnnotation(new QualifiedTypeReference(Eclipse.fromQualifiedName(annType), ps), 0);
+
+ if (!typeUse || mth.returnType == null || mth.returnType.getTypeName().length < 2) {
+ Annotation[] a = mth.annotations;
+ if (a == null) a = new Annotation[1];
+ else {
+ Annotation[] b = new Annotation[a.length + 1];
+ System.arraycopy(a, 0, b, 0, a.length);
+ a = b;
+ }
+ a[a.length - 1] = ann;
+ mth.annotations = a;
+ } else {
+ int len = mth.returnType.getTypeName().length;
+ if (mth.returnType.annotations == null) mth.returnType.annotations = new Annotation[len][];
+ Annotation[] a = mth.returnType.annotations[len - 1];
+ if (a == null) a = new Annotation[1];
+ else {
+ Annotation[] b = new Annotation[a.length + 1];
+ System.arraycopy(a, 0, b, 1, a.length);
+ a = b;
+ }
+ a[0] = ann;
+ mth.returnType.annotations[len - 1] = a;
+ }
+ }
+ private static void applyAnnotationToVarDecl(EclipseNode typeNode, Argument arg, String annType, boolean typeUse) {
+ if (annType == null) return;
+
+ int partCount = 1;
+ for (int i = 0; i < annType.length(); i++) if (annType.charAt(i) == '.') partCount++;
+ long[] ps = new long[partCount];
+ Arrays.fill(ps, 0L);
+ Annotation ann = new MarkerAnnotation(new QualifiedTypeReference(Eclipse.fromQualifiedName(annType), ps), 0);
+
+ if (!typeUse || arg.type.getTypeName().length < 2) {
+ Annotation[] a = arg.annotations;
+ if (a == null) a = new Annotation[1];
+ else {
+ Annotation[] b = new Annotation[a.length + 1];
+ System.arraycopy(a, 0, b, 0, a.length);
+ a = b;
+ }
+ a[a.length - 1] = ann;
+ arg.annotations = a;
+ } else {
+ int len = arg.type.getTypeName().length;
+ if (arg.type.annotations == null) arg.type.annotations = new Annotation[len][];
+ Annotation[] a = arg.type.annotations[len - 1];
+ if (a == null) a = new Annotation[1];
+ else {
+ Annotation[] b = new Annotation[a.length + 1];
+ System.arraycopy(a, 0, b, 1, a.length);
+ a = b;
+ }
+ a[0] = ann;
+ arg.type.annotations[len - 1] = a;
+ }
+ }
+
public static NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
index 5fe4b958..cbbd4cc8 100755
--- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
+++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
@@ -35,6 +35,7 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
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.Block;
import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
@@ -437,7 +438,7 @@ public class EclipseSingularsRecipes {
}
}
- protected void nullBehaviorize(SingularData data, List<Statement> statements) {
+ protected void nullBehaviorize(EclipseNode typeNode, SingularData data, List<Statement> statements, Argument arg) {
NullCollectionBehavior behavior = data.getNullCollectionBehavior();
if (behavior == NullCollectionBehavior.IGNORE) {
@@ -446,9 +447,12 @@ public class EclipseSingularsRecipes {
b.statements = statements.toArray(new Statement[statements.size()]);
statements.clear();
statements.add(new IfStatement(isNotNull, b, 0, 0));
+ EclipseHandlerUtil.createRelevantNullableAnnotation(typeNode, arg);
return;
}
+ EclipseHandlerUtil.createRelevantNonNullAnnotation(typeNode, arg);
+
String exceptionTypeStr = behavior.getExceptionType();
StringLiteral message = new StringLiteral(behavior.toExceptionMessage(new String(data.getPluralName())).toCharArray(), 0, 0, 0);
if (exceptionTypeStr != null) {
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index 46474b07..959b2cae 100755
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -557,6 +557,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
method.arguments = new Argument[] {new Argument(new char[] { 'o' }, 0, objectRef, Modifier.FINAL)};
method.arguments[0].sourceStart = pS; method.arguments[0].sourceEnd = pE;
if (!onParam.isEmpty()) method.arguments[0].annotations = onParam.toArray(new Annotation[0]);
+ EclipseHandlerUtil.createRelevantNullableAnnotation(type, method.arguments[0]);
setGeneratedBy(method.arguments[0], source);
List<Statement> statements = new ArrayList<Statement>();
@@ -806,6 +807,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
method.arguments = new Argument[] {new Argument(otherName, 0, objectRef, Modifier.FINAL)};
method.arguments[0].sourceStart = pS; method.arguments[0].sourceEnd = pE;
if (!onParam.isEmpty()) method.arguments[0].annotations = onParam.toArray(new Annotation[0]);
+ EclipseHandlerUtil.createRelevantNullableAnnotation(type, method.arguments[0]);
setGeneratedBy(method.arguments[0], source);
SingleNameReference otherRef = new SingleNameReference(otherName, p);
diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java
index 39fd5937..a6bcb24f 100644
--- a/src/core/lombok/eclipse/handlers/HandleToString.java
+++ b/src/core/lombok/eclipse/handlers/HandleToString.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2019 The Project Lombok Authors.
+ * Copyright (C) 2009-2020 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
@@ -315,6 +315,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
method.statements = new Statement[] { returnStatement };
+ EclipseHandlerUtil.createRelevantNonNullAnnotation(type, method);
return method;
}
diff --git a/src/core/lombok/eclipse/handlers/HandleWith.java b/src/core/lombok/eclipse/handlers/HandleWith.java
index 8c8c3712..4771818d 100644
--- a/src/core/lombok/eclipse/handlers/HandleWith.java
+++ b/src/core/lombok/eclipse/handlers/HandleWith.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2019 The Project Lombok Authors.
+ * Copyright (C) 2012-2020 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
@@ -287,6 +287,8 @@ public class HandleWith extends EclipseAnnotationHandler<With> {
}
param.annotations = copyAnnotations(source, copyableAnnotations, onParam.toArray(new Annotation[0]));
+ EclipseHandlerUtil.createRelevantNonNullAnnotation(fieldNode, method);
+
method.traverse(new SetGeneratedByVisitor(source), parent.scope);
return method;
}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
index b067ad80..5d656c91 100755
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
@@ -196,16 +196,18 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
thisDotFieldDotAddAll.selector = (getAddMethodName() + "All").toCharArray();
statements.add(thisDotFieldDotAddAll);
- nullBehaviorize(data, statements);
+ TypeReference paramType;
+ paramType = new QualifiedTypeReference(fromQualifiedName(getAddAllTypeName()), NULL_POSS);
+ paramType = addTypeArgs(getTypeArgumentsCount(), true, builderType, paramType, data.getTypeArgs());
+ Argument param = new Argument(data.getPluralName(), 0, paramType, ClassFileConstants.AccFinal);
+
+ nullBehaviorize(builderType, data, statements, param);
if (returnStatement != null) statements.add(returnStatement);
md.statements = statements.toArray(new Statement[0]);
- TypeReference paramType;
- paramType = new QualifiedTypeReference(fromQualifiedName(getAddAllTypeName()), NULL_POSS);
- paramType = addTypeArgs(getTypeArgumentsCount(), true, builderType, paramType, data.getTypeArgs());
- Argument param = new Argument(data.getPluralName(), 0, paramType, ClassFileConstants.AccFinal);
+
md.arguments = new Argument[] {param};
md.returnType = returnType;
char[] prefixedSelector = data.getSetterPrefix().length == 0 ? data.getPluralName() : HandlerUtil.buildAccessorName(new String(data.getSetterPrefix()), new String(data.getPluralName())).toCharArray();
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
index ba447397..024f5880 100755
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
@@ -174,14 +174,15 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
thisDotFieldDotAddAll.selector = "addAll".toCharArray();
statements.add(thisDotFieldDotAddAll);
- nullBehaviorize(data, statements);
+ TypeReference paramType = new QualifiedTypeReference(TypeConstants.JAVA_UTIL_COLLECTION, NULL_POSS);
+ paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs());
+ Argument param = new Argument(data.getPluralName(), 0, paramType, ClassFileConstants.AccFinal);
+
+ nullBehaviorize(builderType, data, statements, param);
if (returnStatement != null) statements.add(returnStatement);
md.statements = statements.toArray(new Statement[0]);
- TypeReference paramType = new QualifiedTypeReference(TypeConstants.JAVA_UTIL_COLLECTION, NULL_POSS);
- paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs());
- Argument param = new Argument(data.getPluralName(), 0, paramType, ClassFileConstants.AccFinal);
md.arguments = new Argument[] {param};
md.returnType = returnType;
char[] prefixedSelector = data.getSetterPrefix().length == 0 ? data.getPluralName() : HandlerUtil.buildAccessorName(new String(data.getSetterPrefix()), new String(data.getPluralName())).toCharArray();
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
index e91c6616..843fd073 100755
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
@@ -306,15 +306,16 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
forEach.action = forEachContent;
statements.add(forEach);
- nullBehaviorize(data, statements);
+ TypeReference paramType = new QualifiedTypeReference(JAVA_UTIL_MAP, NULL_POSS);
+ paramType = addTypeArgs(2, true, builderType, paramType, data.getTypeArgs());
+ Argument param = new Argument(data.getPluralName(), 0, paramType, ClassFileConstants.AccFinal);
+
+ nullBehaviorize(builderType, data, statements, param);
if (returnStatement != null) statements.add(returnStatement);
md.statements = statements.toArray(new Statement[0]);
- TypeReference paramType = new QualifiedTypeReference(JAVA_UTIL_MAP, NULL_POSS);
- paramType = addTypeArgs(2, true, builderType, paramType, data.getTypeArgs());
- Argument param = new Argument(data.getPluralName(), 0, paramType, ClassFileConstants.AccFinal);
md.arguments = new Argument[] {param};
md.returnType = returnType;
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index 0d0369b9..c4cf28da 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -383,18 +383,6 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
List<JCAnnotation> annsOnParamOnMethod = List.nil();
- String nearest = scanForNearestAnnotation(typeNode, "org.eclipse.jdt.annotation.NonNullByDefault");
- if (nearest != null) {
- JCAnnotation m = maker.Annotation(genTypeRef(typeNode, "org.eclipse.jdt.annotation.Nullable"), List.<JCExpression>nil());
- annsOnParamOnMethod = annsOnParamOnMethod.prepend(m);
- }
-
- nearest = scanForNearestAnnotation(typeNode, "javax.annotation.ParametersAreNullableByDefault", "javax.annotation.ParametersAreNonnullByDefault");
- if ("javax.annotation.ParametersAreNonnullByDefault".equals(nearest)) {
- JCAnnotation m = maker.Annotation(genTypeRef(typeNode, "javax.annotation.Nullable"), List.<JCExpression>nil());
- annsOnParamOnMethod = annsOnParamOnMethod.prepend(m);
- }
-
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil());
List<JCAnnotation> annsOnMethod = List.of(overrideAnnotation);
CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode);
@@ -415,7 +403,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext());
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
- final List<JCVariableDecl> params = List.of(maker.VarDef(maker.Modifiers(finalFlag | Flags.PARAMETER, onParam), oName, objectType, null));
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(finalFlag | Flags.PARAMETER, onParam), oName, objectType, null);
+ JavacHandlerUtil.createRelevantNullableAnnotation(typeNode, param);
+
+ final List<JCVariableDecl> params = List.of(param);
/* if (o == this) return true; */ {
statements.append(maker.If(maker.Binary(CTC_EQUAL, maker.Ident(oName),
@@ -538,7 +529,9 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCExpression objectType = genJavaLangTypeRef(typeNode, "Object");
Name otherName = typeNode.toName("other");
long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
- List<JCVariableDecl> params = List.of(maker.VarDef(maker.Modifiers(flags, onParam), otherName, objectType, null));
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, onParam), otherName, objectType, null);
+ createRelevantNullableAnnotation(typeNode, param);
+ List<JCVariableDecl> params = List.of(param);
JCBlock body = maker.Block(0, List.<JCStatement>of(
maker.Return(maker.TypeTest(maker.Ident(otherName), createTypeReference(typeNode, false)))));
diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java
index d0d36e06..0a950f7c 100644
--- a/src/core/lombok/javac/handlers/HandleToString.java
+++ b/src/core/lombok/javac/handlers/HandleToString.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2019 The Project Lombok Authors.
+ * Copyright (C) 2009-2020 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
@@ -253,8 +253,10 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
JCBlock body = maker.Block(0, List.of(returnStatement));
- return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("toString"), returnType,
- List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext());
+ JCMethodDecl methodDef = maker.MethodDef(mods, typeNode.toName("toString"), returnType,
+ List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ createRelevantNonNullAnnotation(typeNode, methodDef);
+ return recursiveSetGeneratedBy(methodDef, source, typeNode.getContext());
}
public static String getTypeName(JavacNode typeNode) {
diff --git a/src/core/lombok/javac/handlers/HandleWith.java b/src/core/lombok/javac/handlers/HandleWith.java
index 7b2417da..6977b10e 100644
--- a/src/core/lombok/javac/handlers/HandleWith.java
+++ b/src/core/lombok/javac/handlers/HandleWith.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2019 The Project Lombok Authors.
+ * Copyright (C) 2012-2020 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
@@ -211,6 +211,7 @@ public class HandleWith extends JavacAnnotationHandler<With> {
long access = toJavacModifier(level);
JCMethodDecl createdWith = createWith(access, fieldNode, fieldNode.getTreeMaker(), source, onMethod, onParam, makeAbstract);
+ createRelevantNonNullAnnotation(fieldNode, createdWith);
ClassSymbol sym = ((JCClassDecl) fieldNode.up().get()).sym;
Type returnType = sym == null ? null : sym.type;
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 9359b1ae..0ef8c359 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -94,6 +94,7 @@ import lombok.core.CleanupTask;
import lombok.core.LombokImmutableList;
import lombok.core.TypeResolver;
import lombok.core.configuration.CheckerFrameworkVersion;
+import lombok.core.configuration.NullAnnotationLibrary;
import lombok.core.configuration.NullCheckExceptionType;
import lombok.core.configuration.TypeName;
import lombok.core.handlers.HandlerUtil;
@@ -1085,6 +1086,13 @@ public class JavacHandlerUtil {
}
}
+ static void setAnnotations(JCTree obj, List<JCAnnotation> anns) {
+ init(obj.getClass());
+ try {
+ ANNOTATIONS.set(obj, anns);
+ } catch (Exception e) {}
+ }
+
static JCExpression getUnderlyingType(JCTree obj) {
init(obj.getClass());
try {
@@ -2054,4 +2062,95 @@ public class JavacHandlerUtil {
String p = extending.toString();
return p.equals("Object") || p.equals("java.lang.Object");
}
+
+ public static void createRelevantNullableAnnotation(JavacNode typeNode, JCMethodDecl mth) {
+ NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
+ if (lib == null) return;
+ applyAnnotationToMethodDecl(typeNode, mth, lib.getNullableAnnotation(), lib.isTypeUse());
+ }
+
+ public static void createRelevantNonNullAnnotation(JavacNode typeNode, JCMethodDecl mth) {
+ NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
+ if (lib == null) return;
+ applyAnnotationToMethodDecl(typeNode, mth, lib.getNonNullAnnotation(), lib.isTypeUse());
+ }
+
+ public static void createRelevantNonNullAnnotation(JavacNode typeNode, JCVariableDecl arg) {
+ NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
+ if (lib == null) return;
+
+ applyAnnotationToVarDecl(typeNode, arg, lib.getNonNullAnnotation(), lib.isTypeUse());
+ }
+
+ public static void createRelevantNullableAnnotation(JavacNode typeNode, JCVariableDecl arg) {
+ NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
+ if (lib == null) return;
+
+ applyAnnotationToVarDecl(typeNode, arg, lib.getNullableAnnotation(), lib.isTypeUse());
+ }
+
+ private static void applyAnnotationToMethodDecl(JavacNode typeNode, JCMethodDecl mth, String annType, boolean typeUse) {
+ if (annType == null) return;
+ JavacTreeMaker maker = typeNode.getTreeMaker();
+
+ JCAnnotation m = maker.Annotation(genTypeRef(typeNode, annType), List.<JCExpression>nil());
+ if (typeUse) {
+ JCExpression resType = mth.restype;
+ if (resType instanceof JCTypeApply) {
+ JCTypeApply ta = (JCTypeApply) resType;
+ resType = ta.clazz;
+ }
+
+ if (resType instanceof JCFieldAccess || resType instanceof JCArrayTypeTree) {
+ mth.restype = maker.AnnotatedType(List.of(m), resType);
+ return;
+ }
+
+ if (JCAnnotatedTypeReflect.is(resType)) {
+ List<JCAnnotation> annotations = JCAnnotatedTypeReflect.getAnnotations(resType);
+ JCAnnotatedTypeReflect.setAnnotations(resType, annotations.prepend(m));
+ return;
+ }
+
+ if (resType instanceof JCPrimitiveTypeTree || resType instanceof JCIdent) {
+ mth.mods.annotations = mth.mods.annotations == null ? List.of(m) : mth.mods.annotations.prepend(m);
+ }
+ } else {
+ mth.mods.annotations = mth.mods.annotations == null ? List.of(m) : mth.mods.annotations.prepend(m);
+ }
+ }
+
+ private static void applyAnnotationToVarDecl(JavacNode typeNode, JCVariableDecl arg, String annType, boolean typeUse) {
+ if (annType == null) return;
+ JavacTreeMaker maker = typeNode.getTreeMaker();
+
+ JCAnnotation m = maker.Annotation(genTypeRef(typeNode, annType), List.<JCExpression>nil());
+ if (typeUse) {
+ JCExpression varType = arg.vartype;
+ JCTypeApply ta = null;
+ if (varType instanceof JCTypeApply) {
+ ta = (JCTypeApply) varType;
+ varType = ta.clazz;
+ }
+
+ if (varType instanceof JCFieldAccess || varType instanceof JCArrayTypeTree) {
+ varType = maker.AnnotatedType(List.of(m), varType);
+ if (ta != null) ta.clazz = varType;
+ else arg.vartype = varType;
+ return;
+ }
+
+ if (JCAnnotatedTypeReflect.is(varType)) {
+ List<JCAnnotation> annotations = JCAnnotatedTypeReflect.getAnnotations(varType);
+ JCAnnotatedTypeReflect.setAnnotations(varType, annotations.prepend(m));
+ return;
+ }
+
+ if (varType instanceof JCPrimitiveTypeTree || varType instanceof JCIdent) {
+ arg.mods.annotations = arg.mods.annotations == null ? List.of(m) : arg.mods.annotations.prepend(m);
+ }
+ } else {
+ arg.mods.annotations = arg.mods.annotations == null ? List.of(m) : arg.mods.annotations.prepend(m);
+ }
+ }
}
diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
index a5d4a295..9dab3da5 100644
--- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
+++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
@@ -273,12 +273,19 @@ public class JavacSingularsRecipes {
generateClearMethod(cfv, deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, access);
}
- private void finishAndInjectMethod(CheckerFrameworkVersion cfv, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean deprecate, ListBuffer<JCStatement> statements, Name methodName, List<JCVariableDecl> jcVariableDecls, AccessLevel access) {
+ private void finishAndInjectMethod(CheckerFrameworkVersion cfv, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean deprecate, ListBuffer<JCStatement> statements, Name methodName, List<JCVariableDecl> jcVariableDecls, AccessLevel access, NullCollectionBehavior nullBehavior) {
if (returnStatement != null) statements.append(returnStatement);
JCBlock body = maker.Block(0, statements.toList());
JCModifiers mods = makeMods(maker, cfv, builderType, deprecate, access);
List<JCTypeParameter> typeParams = List.nil();
List<JCExpression> thrown = List.nil();
+
+ if (nullBehavior == NullCollectionBehavior.IGNORE) {
+ for (JCVariableDecl d : jcVariableDecls) createRelevantNullableAnnotation(builderType, d);
+ } else if (nullBehavior != null) {
+ for (JCVariableDecl d : jcVariableDecls) createRelevantNonNullAnnotation(builderType, d);
+ }
+
JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, jcVariableDecls, thrown, body, null);
recursiveSetGeneratedBy(method, source, builderType.getContext());
injectMethod(builderType, method);
@@ -290,7 +297,7 @@ public class JavacSingularsRecipes {
statements.add(clearStatement);
Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString()));
- finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, methodName, List.<JCVariableDecl>nil(), access);
+ finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, methodName, List.<JCVariableDecl>nil(), access, null);
}
protected abstract JCStatement generateClearStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType);
@@ -304,7 +311,7 @@ public class JavacSingularsRecipes {
if (!setterPrefix.isEmpty()) name = builderType.toName(HandlerUtil.buildAccessorName(setterPrefix, name.toString()));
statements.prepend(createConstructBuilderVarIfNeeded(maker, data, builderType, source));
- finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, access);
+ finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, access, null);
}
protected JCVariableDecl generateSingularMethodParameter(int typeIndex, JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source, Name name) {
@@ -336,10 +343,11 @@ public class JavacSingularsRecipes {
JCExpression paramType = getPluralMethodParamType(builderType);
paramType = addTypeArgs(getTypeArgumentsCount(), true, builderType, paramType, data.getTypeArgs(), source);
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));
-
NullCollectionBehavior behavior = data.getNullCollectionBehavior();
+ if (behavior == null) behavior = NullCollectionBehavior.IGNORE;
+ JCModifiers paramMods = maker.Modifiers(paramFlags);
+ JCVariableDecl param = maker.VarDef(paramMods, data.getPluralName(), paramType, null);
+ statements.prepend(createConstructBuilderVarIfNeeded(maker, data, builderType, source));
if (behavior == NullCollectionBehavior.IGNORE) {
JCExpression incomingIsNotNull = maker.Binary(CTC_NOT_EQUAL, maker.Ident(data.getPluralName()), maker.Literal(CTC_BOT, null));
@@ -361,7 +369,7 @@ public class JavacSingularsRecipes {
}
}
- finishAndInjectMethod(cfv, 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, behavior);
}
protected ListBuffer<JCStatement> 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 546dc66e..7cd676c0 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015-2018 The Project Lombok Authors.
+ * Copyright (C) 2015-2020 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
@@ -121,14 +121,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer {
@Override
protected JCExpression getPluralMethodParamType(JavacNode builderType) {
- JCExpression paramType;
- String aaTypeName = getAddAllTypeName();
- if (aaTypeName.startsWith("java.lang.") && aaTypeName.indexOf('.', 11) == -1) {
- paramType = genJavaLangTypeRef(builderType, aaTypeName.substring(10));
- } else {
- paramType = chainDotsString(builderType, aaTypeName);
- }
- return paramType;
+ return genTypeRef(builderType, getAddAllTypeName());
}
@Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) {