Merge branch 'check-return-value'

This commit is contained in:
Reinier Zwitserloot committed 2026-08-21 02:57:59 +02:00
commit 84f8baf3d5
42 files changed
+433 -19

No files matched your search

+1
View File
@@ -63,6 +63,7 @@ Taiki Sugawara <buzz.taiki@gmail.com>
Takuya Murakami <tmurakam@tmurakam.org>
Thomas Darimont <thomas.darimont@gmail.com>
Till Brychcy <till.brychcy@mercateo.com>
Tim te Beek <timtebeek@gmail.com>
Victor Williams Stafusa da Silva <victorwssilva@gmail.com>
Yonatan Sherwin <yonatansherwin@gmail.com>
Yun Zhi Lin <yun@yunspace.com>
+3
View File
@@ -3,8 +3,11 @@ Lombok Changelog
### v1.18.47 "Edgy Guinea Pig"
* BUGFIX: `@SneakyThrows` usage on JDK26 no longer results in class files that require `lombok.jar` to be on the runtime classpath (which should not be neccessary). [#4040](https://github.com/projectlombok/lombok/issues/4022).
* FEATURE: New [config key](https://projectlombok.org/features/configuration) `lombok.checkReturnValueAnnotation` (values: `none`, `lombok`; default: `none`) lets lombok generate `@lombok.CheckReturnValue` on generated methods where the return value should not be ignored, such as `@With` methods and `@Builder.build()`. A future lombok release may flip the default to `lombok`. [#4013](https://github.com/projectlombok/lombok/pull/4013).
* PROMOTION: `@SuperBuilder` has been promoted to the main package. Otherwise, no changes have been made to the annotation. The old experimental annotation will remain for a few versions, at which point it will be marked as a deprecated annotation. Eventually it'll be removed. If you had `lombok.config` configuration for this annotation, the configuration keys for this feature have been renamed.
* OLD-CRUFT: `lombok.experimental.Wither` and `lombok.Delegate` are deprecated remnants; these features were moved (to respectively `lombok.With` and `lombok.experimental.Delegate` over 5 years ago. They are now removed entirely. If your project is dependent on an older version of lombok which still has those; fret not, lombok still processes these annotations. It just no longer includes them in the jar.
* FEATURE: CheckerFramework: Lombok now adds `@SideEffectFree` to constructors it makes if you have enabled checker framework via `lombok.config`.
* BUGFIX: CheckerFramework: Lombok would add `@SideEffectFree` to the `build()` method of any generated builder, even if it is a builder for invoking a method; in that case, the side-effect-free nature of `build()` mirrors the side-effect-free nature of the method invocation you've built. Lombok now checks if the method it generated a builder for is side effect free / 'check return type'.
### v1.18.46 (April 22nd, 2026)
* PLATFORM: JDK26 support added [#4019](https://github.com/projectlombok/lombok/issues/4019).
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2025-2026 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;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Lombok adds this annotation to generated methods where the return value should not be ignored.
* <p>
* For example, {@code @With} methods return a new instance, so ignoring the return value is always a bug.
* Similarly, {@code @Builder}'s {@code build()} method produces the built object.
* <p>
* Static analysis tools (Error Prone, IntelliJ, SpotBugs) recognize {@code @CheckReturnValue}
* by simple class name, regardless of package, and will warn when the return value is discarded.
* <p>
* If you want to opt in, you can add {@code lombok.checkReturnValueAnnotation = lombok} to
* {@code lombok.config}.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface CheckReturnValue {
}
+16
View File
@@ -25,6 +25,7 @@ import java.util.List;
import lombok.core.configuration.CallSuperType;
import lombok.core.configuration.CapitalizationStrategy;
import lombok.core.configuration.CheckReturnValueFlavor;
import lombok.core.configuration.CheckerFrameworkVersion;
import lombok.core.configuration.ConfigurationKey;
import lombok.core.configuration.FlagUsageType;
@@ -90,6 +91,21 @@ public class ConfigurationKeys {
*/
public static final ConfigurationKey<Boolean> ADD_LOMBOK_GENERATED_ANNOTATIONS = new ConfigurationKey<Boolean>("lombok.addLombokGeneratedAnnotation", "Generate @lombok.Generated on all generated code (default: true).") {};
/**
* lombok configuration: {@code lombok.checkReturnValueAnnotation} += {@code lombok} / {@code androidx}.
*
* If set, lombok generates all requested annotation flavors indicating that a caller must use the returned value on all methods where it is sensible, such as {@code @With} methods and {@code @Builder}'s {@code build()} method.
* <p>
* If value {@code lombok} is added, lombok generates {@code @lombok.CheckReturnValue}.
* <p>
* If value {@code androidx} is added, lombok generates {@code @androidx.annotation.CheckResult}.
* <p>
* The current default is no flavors (no annotations are added), but a future lombok release may flip the default {@code lombok}.
* <p>
* NB: {@code build()} methods only get these annotations if the thing they are building for is a type or constructor. Applying {@code @Builder} to a method will not result in these annotations unless that method is itself so annotated.
*/
public static final ConfigurationKey<List<CheckReturnValueFlavor>> CHECK_RETURN_VALUE_ANNOTATION = new ConfigurationKey<List<CheckReturnValueFlavor>>("lombok.checkReturnValueAnnotation", "Which @CheckReturnValue annotation flavor(s) to emit on generated methods. Values: lombok, androidx (default: none).") {};
/**
* lombok configuration: {@code lombok.extern.findbugs.addSuppressFBWarnings} = {@code true} | {@code false}.
*
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2026 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;
/** Used for lombok configuration to select which {@code @CheckReturnValue} annotation flavor(s) to emit on generated methods. */
public enum CheckReturnValueFlavor implements MappedConfigEnum {
LOMBOK,
ANDROIDX,
;
@Override public boolean matches(String value) {
return value.equals(this.name().toLowerCase());
}
@Override public String toString() {
return this.name().toLowerCase();
}
public String getMarkerAnnotationFullyQualifiedName() {
if (this == LOMBOK) return "lombok.CheckReturnValue";
if (this == ANDROIDX) return "androidx.annotation.CheckResult";
throw new IllegalStateException("bug");
}
}
@@ -31,11 +31,13 @@ public enum JacksonVersion implements MappedConfigEnum {
@Override public boolean matches(String value) {
if (this == TWO) return "2".equals(value);
return "3".equals(value);
if (this == THREE) return "3".equals(value);
throw new IllegalStateException("bug");
}
@Override public String toString() {
if (this == TWO) return "2";
return "3";
if (this == THREE) return "3";
throw new IllegalStateException("bug");
}
}
@@ -79,7 +79,7 @@ public class HandlerUtil {
return 43;
}
public static final List<String> NONNULL_ANNOTATIONS, BASE_COPYABLE_ANNOTATIONS, JACKSON_COPY_TO_GETTER_ANNOTATIONS, JACKSON_COPY_TO_SETTER_ANNOTATIONS, JACKSON_COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS, JACKSON_COPY_TO_BUILDER_ANNOTATIONS;
public static final List<String> NONNULL_ANNOTATIONS, BASE_COPYABLE_ANNOTATIONS, JACKSON_COPY_TO_GETTER_ANNOTATIONS, JACKSON_COPY_TO_SETTER_ANNOTATIONS, JACKSON_COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS, JACKSON_COPY_TO_BUILDER_ANNOTATIONS, IMPLIES_CHECK_RETURN_VALUE_ANNOTATIONS;
static {
// This is a list of annotations with a __highly specific meaning__: All annotations in this list indicate that passing null for the relevant item is __never__ acceptable, regardless of settings or circumstance.
// In other words, things like 'this models a database table, and the db table column has a nonnull constraint', or 'this represents a web form, and if this is null, the form is invalid' __do not count__ and should not be in this list;
@@ -497,6 +497,13 @@ public class HandlerUtil {
"com.fasterxml.jackson.databind.annotation.JsonNaming",
"tools.jackson.databind.annotation.JsonNaming",
}));
IMPLIES_CHECK_RETURN_VALUE_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
"lombok.CheckReturnValue",
"android.annotation.CheckResult",
"androidx.annotation.CheckResult",
"org.checkerframework.dataflow.qual.SideEffectFree",
"org.checkerframework.dataflow.qual.Pure",
}));
}
/** Checks if the given name is a valid identifier.
@@ -116,6 +116,7 @@ import lombok.core.AnnotationValues;
import lombok.core.AnnotationValues.AnnotationValue;
import lombok.core.LombokImmutableList;
import lombok.core.TypeResolver;
import lombok.core.configuration.CheckReturnValueFlavor;
import lombok.core.configuration.CheckerFrameworkVersion;
import lombok.core.configuration.NullAnnotationLibrary;
import lombok.core.configuration.NullCheckExceptionType;
@@ -790,6 +791,17 @@ public class EclipseHandlerUtil {
return false;
}
public static boolean hasCheckReturnValueImplicatingAnnotations(EclipseNode node, Annotation[] anns) {
if (anns == null) return false;
for (Annotation annotation : anns) {
TypeReference typeRef = annotation.type;
if (typeRef != null && typeRef.getTypeName() != null) {
for (String bn : IMPLIES_CHECK_RETURN_VALUE_ANNOTATIONS) if (typeMatches(bn, node, typeRef)) return true;
}
}
return false;
}
private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0];
/**
@@ -2096,6 +2108,7 @@ public class EclipseHandlerUtil {
private static final char[][] JAKARTA_ANNOTATION_GENERATED = Eclipse.fromQualifiedName("jakarta.annotation.Generated");
private static final char[][] LOMBOK_GENERATED = Eclipse.fromQualifiedName("lombok.Generated");
private static final char[][] EDU_UMD_CS_FINDBUGS_ANNOTATIONS_SUPPRESSFBWARNINGS = Eclipse.fromQualifiedName("edu.umd.cs.findbugs.annotations.SuppressFBWarnings");
private static final Map<CheckReturnValueFlavor, char[][]> checkReturnValueQualifiedNameCache = new HashMap<CheckReturnValueFlavor, char[][]>();
public static Annotation[] addSuppressWarningsAll(EclipseNode node, ASTNode source, Annotation[] originalAnnotationArray) {
Annotation[] anns = originalAnnotationArray;
@@ -2126,6 +2139,18 @@ public class EclipseHandlerUtil {
return result;
}
public static Annotation[] addCheckReturnValue(EclipseNode node, ASTNode source, Annotation[] originalAnnotationArray) {
Annotation[] out = originalAnnotationArray;
List<CheckReturnValueFlavor> flavors = node.getAst().readConfiguration(ConfigurationKeys.CHECK_RETURN_VALUE_ANNOTATION);
for (CheckReturnValueFlavor flavor : flavors) {
char[][] fqn = checkReturnValueQualifiedNameCache.get(flavor);
if (fqn == null) checkReturnValueQualifiedNameCache.put(flavor, fqn = Eclipse.fromQualifiedName(flavor.getMarkerAnnotationFullyQualifiedName()));
out = addAnnotation(source, out, fqn);
}
return out;
}
static Annotation[] addAnnotation(ASTNode source, Annotation[] originalAnnotationArray, char[][] annotationTypeFqn) {
return addAnnotation(source, originalAnnotationArray, annotationTypeFqn, (ASTNode[]) null);
}
@@ -125,6 +125,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
static class BuilderJob {
CheckerFrameworkVersion checkerFramework;
EclipseNode parentType;
boolean checkReturnValue;
String builderMethodName, buildMethodName;
boolean isStatic;
TypeParameter[] typeParams;
@@ -299,6 +300,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
if (parent.get() instanceof TypeDeclaration) {
job.checkReturnValue = true;
if (!isClass(parent) && !isRecord(parent)) {
annotationNode.addError(BUILDER_NODE_NOT_SUPPORTED_ERR);
return;
@@ -366,6 +368,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
job.setBuilderClassName(job.replaceBuilderClassName(td.name));
if (!checkName("builderClassName", job.builderClassName, annotationNode)) return;
} else if (parent.get() instanceof ConstructorDeclaration) {
job.checkReturnValue = true;
ConstructorDeclaration cd = (ConstructorDeclaration) parent.get();
if (cd.typeParameters != null && cd.typeParameters.length > 0) {
annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
@@ -382,6 +385,11 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (!checkName("builderClassName", job.builderClassName, annotationNode)) return;
} else if (parent.get() instanceof MethodDeclaration) {
MethodDeclaration md = (MethodDeclaration) parent.get();
if (md.returnType != null && Arrays.equals(md.returnType.getLastToken(), TypeReference.VOID)) {
job.checkReturnValue = false;
} else {
job.checkReturnValue = hasCheckReturnValueImplicatingAnnotations(parent, md.annotations);
}
job.parentType = parent.up();
job.isStatic = md.isStatic();
@@ -885,10 +893,11 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
}
out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[0]);
if (job.checkerFramework.generateSideEffectFree()) {
if (job.checkReturnValue && job.checkerFramework.generateSideEffectFree()) {
out.annotations = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE)};
}
out.receiver = generateBuildReceiver(job);
if (job.checkReturnValue) out.annotations = addCheckReturnValue(job.builderType, job.source, out.annotations);
if (staticName == null) createRelevantNonNullAnnotation(job.builderType, out);
out.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null);
return out;
@@ -475,9 +475,12 @@ public class HandleConstructor {
Annotation[] constructorProperties = null;
if (addConstructorProperties && !isLocalType(type)) constructorProperties = createConstructorProperties(source, fieldsToParam);
Annotation[] checkerFramework = null;
if (getCheckerFrameworkVersion(type).generateSideEffectFree()) checkerFramework = new Annotation[] { generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) };
constructor.annotations = copyAnnotations(source,
onConstructor.toArray(new Annotation[0]),
constructorProperties);
constructorProperties, checkerFramework);
}
constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope);
@@ -570,7 +573,10 @@ 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(type).generateSideEffectFree()) checkerFramework = new Annotation[] { generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) };
parameter.annotations = copyAnnotations(source, findCopyableAnnotations(fieldNode), checkerFramework);
if (parameter.annotations != null) {
parameter.bits |= Eclipse.HasTypeAnnotations;
constructor.bits |= Eclipse.HasTypeAnnotations;
@@ -272,6 +272,8 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
} else {
method.annotations = new Annotation[] { overrideAnnotation };
}
method.annotations = EclipseHandlerUtil.addCheckReturnValue(type, method, method.annotations);
method.selector = "hashCode".toCharArray();
method.thrownExceptions = null;
method.typeParameters = null;
@@ -635,6 +637,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
} else {
method.annotations = new Annotation[] { overrideAnnotation };
}
method.annotations = EclipseHandlerUtil.addCheckReturnValue(type, method, method.annotations);
method.selector = "equals".toCharArray();
method.thrownExceptions = null;
method.typeParameters = null;
@@ -291,6 +291,7 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
findDelegatesAndMarkAsHandled(fieldNode),
checkerFramework,
deprecated);
method.annotations = EclipseHandlerUtil.addCheckReturnValue(fieldNode, method, method.annotations);
}
if (addSuppressWarningsUnchecked) {
@@ -27,7 +27,6 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -163,7 +162,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
AnnotationValues<Accessors> accessors = getAccessorsForField(fieldNode);
String setterName = toSetterName(fieldNode, isBoolean, accessors);
boolean shouldReturnThis = shouldReturnThis(fieldNode, accessors);
boolean fluent = accessors.isExplicit("fluent");
boolean fluent = accessors.isExplicit("fluent");
if (setterName == null) {
fieldNode.addWarning("Not generating setter for this field: It does not fit your @Accessors prefix list.");
@@ -25,6 +25,7 @@ import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.experimental.StandardException;
import lombok.core.AST.Kind;
import lombok.core.configuration.CheckerFrameworkVersion;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
@@ -75,6 +76,9 @@ public class HandleStandardException extends EclipseAnnotationHandler<StandardEx
ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This);
explicitCall.arguments = new Expression[] {messageArgument, causeArgument};
ConstructorDeclaration constructor = createConstructor(level, typeNode, false, false, source, explicitCall, null);
Annotation[] checkerFramework = null;
if (getCheckerFrameworkVersion(typeNode).generateSideEffectFree()) checkerFramework = new Annotation[] { generateNamedAnnotation(source.get(), CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) };
constructor.annotations = checkerFramework;
injectMethod(typeNode, constructor);
}
@@ -88,6 +92,9 @@ public class HandleStandardException extends EclipseAnnotationHandler<StandardEx
ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This);
explicitCall.arguments = new Expression[] {messageArgument, causeArgument};
ConstructorDeclaration constructor = createConstructor(level, typeNode, true, false, source, explicitCall, null);
Annotation[] checkerFramework = null;
if (getCheckerFrameworkVersion(typeNode).generateSideEffectFree()) checkerFramework = new Annotation[] { generateNamedAnnotation(source.get(), CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) };
constructor.annotations = checkerFramework;
injectMethod(typeNode, constructor);
}
@@ -105,6 +112,9 @@ public class HandleStandardException extends EclipseAnnotationHandler<StandardEx
Expression messageExpr = new ConditionalExpression(causeNotNull, causeDotGetMessage, new NullLiteral(pS, pE));
explicitCall.arguments = new Expression[] {messageExpr, new SingleNameReference(CAUSE, p)};
ConstructorDeclaration constructor = createConstructor(level, typeNode, false, true, source, explicitCall, null);
Annotation[] checkerFramework = null;
if (getCheckerFrameworkVersion(typeNode).generateSideEffectFree()) checkerFramework = new Annotation[] { generateNamedAnnotation(source.get(), CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) };
constructor.annotations = checkerFramework;
injectMethod(typeNode, constructor);
}
@@ -123,6 +133,9 @@ public class HandleStandardException extends EclipseAnnotationHandler<StandardEx
causeDotInitCause.arguments = new Expression[] {new SingleNameReference(CAUSE, p)};
IfStatement ifs = new IfStatement(causeNotNull, causeDotInitCause, pS, pE);
ConstructorDeclaration constructor = createConstructor(level, typeNode, true, true, source, explicitCall, ifs);
Annotation[] checkerFramework = null;
if (getCheckerFrameworkVersion(typeNode).generateSideEffectFree()) checkerFramework = new Annotation[] { generateNamedAnnotation(source.get(), CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) };
constructor.annotations = checkerFramework;
injectMethod(typeNode, constructor);
}
@@ -863,11 +863,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
out.returnType = new SingleTypeReference(classGenericName.toCharArray(), 0);
Annotation overrideAnn = override ? makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, job.source) : null;
Annotation sefAnn = job.checkerFramework.generateSideEffectFree() ? generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE): null;
Annotation sefAnn = job.checkerFramework.generateSideEffectFree() ? generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) : null;
if (overrideAnn != null && sefAnn != null) out.annotations = new Annotation[] {overrideAnn, sefAnn};
else if (overrideAnn != null) out.annotations = new Annotation[] {overrideAnn};
else if (sefAnn != null) out.annotations = new Annotation[] {sefAnn};
out.receiver = HandleBuilder.generateBuildReceiver(job);
out.annotations = addCheckReturnValue(job.builderType, job.source, out.annotations);
out.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null);
return out;
}
@@ -893,6 +894,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
statements.add(new ReturnStatement(allocationStatement, 0, 0));
out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[0]);
out.receiver = HandleBuilder.generateBuildReceiver(job);
out.annotations = addCheckReturnValue(job.builderType, job.source, out.annotations);
createRelevantNonNullAnnotation(job.builderType, out);
out.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null);
return out;
@@ -298,6 +298,8 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
} else {
method.annotations = new Annotation[] { overrideAnnotation };
}
method.annotations = EclipseHandlerUtil.addCheckReturnValue(type, method, method.annotations);
method.arguments = null;
method.selector = "toString".toCharArray();
method.thrownExceptions = null;
@@ -292,6 +292,7 @@ public class HandleWith extends EclipseAnnotationHandler<With> {
param.annotations = copyAnnotations(source, copyableAnnotations, onParam.toArray(new Annotation[0]));
EclipseHandlerUtil.createRelevantNonNullAnnotation(fieldNode, method);
method.annotations = EclipseHandlerUtil.addCheckReturnValue(fieldNode, source, method.annotations);
method.traverse(new SetGeneratedByVisitor(source), parent.scope);
copyJavadoc(fieldNode, method, CopyJavadoc.WITH);
@@ -375,6 +375,7 @@ public class HandleWithBy extends EclipseAnnotationHandler<WithBy> {
createRelevantNonNullAnnotation(sourceNode, param, method);
createRelevantNonNullAnnotation(fieldNode, method);
method.annotations = addCheckReturnValue(fieldNode, source, method.annotations);
method.traverse(new SetGeneratedByVisitor(source), parent.scope);
copyJavadoc(fieldNode, method, CopyJavadoc.WITH_BY);
@@ -29,6 +29,7 @@ import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.ArrayList;
import javax.lang.model.element.Modifier;
import javax.lang.model.type.TypeKind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
@@ -105,6 +106,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
static class BuilderJob {
CheckerFrameworkVersion checkerFramework;
JavacNode parentType;
boolean checkReturnValue;
String builderMethodName, buildMethodName;
boolean isStatic;
List<JCTypeParameter> typeParams;
@@ -247,6 +249,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
job.parentType = parent;
job.checkReturnValue = true;
JCClassDecl td = (JCClassDecl) parent.get();
ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>();
@@ -315,6 +318,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
job.parentType = parent.up();
job.checkReturnValue = true;
JCClassDecl td = (JCClassDecl) job.parentType.get();
job.typeParams = job.builderTypeParams = td.typarams;
buildMethodReturnType = job.createBuilderParentTypeReference();
@@ -326,6 +330,11 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
job.parentType = parent.up();
JCClassDecl td = (JCClassDecl) job.parentType.get();
JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
if (jmd.restype instanceof JCPrimitiveTypeTree && ((JCPrimitiveTypeTree) jmd.restype).getPrimitiveTypeKind() == TypeKind.VOID) {
job.checkReturnValue = false;
} else {
job.checkReturnValue = hasCheckReturnValueImplicatingAnnotations(fillParametersFrom, jmd.mods.annotations);
}
job.isStatic = (jmd.mods.flags & Flags.STATIC) != 0;
JCExpression fullReturnType = jmd.restype;
@@ -338,7 +347,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
if (job.builderClassName.indexOf('*') > -1) {
String replStr = returnTypeToBuilderClassName(annotationNode, td, buildMethodReturnType, job.typeParams);
if (replStr == null) return; // shuold not happen
if (replStr == null) return; // should not happen
job.builderClassName = job.builderClassName.replace("*", replStr);
}
if (job.toBuilder) {
@@ -792,7 +801,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCBlock body = maker.Block(0, statements.toList());
List<JCAnnotation> annsOnMethod = job.checkerFramework.generateSideEffectFree() ? List.of(maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil();
List<JCAnnotation> annsOnMethod = (job.checkReturnValue && job.checkerFramework.generateSideEffectFree()) ? List.of(maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil();
JCVariableDecl recv = generateReceiver(job);
JCMethodDecl methodDef;
JCExpression returnTypeCopy = cloneType(maker, returnType, job.sourceNode);
@@ -801,6 +810,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
} else {
methodDef = maker.MethodDef(maker.Modifiers(toJavacModifier(job.accessInners), annsOnMethod), job.toName(job.buildMethodName), returnTypeCopy, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
}
if (job.checkReturnValue) addCheckReturnValue(methodDef.mods, job.builderType, job.sourceNode);
if (staticName == null) createRelevantNonNullAnnotation(job.builderType, methodDef);
return methodDef;
}
@@ -360,7 +360,12 @@ public class HandleConstructor {
assigns.append(maker.Exec(assign));
}
JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil());
CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(source);
List<JCAnnotation> anns = checkerFramework.generateSideEffectFree() ?
List.of(maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) :
List.<JCAnnotation>nil();
JCModifiers mods = maker.Modifiers(toJavacModifier(level), anns);
if (addConstructorProperties && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) {
addConstructorProperties(mods, typeNode, fieldsToParam);
}
@@ -442,8 +447,6 @@ public class HandleConstructor {
JavacTreeMaker maker = typeNode.getTreeMaker();
JCClassDecl type = (JCClassDecl) typeNode.get();
JCModifiers mods = maker.Modifiers(Flags.STATIC | toJavacModifier(level));
JCExpression returnType, constructorType;
ListBuffer<JCTypeParameter> typeParams = new ListBuffer<JCTypeParameter>();
@@ -473,6 +476,13 @@ public class HandleConstructor {
JCReturn returnStatement = maker.Return(maker.NewClass(null, List.<JCExpression>nil(), constructorType, args.toList(), null));
JCBlock body = maker.Block(0, List.<JCStatement>of(returnStatement));
CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(source);
List<JCAnnotation> anns = checkerFramework.generateSideEffectFree() ?
List.of(maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) :
List.<JCAnnotation>nil();
JCModifiers mods = maker.Modifiers(Flags.STATIC | toJavacModifier(level), anns);
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);
@@ -228,6 +228,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil()));
}
JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod);
addCheckReturnValue(mods, typeNode, source);
JCExpression returnType = maker.TypeIdent(CTC_INT);
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
@@ -439,6 +440,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil()));
}
JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod);
addCheckReturnValue(mods, typeNode, source);
JCExpression objectType;
if (annsOnParamOnMethod.isEmpty()) {
objectType = genJavaLangTypeRef(typeNode, "Object");
@@ -56,6 +56,7 @@ import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCSynchronized;
@@ -264,9 +265,11 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
if (getCheckerFrameworkVersion(field).generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genTypeRef(field, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil()));
}
if (isFieldDeprecated(field)) annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.<JCExpression>nil()));
JCModifiers mods = treeMaker.Modifiers(access, annsOnMethod);
addCheckReturnValue(mods, field, source);
if (makeFinal) access |= Flags.FINAL;
JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType,
JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(mods, methodName, methodType,
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source);
decl.mods.annotations = decl.mods.annotations.appendList(delegates);
@@ -31,6 +31,7 @@ import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.experimental.StandardException;
import lombok.core.AST.Kind;
import lombok.core.configuration.CheckerFrameworkVersion;
import lombok.core.AnnotationValues;
import lombok.delombok.LombokOptionsFactory;
import lombok.javac.Javac;
@@ -205,7 +206,12 @@ public class HandleStandardException extends JavacAnnotationHandler<StandardExce
params.append(param);
}
JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil());
CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(source);
List<JCAnnotation> anns = checkerFramework.generateSideEffectFree() ?
List.of(maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) :
List.<JCAnnotation>nil();
JCModifiers mods = maker.Modifiers(toJavacModifier(level), anns);
if (addConstructorProperties) addConstructorProperties(mods, typeNode, msgParam, causeParam);
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"),
null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
@@ -841,6 +841,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
} else {
methodDef = maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null);
}
addCheckReturnValue(methodDef.mods, job.builderType, job.sourceNode);
return methodDef;
}
@@ -869,6 +870,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
} else {
methodDef = maker.MethodDef(modifiers, job.toName(job.buildMethodName), cloneSelfType(job.parentType), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
}
addCheckReturnValue(methodDef.mods, job.builderType, job.sourceNode);
createRelevantNonNullAnnotation(job.builderType, methodDef);
return methodDef;
}
@@ -153,6 +153,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
List<JCAnnotation> annsOnMethod = List.of(overrideAnnotation);
if (getCheckerFrameworkVersion(typeNode).generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil()));
JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod);
addCheckReturnValue(mods, typeNode, source);
JCExpression returnType = genJavaLangTypeRef(typeNode, "String");
boolean first = true;
@@ -290,6 +290,7 @@ public class HandleWith extends JavacAnnotationHandler<With> {
if (makeFinal) access |= Flags.FINAL;
JCMethodDecl decl = recursiveSetGeneratedBy(maker.MethodDef(maker.Modifiers(access, annsOnMethod), methodName, returnType,
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source);
addCheckReturnValue(decl.mods, field, source);
copyJavadoc(field, decl, CopyJavadoc.WITH);
return decl;
}
@@ -334,6 +334,7 @@ public class HandleWithBy extends JavacAnnotationHandler<WithBy> {
createRelevantNonNullAnnotation(source, param);
JCMethodDecl decl = recursiveSetGeneratedBy(maker.MethodDef(maker.Modifiers(access, annsOnMethod), methodName, returnType,
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source);
addCheckReturnValue(decl.mods, field, source);
copyJavadoc(field, decl, CopyJavadoc.WITH_BY);
createRelevantNonNullAnnotation(source, decl);
return decl;
@@ -96,6 +96,7 @@ import lombok.core.AnnotationValues.AnnotationValue;
import lombok.core.CleanupTask;
import lombok.core.LombokImmutableList;
import lombok.core.TypeResolver;
import lombok.core.configuration.CheckReturnValueFlavor;
import lombok.core.configuration.CheckerFrameworkVersion;
import lombok.core.configuration.NullAnnotationLibrary;
import lombok.core.configuration.NullCheckExceptionType;
@@ -1554,6 +1555,13 @@ public class JavacHandlerUtil {
}
}
public static void addCheckReturnValue(JCModifiers mods, JavacNode node, JavacNode source) {
java.util.List<CheckReturnValueFlavor> flavors = node.getAst().readConfiguration(ConfigurationKeys.CHECK_RETURN_VALUE_ANNOTATION);
for (CheckReturnValueFlavor flavor : flavors) {
addAnnotation(mods, node, source, flavor.getMarkerAnnotationFullyQualifiedName(), null);
}
}
public static void addAnnotation(JCModifiers mods, JavacNode node, JavacNode source, String annotationTypeFqn, JCExpression arg) {
boolean isJavaLangBased;
String simpleName; {
@@ -1730,6 +1738,16 @@ public class JavacHandlerUtil {
return false;
}
public static boolean hasCheckReturnValueImplicatingAnnotations(JavacNode node, List<JCAnnotation> anns) {
if (anns == null) return false;
for (JCAnnotation ann : anns) {
String annotationTypeName = getTypeName(ann.annotationType);
for (String nn : IMPLIES_CHECK_RETURN_VALUE_ANNOTATIONS) if (typeMatches(nn, node, annotationTypeName)) return true;
}
return false;
}
/**
* Searches the given field node for annotations and returns each one that is 'copyable' (either via configuration or from the base list).
*/
@@ -0,0 +1,60 @@
class CheckReturnValueBuilder {
private final int x;
private final String name;
@java.lang.SuppressWarnings("all")
@lombok.Generated
CheckReturnValueBuilder(final int x, final String name) {
this.x = x;
this.name = name;
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public static class CheckReturnValueBuilderBuilder {
@java.lang.SuppressWarnings("all")
@lombok.Generated
private int x;
@java.lang.SuppressWarnings("all")
@lombok.Generated
private String name;
@java.lang.SuppressWarnings("all")
@lombok.Generated
CheckReturnValueBuilderBuilder() {
}
/**
* @return {@code this}.
*/
@java.lang.SuppressWarnings("all")
@lombok.Generated
public CheckReturnValueBuilder.CheckReturnValueBuilderBuilder x(final int x) {
this.x = x;
return this;
}
/**
* @return {@code this}.
*/
@java.lang.SuppressWarnings("all")
@lombok.Generated
public CheckReturnValueBuilder.CheckReturnValueBuilderBuilder name(final String name) {
this.name = name;
return this;
}
@lombok.CheckReturnValue
@java.lang.SuppressWarnings("all")
@lombok.Generated
public CheckReturnValueBuilder build() {
return new CheckReturnValueBuilder(this.x, this.name);
}
@java.lang.Override
@lombok.CheckReturnValue
@java.lang.SuppressWarnings("all")
@lombok.Generated
public java.lang.String toString() {
return "CheckReturnValueBuilder.CheckReturnValueBuilderBuilder(x=" + this.x + ", name=" + this.name + ")";
}
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public static CheckReturnValueBuilder.CheckReturnValueBuilderBuilder builder() {
return new CheckReturnValueBuilder.CheckReturnValueBuilderBuilder();
}
}
@@ -0,0 +1,26 @@
class CheckReturnValueWith {
final int x;
final String name;
CheckReturnValueWith(int x, String name) {
this.x = x;
this.name = name;
}
/**
* @return a clone of this object, except with this updated property (returns {@code this} if an identical value is passed).
*/
@lombok.CheckReturnValue
@java.lang.SuppressWarnings("all")
@lombok.Generated
public CheckReturnValueWith withX(final int x) {
return this.x == x ? this : new CheckReturnValueWith(x, this.name);
}
/**
* @return a clone of this object, except with this updated property (returns {@code this} if an identical value is passed).
*/
@lombok.CheckReturnValue
@java.lang.SuppressWarnings("all")
@lombok.Generated
public CheckReturnValueWith withName(final String name) {
return this.name == name ? this : new CheckReturnValueWith(this.x, name);
}
}
@@ -70,6 +70,7 @@ class CheckerFrameworkBasic {
public java.lang.String toString() {
return "CheckerFrameworkBasic(x=" + this.getX() + ", y=" + this.getY() + ", z=" + this.getZ() + ")";
}
@org.checkerframework.dataflow.qual.SideEffectFree
@java.lang.SuppressWarnings("all")
@lombok.Generated
public CheckerFrameworkBasic(final int x, final int y, final int z) {
@@ -11,6 +11,7 @@ class CheckerFrameworkBuilder {
private static int $default$x() {
return 5;
}
@org.checkerframework.dataflow.qual.SideEffectFree
@java.lang.SuppressWarnings("all")
@lombok.Generated
CheckerFrameworkBuilder(final int x, final int y, final int z, final List<String> names) {
@@ -37,6 +38,7 @@ class CheckerFrameworkBuilder {
@java.lang.SuppressWarnings("all")
@lombok.Generated
private java.util.ArrayList<String> names;
@org.checkerframework.dataflow.qual.SideEffectFree
@java.lang.SuppressWarnings("all")
@lombok.Generated
CheckerFrameworkBuilderBuilder() {
@@ -100,6 +100,7 @@ class CheckerFrameworkSuperBuilder {
@java.lang.SuppressWarnings("all")
@lombok.Generated
private static final class ParentBuilderImpl extends CheckerFrameworkSuperBuilder.Parent.ParentBuilder<CheckerFrameworkSuperBuilder.Parent, CheckerFrameworkSuperBuilder.Parent.ParentBuilderImpl> {
@org.checkerframework.dataflow.qual.SideEffectFree
@java.lang.SuppressWarnings("all")
@lombok.Generated
private ParentBuilderImpl() {
@@ -207,6 +208,7 @@ class CheckerFrameworkSuperBuilder {
@java.lang.SuppressWarnings("all")
@lombok.Generated
private static final class ZChildBuilderImpl extends CheckerFrameworkSuperBuilder.ZChild.ZChildBuilder<CheckerFrameworkSuperBuilder.ZChild, CheckerFrameworkSuperBuilder.ZChild.ZChildBuilderImpl> {
@org.checkerframework.dataflow.qual.SideEffectFree
@java.lang.SuppressWarnings("all")
@lombok.Generated
private ZChildBuilderImpl() {
@@ -0,0 +1,39 @@
@lombok.Builder class CheckReturnValueBuilder {
public static @java.lang.SuppressWarnings("all") @lombok.Generated class CheckReturnValueBuilderBuilder {
private @java.lang.SuppressWarnings("all") @lombok.Generated int x;
private @java.lang.SuppressWarnings("all") @lombok.Generated String name;
@java.lang.SuppressWarnings("all") @lombok.Generated CheckReturnValueBuilderBuilder() {
super();
}
/**
* @return {@code this}.
*/
public @java.lang.SuppressWarnings("all") @lombok.Generated CheckReturnValueBuilder.CheckReturnValueBuilderBuilder x(final int x) {
this.x = x;
return this;
}
/**
* @return {@code this}.
*/
public @java.lang.SuppressWarnings("all") @lombok.Generated CheckReturnValueBuilder.CheckReturnValueBuilderBuilder name(final String name) {
this.name = name;
return this;
}
public @lombok.CheckReturnValue @java.lang.SuppressWarnings("all") @lombok.Generated CheckReturnValueBuilder build() {
return new CheckReturnValueBuilder(this.x, this.name);
}
public @java.lang.Override @lombok.CheckReturnValue @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() {
return (((("CheckReturnValueBuilder.CheckReturnValueBuilderBuilder(x=" + this.x) + ", name=") + this.name) + ")");
}
}
private final int x;
private final String name;
@java.lang.SuppressWarnings("all") @lombok.Generated CheckReturnValueBuilder(final int x, final String name) {
super();
this.x = x;
this.name = name;
}
public static @java.lang.SuppressWarnings("all") @lombok.Generated CheckReturnValueBuilder.CheckReturnValueBuilderBuilder builder() {
return new CheckReturnValueBuilder.CheckReturnValueBuilderBuilder();
}
}
@@ -0,0 +1,22 @@
import lombok.With;
class CheckReturnValueWith {
final @With int x;
final @With String name;
CheckReturnValueWith(int x, String name) {
super();
this.x = x;
this.name = name;
}
/**
* @return a clone of this object, except with this updated property (returns {@code this} if an identical value is passed).
*/
public @lombok.CheckReturnValue @java.lang.SuppressWarnings("all") @lombok.Generated CheckReturnValueWith withX(final int x) {
return ((this.x == x) ? this : new CheckReturnValueWith(x, this.name));
}
/**
* @return a clone of this object, except with this updated property (returns {@code this} if an identical value is passed).
*/
public @lombok.CheckReturnValue @java.lang.SuppressWarnings("all") @lombok.Generated CheckReturnValueWith withName(final String name) {
return ((this.name == name) ? this : new CheckReturnValueWith(this.x, name));
}
}
@@ -58,7 +58,7 @@ import lombok.With;
public @java.lang.Override @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() {
return (((((("CheckerFrameworkBasic(x=" + this.getX()) + ", y=") + this.getY()) + ", z=") + this.getZ()) + ")");
}
public @java.lang.SuppressWarnings("all") @lombok.Generated CheckerFrameworkBasic(final int x, final int y, final int z) {
public @org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") @lombok.Generated CheckerFrameworkBasic(final int x, final int y, final int z) {
super();
this.x = x;
this.y = y;
@@ -8,7 +8,7 @@ import lombok.Singular;
private @java.lang.SuppressWarnings("all") @lombok.Generated int y;
private @java.lang.SuppressWarnings("all") @lombok.Generated int z;
private @java.lang.SuppressWarnings("all") @lombok.Generated java.util.ArrayList<String> names;
@java.lang.SuppressWarnings("all") @lombok.Generated CheckerFrameworkBuilderBuilder() {
@org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") @lombok.Generated CheckerFrameworkBuilderBuilder() {
super();
}
/**
@@ -82,7 +82,7 @@ import lombok.Singular;
private static @java.lang.SuppressWarnings("all") @lombok.Generated int $default$x() {
return 5;
}
@java.lang.SuppressWarnings("all") @lombok.Generated CheckerFrameworkBuilder(final int x, final int y, final int z, final List<String> names) {
@org.checkerframework.dataflow.qual.SideEffectFree @java.lang.SuppressWarnings("all") @lombok.Generated CheckerFrameworkBuilder(final int x, final int y, final int z, final List<String> names) {
super();
this.x = x;
this.y = y;
@@ -0,0 +1,6 @@
//CONF: lombok.checkReturnValueAnnotation = lombok
@lombok.Builder
class CheckReturnValueBuilder {
private final int x;
private final String name;
}
@@ -0,0 +1,11 @@
//CONF: lombok.checkReturnValueAnnotation = lombok
import lombok.With;
class CheckReturnValueWith {
@With final int x;
@With final String name;
CheckReturnValueWith(int x, String name) {
this.x = x;
this.name = name;
}
}
+2
View File
@@ -234,6 +234,8 @@ public class JacksonExample {
<code>protected</code> keyword is different inside the inner class, and the precise behavior that <code>PROTECTED</code> would indicate (access by any source in the same package is allowed, as well as any subclasses <em>from the outer class, marked with <code>@Builder</code></em> is not possible, and marking the inner members <code>public</code> is as close as we can get.
</p><p>
If you have configured a nullity annotation flavour via <a href="configuration"><code>lombok.config</code></a> key <code>lombok.addNullAnnotations</code>, any plural-form generated builder methods for <code>@Singular</code> marked properties (these plural form methods take a collection of some sort and add all elements) get a nullity annotation on the parameter. You get a non-null one normally, but if you have configured the behavior on <code>null</code> being passed in as collection to <code>IGNORE</code>, a nullable annotation is generated instead.
</p><p>
The generated <code>build()</code> method will be annotated to indicate that callers must not ignore the returned value if you ask lombok to do this via the <a href="configuration"><code>lombok.checkReturnValueAnnotation</code></a> configuration key. However, if applying <code>@Builder</code> to a method, lombok doesn't add such an annotation, <em>unless</em> the method you marked with <code>@Builder</code> is itself marked with any annotation that implies 'check return value', including annotations that indicate <em>side effect free</em> or <em>pure</em>. If <code>@Builder</code> is applied to an explicitly written constructor, the <code>@CheckReturnValue</code> annotation will be added to the <code>build()</code> method if requested, even though constructors can in theory have side effects, and thus, ignoring the return value could be acceptable. Nevertheless, lombok assumes all constructors have no effect other than making an object, and you cannot override this assumption.
</p>
</@f.smallPrint>
</@f.scaffold>
@@ -86,6 +86,8 @@
For remarks on <code>@Singular</code>, see <a href="/features/Builder#small-print">the <code>@Builder</code> documentation's</a> small print.
</p><p>
Various well known annotations about nullity cause null checks to be inserted and will be copied to parameter of the builder's 'setter' method. See <a href="/features/GetterSetter#small-print">Getter/Setter documentation's small print</a> for more information.
</p><p>
The generated <code>build()</code> method will be annotated to indicate that callers must not ignore the returned value if you ask lombok to do this via the <a href="configuration"><code>lombok.checkReturnValueAnnotation</code></a> configuration key. While in theory constructors can have side effects, lombok always assumes they do not, and you cannot override this assumption.
</p>
</@f.smallPrint>
</@f.scaffold>
@@ -89,6 +89,15 @@
</ol>
Many <em>flavors</em> are available: <code>jspecify</code> (recommended), <code>checkerframework</code> (recommended), <code>jakarta</code>, <code>eclipse</code>, <code>jetbrains</code>, <code>netbeans</code>, <code>androidx</code>, <code>findbugs</code>, <code>spring</code>, <code>jml</code>, <code>javax</code> (=JSR305; not recommended), <code>android.support</code> (deprecated within android), or define your own via <code>CUSTOM:fully.qualified.NonNullAnnotation:fully.qualified.NullableAnnotation</code>; if your nullity annotation is solely of the type use style (it annotates types, such as eclipse's and checkerframework's offerings, versus annotating methods and parameters), the format is <code>CUSTOM:TYPE_USE:nonnullanno:nullableanno</code>.<br />
<em>This feature was added in lombok v1.18.12</em>.<br />
</p><p id="checkReturnValueAnnotation">
Lombok can add annotations to methods it generates if code that invokes this method should always to something with the returned value. For example, <a href="with"><code>withX</code></a> methods, and <a href="Builder"><code>@Builder</code></a>'s <code>build()</code> method.<br />
Lombok ships with its own take on this annotation named <code>@lombok.CheckReturnValue</code>. Static analysis tools (IntelliJ, Error Prone, SpotBugs) will then warn when callers discard the result. By default, no such annotation is added. Enable with:
<ol class="snippet example oneliner">
<li><code>lombok.checkReturnValueAnnotation = <em>&lt;flavor&gt;</em></code></li>
</ol>
Supported flavors are: <code>lombok</code>, as well as <code>androidx</code> which emits <code>@androidx.annotation.CheckResult</code>.<br />
Default: none. A future lombok release may flip the default to <code>lombok</code>.<br />
<em>This feature was added in lombok v1.18.48</em>.
</p><p>
Lombok can be configured to add <code>@lombok.Generated</code> annotations to all generated nodes where possible; useful for JaCoCo (which has built in support),
or other style checkers and code coverage tools: