Skip to content

Commit

Permalink
[issue #788] Add more nullity annotations where relevant
Browse files Browse the repository at this point in the history
 (chainable setters, static constructors, builder stuff)
  • Loading branch information
rzwitserloot committed Jan 31, 2020
1 parent c42bfba commit 3f0fec1
Show file tree
Hide file tree
Showing 24 changed files with 112 additions and 41 deletions.
2 changes: 1 addition & 1 deletion doc/changelog.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Lombok Changelog
* FEATURE: In [`lombok.config`](https://projectlombok.org/features/configuration) it is possible to import other config files, even from a `.zip` or `.jar`.
* FEATURE: You can now configure a builder's 'setter' prefixes via `@Builder(setterPrefix = "set")` for example. We discourage doing this, but if some library you use requires them, have at it. [Pull Request #2174](https://github.com/rzwitserloot/lombok/pull/2174], [Issue #1805](https://github.com/rzwitserloot/lombok/issues/1805).
* FEATURE: If you use `@Builder`'s `@Singular`, a plural form is also generated, which has the effect of adding all elements in the passed collection. If you pass a null reference, this would result in a message-less `NullPointerException`. Now, it results in that exception but with a useful message attached (uses the same config as `@NonNull`), or alternatively via a parameter on `@Singular`, you can choose to ignore such a call (add nothing, return immediately); this can be useful when deserializing (e.g. Jackson JSON) and JPA/Hibernate code. [Issue #2221](https://github.com/rzwitserloot/lombok/issues/2221]. [singular documentation](https://projectlombok.org/features/Builder).
* FEATURE: Tired of being unable to use `@javax.annotation.ParametersAreNonnullByDefault` or `@org.eclipse.jdt.annotation.NonNullByDefault` because then the equals method that lombok generates isn't valid? Fret no more; lombok can now add nullity annotations where relevant. Set the flavour of nullity annotation you prefer in your `lombok.config`. Applies to the return value of `toString` and `withX` methods, and the parameter of `equals`, `canEqual`, and the plural form of `@Singular` marked fields for builder classes. [Issue #788](https://github.com/rzwitserloot/lombok/issues/788)
* FEATURE: Tired of being unable to use `@javax.annotation.ParametersAreNonnullByDefault` or `@org.eclipse.jdt.annotation.NonNullByDefault` because then the equals method that lombok generates isn't valid? Fret no more; lombok can now add nullity annotations where relevant. Set the flavour of nullity annotation you prefer in your `lombok.config`. Applies to the return value of `toString`, `withX`, chainable `setX`, static constructors, `build`, `builder`, etcetera, and the parameter of `equals`, `canEqual`, and the plural form of `@Singular` marked fields for builder classes. [Issue #788](https://github.com/rzwitserloot/lombok/issues/788)
* BUGFIX: `lombok.experimental.Wither` has been deprecated (it has been renamed to `lombok.With`). However, the intent is that lombok still handles the old annotation in case you haven't updated your lombok dep yet. However, only a star import on `lombok.experimental.*` worked; an explicit one would cause lombok to not generate any with method. [Issue #2235](https://github.com/rzwitserloot/lombok/issues/2235)
* BUGFIX: Referring to an inner class inside the generics on a class marked with `@SuperBuilder` would cause the error `wrong number of type arguments; required 3` [Issue #2262](https://github.com/rzwitserloot/lombok/issues/2262); fixed by github user [`@Lekanich`](https://github.com/rzwitserloot/lombok/issues/2262) - thank you!
* BUGFIX: Some of the code generated by `@Builder` did not include `this.` prefixes when accessing fields. While semantically it didn't matter, if you use the 'add this prefix for field accesses' save action in eclipse, the save action would break. [Issue #2327](https://github.com/rzwitserloot/lombok/issues/2327)
Expand Down
3 changes: 3 additions & 0 deletions src/core/lombok/eclipse/handlers/HandleBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ private MethodDeclaration generateToBuilderMethod(CheckerFrameworkVersion cfv, b
out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)};
}

createRelevantNonNullAnnotation(type, out);
out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
return out;
}
Expand Down Expand Up @@ -796,6 +797,7 @@ public MethodDeclaration generateBuildMethod(CheckerFrameworkVersion cfv, Eclips
out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE)};
}
out.arguments = generateBuildArgs(cfv, type, builderFields, source);
if (staticName == null) createRelevantNonNullAnnotation(type, out);
out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
return out;
}
Expand Down Expand Up @@ -864,6 +866,7 @@ public MethodDeclaration generateBuilderMethod(CheckerFrameworkVersion cfv, bool
} else if (sefAnn != null) {
out.annotations = new Annotation[] {sefAnn};
}
createRelevantNonNullAnnotation(type, out);
out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
return out;
}
Expand Down
1 change: 1 addition & 0 deletions src/core/lombok/eclipse/handlers/HandleConstructor.java
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ public MethodDeclaration createStaticConstructor(AccessLevel level, String name,
constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[0]);
constructor.statements = new Statement[] { new ReturnStatement(statement, (int) (p >> 32), (int)p) };

createRelevantNonNullAnnotation(type, constructor);
constructor.traverse(new SetGeneratedByVisitor(source), typeDecl.scope);
return constructor;
}
Expand Down
2 changes: 2 additions & 0 deletions src/core/lombok/eclipse/handlers/HandleSetter.java
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate,
method.statements = statements.toArray(new Statement[0]);
param.annotations = copyAnnotations(source, copyableAnnotations, onParam.toArray(new Annotation[0]));

if (returnType != null && returnStatement != null) createRelevantNonNullAnnotation(sourceNode, method);

method.traverse(new SetGeneratedByVisitor(source), parent.scope);
return method;
}
Expand Down
5 changes: 4 additions & 1 deletion src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ private MethodDeclaration generateBuilderMethod(CheckerFrameworkVersion cfv, Str

if (cfv.generateUnique()) out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)};

createRelevantNonNullAnnotation(type, out);
out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
return out;
}
Expand Down Expand Up @@ -633,6 +634,7 @@ private MethodDeclaration generateToBuilderMethod(CheckerFrameworkVersion cfv, S

if (cfv.generateUnique()) out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)};

createRelevantNonNullAnnotation(type, out);
out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
return out;
}
Expand Down Expand Up @@ -850,8 +852,9 @@ private MethodDeclaration generateBuildMethod(CheckerFrameworkVersion cfv, Eclip
allocationStatement.arguments = new Expression[] {new ThisReference(0, 0)};
statements.add(new ReturnStatement(allocationStatement, 0, 0));
out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[0]);
out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
out.arguments = HandleBuilder.generateBuildArgs(cfv, builderType, builderFields, source);
createRelevantNonNullAnnotation(builderType, out);
out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
return out;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ void generateClearMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeRef
md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource());

data.setGeneratedByRecursive(md);
if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
injectMethod(builderType, md);
}

Expand Down Expand Up @@ -176,6 +177,7 @@ void generateSingularMethod(CheckerFrameworkVersion cfv, boolean deprecate, Type
md.selector = fluent ? prefixedSingularName : HandlerUtil.buildAccessorName("add", new String(data.getSingularName())).toCharArray();
md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource());

if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
data.setGeneratedByRecursive(md);
HandleNonNull.INSTANCE.fix(injectMethod(builderType, md));
}
Expand Down Expand Up @@ -207,13 +209,13 @@ void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeRe

md.statements = statements.toArray(new Statement[0]);


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();
md.selector = fluent ? prefixedSelector : HandlerUtil.buildAccessorName("addAll", new String(data.getPluralName())).toCharArray();
md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource());

if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ private void generateClearMethod(CheckerFrameworkVersion cfv, boolean deprecate,
md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource());

data.setGeneratedByRecursive(md);
if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
injectMethod(builderType, md);
}

Expand Down Expand Up @@ -154,6 +155,7 @@ void generateSingularMethod(CheckerFrameworkVersion cfv, boolean deprecate, Type
md.selector = fluent ? prefixedSingularName : HandlerUtil.buildAccessorName("add", new String(data.getSingularName())).toCharArray();
md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource());

if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
data.setGeneratedByRecursive(md);
HandleNonNull.INSTANCE.fix(injectMethod(builderType, md));
}
Expand Down Expand Up @@ -189,6 +191,7 @@ void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, TypeRe
md.selector = fluent ? prefixedSelector : HandlerUtil.buildAccessorName("addAll", new String(data.getPluralName())).toCharArray();
md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource());

if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ private void generateClearMethod(CheckerFrameworkVersion cfv, boolean deprecate,
md.returnType = returnType;
md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource());

if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
Expand Down Expand Up @@ -253,6 +254,7 @@ private void generateSingularMethod(CheckerFrameworkVersion cfv, boolean depreca
md.selector = setterName.toCharArray();
md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource());

if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
data.setGeneratedByRecursive(md);
HandleNonNull.INSTANCE.fix(injectMethod(builderType, md));
}
Expand Down Expand Up @@ -326,6 +328,7 @@ private void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate
md.selector = setterName.toCharArray();
md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource());

if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
Expand Down
12 changes: 9 additions & 3 deletions src/core/lombok/javac/handlers/HandleBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,9 @@ private JCMethodDecl generateToBuilderMethod(CheckerFrameworkVersion cfv, String
}
JCBlock body = maker.Block(0, statements.toList());
List<JCAnnotation> annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__UNIQUE), List.<JCExpression>nil())) : List.<JCAnnotation>nil();
return maker.MethodDef(maker.Modifiers(toJavacModifier(access), annsOnMethod), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), !isStatic, typeParams), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(toJavacModifier(access), annsOnMethod), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), !isStatic, typeParams), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
createRelevantNonNullAnnotation(type, methodDef);
return methodDef;
}

private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
Expand Down Expand Up @@ -714,7 +716,9 @@ private JCMethodDecl generateBuildMethod(CheckerFrameworkVersion cfv, JavacNode

List<JCAnnotation> annsOnMethod = cfv.generateSideEffectFree() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil();
List<JCVariableDecl> params = generateBuildArgs(cfv, type, builderFields);
return maker.MethodDef(maker.Modifiers(toJavacModifier(access), annsOnMethod), type.toName(buildName), returnType, List.<JCTypeParameter>nil(), params, thrownExceptions, body, null);
JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(toJavacModifier(access), annsOnMethod), type.toName(buildName), returnType, List.<JCTypeParameter>nil(), params, thrownExceptions, body, null);
if (builderName == null) createRelevantNonNullAnnotation(type, methodDef);
return methodDef;
}

public static JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode, List<JCTypeParameter> params) {
Expand Down Expand Up @@ -757,7 +761,9 @@ public JCMethodDecl generateBuilderMethod(CheckerFrameworkVersion cfv, boolean i
else if (annUnique != null) annsOnMethod = List.of(annUnique);
else if (annSef != null) annsOnMethod = List.of(annSef);
else annsOnMethod = List.nil();
return maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), !isStatic, typeParams), copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), !isStatic, typeParams), copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
createRelevantNonNullAnnotation(type, methodDef);
return methodDef;
}

public void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) {
Expand Down
4 changes: 3 additions & 1 deletion src/core/lombok/javac/handlers/HandleConstructor.java
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,8 @@ public JCMethodDecl createStaticConstructor(String name, AccessLevel level, Java
JCReturn returnStatement = maker.Return(maker.NewClass(null, List.<JCExpression>nil(), constructorType, args.toList(), null));
JCBlock body = maker.Block(0, List.<JCStatement>of(returnStatement));

return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName(name), returnType, typeParams.toList(), params.toList(), List.<JCExpression>nil(), body, null), source, typeNode.getContext());
JCMethodDecl methodDef = maker.MethodDef(mods, typeNode.toName(name), returnType, typeParams.toList(), params.toList(), List.<JCExpression>nil(), body, null);
createRelevantNonNullAnnotation(typeNode, methodDef);
return recursiveSetGeneratedBy(methodDef, source, typeNode.getContext());
}
}
6 changes: 4 additions & 2 deletions src/core/lombok/javac/handlers/HandleSetter.java
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,10 @@ public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNod
annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.<JCExpression>nil()));
}

JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType,
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source.get(), field.getContext());
JCMethodDecl methodDef = treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType,
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue);
if (returnStatement != null) createRelevantNonNullAnnotation(source, methodDef);
JCMethodDecl decl = recursiveSetGeneratedBy(methodDef, source.get(), field.getContext());
copyJavadoc(field, decl, CopyJavadoc.SETTER, returnStatement != null);
return decl;
}
Expand Down
12 changes: 9 additions & 3 deletions src/core/lombok/javac/handlers/HandleSuperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,9 @@ private JCMethodDecl generateBuilderMethod(CheckerFrameworkVersion cfv, String b
JCTypeApply returnType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), false, List.<JCTypeParameter>nil()), typeParameterNames.toList());

List<JCAnnotation> annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil();
return maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
createRelevantNonNullAnnotation(type, methodDef);
return methodDef;
}

/**
Expand Down Expand Up @@ -604,7 +606,9 @@ private JCMethodDecl generateToBuilderMethod(CheckerFrameworkVersion cfv, String
JCTypeApply returnType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), false, List.<JCTypeParameter>nil()), typeParameterNames.toList());

List<JCAnnotation> annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil();
return maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(TO_BUILDER_METHOD_NAME), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(TO_BUILDER_METHOD_NAME), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
createRelevantNonNullAnnotation(type, methodDef);
return methodDef;
}

/**
Expand Down Expand Up @@ -808,7 +812,9 @@ private JCMethodDecl generateBuildMethod(CheckerFrameworkVersion cfv, String bui
JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, annsOnMethod);

List<JCVariableDecl> params = HandleBuilder.generateBuildArgs(cfv, type, builderFields);
return maker.MethodDef(modifiers, type.toName(buildName), cloneSelfType(returnType), List.<JCTypeParameter>nil(), params, thrownExceptions, body, null);
JCMethodDecl methodDef = maker.MethodDef(modifiers, type.toName(buildName), cloneSelfType(returnType), List.<JCTypeParameter>nil(), params, thrownExceptions, body, null);
createRelevantNonNullAnnotation(type, methodDef);
return methodDef;
}

private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
Expand Down
1 change: 1 addition & 0 deletions src/core/lombok/javac/handlers/JavacSingularsRecipes.java
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ private void finishAndInjectMethod(CheckerFrameworkVersion cfv, JavacTreeMaker m

JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, jcVariableDecls, thrown, body, null);
recursiveSetGeneratedBy(method, source, builderType.getContext());
if (returnStatement != null) createRelevantNonNullAnnotation(builderType, method);
injectMethod(builderType, method);
}

Expand Down
Loading

0 comments on commit 3f0fec1

Please sign in to comment.