[config: lombok.CheckReturnValue]: setting is now a list of possible flavours, do not apply if building for a method, add to getter/hc/equals/toString.

This commit is contained in:
Reinier Zwitserloot committed 2026-08-21 02:42:00 +02:00
1 parent 5aa26873fd
commit e76ba71721
17 files changed
+65 -34

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2025 The Project Lombok Authors.
* 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
+11 -5
View File
@@ -92,13 +92,19 @@ 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 none} | {@code lombok}].
* lombok configuration: {@code lombok.checkReturnValueAnnotation} += {@code lombok} / {@code androidx}.
*
* If set to {@code lombok}, lombok generates {@code @lombok.CheckReturnValue} on generated methods where the return value should not be ignored,
* such as {@code @With} methods and {@code @Builder}'s {@code build()} method.
* If set to {@code none} (the current default), no such annotation is emitted. A future lombok release may flip the default to {@code lombok}.
* 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<CheckReturnValueFlavor> CHECK_RETURN_VALUE_ANNOTATION = new ConfigurationKey<CheckReturnValueFlavor>("lombok.checkReturnValueAnnotation", "Which @CheckReturnValue annotation flavor to emit on generated methods. Values: none, lombok (default: none).") {};
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}.
@@ -21,7 +21,23 @@
*/
package lombok.core.configuration;
/** Used for lombok configuration to select which {@code @CheckReturnValue} annotation flavor to emit on generated methods. */
public enum CheckReturnValueFlavor {
NONE, LOMBOK;
/** 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");
}
}
@@ -2108,7 +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 char[][] LOMBOK_CHECK_RETURN_VALUE = Eclipse.fromQualifiedName("lombok.CheckReturnValue");
private static final Map<CheckReturnValueFlavor, char[][]> checkReturnValueQualifiedNameCache = new HashMap<CheckReturnValueFlavor, char[][]>();
public static Annotation[] addSuppressWarningsAll(EclipseNode node, ASTNode source, Annotation[] originalAnnotationArray) {
Annotation[] anns = originalAnnotationArray;
@@ -2140,15 +2140,15 @@ public class EclipseHandlerUtil {
}
public static Annotation[] addCheckReturnValue(EclipseNode node, ASTNode source, Annotation[] originalAnnotationArray) {
CheckReturnValueFlavor flavor = node.getAst().readConfiguration(ConfigurationKeys.CHECK_RETURN_VALUE_ANNOTATION);
if (flavor == null) flavor = CheckReturnValueFlavor.NONE;
switch (flavor) {
case LOMBOK:
return addAnnotation(source, originalAnnotationArray, LOMBOK_CHECK_RETURN_VALUE);
case NONE:
default:
return 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) {
@@ -897,7 +897,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
out.annotations = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE)};
}
out.receiver = generateBuildReceiver(job);
out.annotations = addCheckReturnValue(job.builderType, job.source, out.annotations);
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;
@@ -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;
@@ -868,6 +868,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
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;
}
@@ -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;
@@ -347,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) {
@@ -810,7 +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);
}
addCheckReturnValue(methodDef.mods, job.builderType, job.sourceNode);
if (job.checkReturnValue) addCheckReturnValue(methodDef.mods, job.builderType, job.sourceNode);
if (staticName == null) createRelevantNonNullAnnotation(job.builderType, methodDef);
return methodDef;
}
@@ -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);
@@ -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;
}
@@ -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;
@@ -1556,15 +1556,9 @@ public class JavacHandlerUtil {
}
public static void addCheckReturnValue(JCModifiers mods, JavacNode node, JavacNode source) {
CheckReturnValueFlavor flavor = node.getAst().readConfiguration(ConfigurationKeys.CHECK_RETURN_VALUE_ANNOTATION);
if (flavor == null) flavor = CheckReturnValueFlavor.NONE;
switch (flavor) {
case LOMBOK:
addAnnotation(mods, node, source, "lombok.CheckReturnValue", null);
return;
case NONE:
default:
return;
java.util.List<CheckReturnValueFlavor> flavors = node.getAst().readConfiguration(ConfigurationKeys.CHECK_RETURN_VALUE_ANNOTATION);
for (CheckReturnValueFlavor flavor : flavors) {
addAnnotation(mods, node, source, flavor.getMarkerAnnotationFullyQualifiedName(), null);
}
}