From f98bf919cc6701e98087d39fefb7bbfc85688834 Mon Sep 17 00:00:00 2001
From: Reinier Zwitserloot
Date: Mon, 6 May 2013 22:09:13 +0200
Subject: Fixed issue 513: If equals is present but hashCode isn't, @Data now
generates a warning to explain this strange situation.
---
.../messages-ecj/EqualsAndHashCodeWithSomeExistingMethods.java.messages | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 test/transform/resource/messages-ecj/EqualsAndHashCodeWithSomeExistingMethods.java.messages
(limited to 'test/transform/resource/messages-ecj')
diff --git a/test/transform/resource/messages-ecj/EqualsAndHashCodeWithSomeExistingMethods.java.messages b/test/transform/resource/messages-ecj/EqualsAndHashCodeWithSomeExistingMethods.java.messages
new file mode 100644
index 00000000..cf6ebea2
--- /dev/null
+++ b/test/transform/resource/messages-ecj/EqualsAndHashCodeWithSomeExistingMethods.java.messages
@@ -0,0 +1,2 @@
+4:57 Not generating equals: One of equals, hashCode, and canEqual exists. You should either write all of these are none of these (in the latter case, lombok generates them).
+15:194 Not generating equals and hashCode: One of equals, hashCode, and canEqual exists. You should either write all of these are none of these (in the latter case, lombok generates them).
--
cgit
From 5a3e9bd8049469169410107011ad0e26b3b629e3 Mon Sep 17 00:00:00 2001
From: Reinier Zwitserloot
Date: Fri, 31 May 2013 01:03:38 +0200
Subject: Added @NonNull on parameters feature (issue 514), including docs and
changelog.
---
buildScripts/website.ant.xml | 3 +
doc/changelog.markdown | 1 +
src/core/lombok/NonNull.java | 16 ++-
.../eclipse/handlers/EclipseHandlerUtil.java | 7 +-
.../lombok/eclipse/handlers/NonNullHandler.java | 147 ++++++++++++++++++++
.../lombok/javac/handlers/HandleSneakyThrows.java | 12 +-
.../lombok/javac/handlers/JavacHandlerUtil.java | 23 +++-
src/core/lombok/javac/handlers/NonNullHandler.java | 148 +++++++++++++++++++++
src/utils/lombok/javac/Javac.java | 32 +++++
.../resource/after-delombok/DataOnLocalClass.java | 8 +-
.../after-delombok/NonNullOnParameter.java | 48 +++++++
.../resource/after-delombok/NonNullPlain.java | 8 +-
.../resource/after-delombok/SetterOnClass.java | 4 +-
.../resource/after-delombok/WitherOnClass.java | 4 +-
.../resource/after-ecj/DataOnLocalClass.java | 8 +-
.../resource/after-ecj/NonNullOnParameter.java | 61 +++++++++
.../transform/resource/after-ecj/NonNullPlain.java | 8 +-
.../resource/after-ecj/SetterOnClass.java | 4 +-
.../resource/after-ecj/WitherOnClass.java | 4 +-
.../resource/before/NonNullOnParameter.java | 30 +++++
.../NonNullOnParameter.java.messages | 1 +
.../messages-delombok/NonNullPlain.java.messages | 1 +
.../messages-ecj/NonNullOnParameter.java.messages | 3 +
.../messages-ecj/NonNullPlain.java.messages | 1 +
.../NonNullOnParameter.java.messages | 1 +
.../messages-idempotent/NonNullPlain.java.messages | 3 +
usage_examples/NonNullExample_post.jpage | 13 ++
usage_examples/NonNullExample_pre.jpage | 10 ++
website/features/Cleanup.html | 2 +-
website/features/Data.html | 2 +-
website/features/Delegate.html | 11 +-
website/features/GetterLazy.html | 2 +-
website/features/GetterSetter.html | 2 +-
website/features/Log.html | 2 +-
website/features/NonNull.html | 73 ++++++++++
website/features/SneakyThrows.html | 4 +-
website/features/Synchronized.html | 2 +-
website/features/ToString.html | 2 +-
website/features/index.html | 18 +--
website/features/val.html | 2 +-
40 files changed, 674 insertions(+), 57 deletions(-)
create mode 100644 src/core/lombok/eclipse/handlers/NonNullHandler.java
create mode 100644 src/core/lombok/javac/handlers/NonNullHandler.java
create mode 100644 test/transform/resource/after-delombok/NonNullOnParameter.java
create mode 100644 test/transform/resource/after-ecj/NonNullOnParameter.java
create mode 100644 test/transform/resource/before/NonNullOnParameter.java
create mode 100644 test/transform/resource/messages-delombok/NonNullOnParameter.java.messages
create mode 100644 test/transform/resource/messages-delombok/NonNullPlain.java.messages
create mode 100644 test/transform/resource/messages-ecj/NonNullOnParameter.java.messages
create mode 100644 test/transform/resource/messages-ecj/NonNullPlain.java.messages
create mode 100644 test/transform/resource/messages-idempotent/NonNullOnParameter.java.messages
create mode 100644 test/transform/resource/messages-idempotent/NonNullPlain.java.messages
create mode 100644 usage_examples/NonNullExample_post.jpage
create mode 100644 usage_examples/NonNullExample_pre.jpage
create mode 100644 website/features/NonNull.html
(limited to 'test/transform/resource/messages-ecj')
diff --git a/buildScripts/website.ant.xml b/buildScripts/website.ant.xml
index 405b388f..41130bd2 100644
--- a/buildScripts/website.ant.xml
+++ b/buildScripts/website.ant.xml
@@ -145,6 +145,9 @@ such as converting the changelog into HTML, and creating javadoc.
+
+
+
diff --git a/doc/changelog.markdown b/doc/changelog.markdown
index e2e3d6b5..aaf66030 100644
--- a/doc/changelog.markdown
+++ b/doc/changelog.markdown
@@ -2,6 +2,7 @@ Lombok Changelog
----------------
### v0.11.9 (Edgy Guinea Pig)
+* FEATURE: `@NonNull` on a method or constructor parameter now generates a null-check statement at the start of your method. This nullcheck will throw a `NullPointerException` with the name of the parameter as the message. [Issue #514](https://code.google.com/p/projectlombok/issues/detail?id=514)
* BUGFIX: Usage of `Lombok.sneakyThrow()` or `@SneakyThrows` would sometimes result in invalid classes (classes which fail with `VerifyError`). [Issue #470](https://code.google.com/p/projectlombok/issues/detail?id=470)
* BUGFIX: Using `val` in try-with-resources did not work for javac. [Issue #520](https://code.google.com/p/projectlombok/issues/detail?id=520)
* BUGFIX: When using `@Data`, warnings are not generated if certain aspects are not generated because you wrote explicit versions of them. However, this gets confusing with `equals` / `hashCode` / `canEqual`, as nothing is generated if any one of those methods is present. Now, if one of `equals` or `hashCode` is present but not the other one (or `canEqual` is present but `equals` and/or `hashCode` is missing), a warning is emitted to explain that lombok will not generate any of the equals / hashCode methods, and that you should either write them all yourself or remove them all. [Issue #513](https://code.google.com/p/projectlombok/issues/detail?id=513)
diff --git a/src/core/lombok/NonNull.java b/src/core/lombok/NonNull.java
index 5f5d8ed2..96813170 100644
--- a/src/core/lombok/NonNull.java
+++ b/src/core/lombok/NonNull.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -28,12 +28,14 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Lombok is smart enough to translate any annotation named {@code @NonNull} in any casing and
- * with any package name to the return type of generated getters and the parameter of generated setters and constructors,
- * as well as generate the appropriate null checks in the setter and constructor.
- *
- * You can use this annotation for the purpose, though you can also use JSR305's annotation, findbugs's, pmd's, or IDEA's, or just
- * about anyone elses. As long as it is named {@code @NonNull}.
+ * If put on a parameter, lombok will insert a null-check at the start of the method / constructor's body, throwing a
+ * {@code NullPointerException} with the parameter's name as message. If put on a field, any generated method assigning
+ * a value to this field will also produce these nullchecks.
+ *
+ * Note that any annotation named {@code NonNull} with any casing and any package will result in nullchecks produced for
+ * generated methods (and the annotation will be copied to the getter return type and any parameters of generated methods),
+ * but only this annotation, if present on a parameter, will result in a null check inserted into your otherwise
+ * handwritten method.
*
* WARNING: If the java community ever does decide on supporting a single {@code @NonNull} annotation (for example via JSR305), then
* this annotation will be deleted from the lombok package. If the need to update an import statement scares
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 7703336f..dc99dabf 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -61,6 +61,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
@@ -1334,7 +1335,11 @@ public class EclipseHandlerUtil {
EqualExpression equalExpression = new EqualExpression(varName, nullLiteral, OperatorIds.EQUAL_EQUAL);
equalExpression.sourceStart = pS; equalExpression.statementEnd = equalExpression.sourceEnd = pE;
setGeneratedBy(equalExpression, source);
- IfStatement ifStatement = new IfStatement(equalExpression, throwStatement, 0, 0);
+ Block throwBlock = new Block(0);
+ throwBlock.statements = new Statement[] {throwStatement};
+ throwBlock.sourceStart = pS; throwBlock.sourceEnd = pE;
+ setGeneratedBy(throwBlock, source);
+ IfStatement ifStatement = new IfStatement(equalExpression, throwBlock, 0, 0);
setGeneratedBy(ifStatement, source);
return ifStatement;
}
diff --git a/src/core/lombok/eclipse/handlers/NonNullHandler.java b/src/core/lombok/eclipse/handlers/NonNullHandler.java
new file mode 100644
index 00000000..5c58069c
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/NonNullHandler.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2013 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.eclipse.handlers;
+
+import java.util.Arrays;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
+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.EqualExpression;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.NonNull;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.eclipse.DeferUntilPostDiet;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+
+import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+@DeferUntilPostDiet
+@ProviderFor(EclipseAnnotationHandler.class)
+public class NonNullHandler extends EclipseAnnotationHandler {
+ @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) {
+ if (annotationNode.up().getKind() == Kind.FIELD) {
+ // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc),
+ // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to
+ // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning
+ // behaviour on _OUR_ 'lombok.NonNull'.
+
+ try {
+ if (isPrimitive(((AbstractVariableDeclaration) annotationNode.up().get()).type)) {
+ annotationNode.addWarning("@NonNull is meaningless on a primitive.");
+ }
+ } catch (Exception ignore) {}
+
+ return;
+ }
+
+ if (annotationNode.up().getKind() != Kind.ARGUMENT) return;
+
+ Argument arg;
+ AbstractMethodDeclaration declaration;
+
+ try {
+ arg = (Argument) annotationNode.up().get();
+ declaration = (AbstractMethodDeclaration) annotationNode.up().up().get();
+ } catch (Exception e) {
+ return;
+ }
+
+ if (isGenerated(declaration)) return;
+
+ // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter,
+ // and if they exist, create a new method in the class: 'private static T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and
+ // wrap all references to it in the super/this to a call to this method.
+
+ Statement nullCheck = generateNullCheck(arg, ast);
+
+ if (nullCheck == null) {
+ // @NonNull applied to a primitive. Kinda pointless. Let's generate a warning.
+ annotationNode.addWarning("@NonNull is meaningless on a primitive.");
+ return;
+ }
+
+ if (declaration.statements == null) {
+ declaration.statements = new Statement[] {nullCheck};
+ } else {
+ char[] expectedName = arg.name;
+ for (Statement stat : declaration.statements) {
+ char[] varNameOfNullCheck = returnVarNameIfNullCheck(stat);
+ if (varNameOfNullCheck == null) break;
+ if (Arrays.equals(expectedName, varNameOfNullCheck)) return;
+ }
+
+ Statement[] newStatements = new Statement[declaration.statements.length + 1];
+ int skipOver = 0;
+ for (Statement stat : declaration.statements) {
+ if (isGenerated(stat)) skipOver++;
+ else break;
+ }
+ System.arraycopy(declaration.statements, 0, newStatements, 0, skipOver);
+ System.arraycopy(declaration.statements, skipOver, newStatements, skipOver + 1, declaration.statements.length - skipOver);
+ newStatements[skipOver] = nullCheck;
+ declaration.statements = newStatements;
+ }
+ annotationNode.up().up().rebuild();
+ }
+
+ private char[] returnVarNameIfNullCheck(Statement stat) {
+ if (!(stat instanceof IfStatement)) return null;
+
+ /* Check that the if's statement is a throw statement, possibly in a block. */ {
+ Statement then = ((IfStatement) stat).thenStatement;
+ if (then instanceof Block) {
+ Statement[] blockStatements = ((Block) then).statements;
+ if (blockStatements == null || blockStatements.length == 0) return null;
+ then = blockStatements[0];
+ }
+
+ if (!(then instanceof ThrowStatement)) return null;
+ }
+
+ /* Check that the if's conditional is like 'x == null'. Return from this method (don't generate
+ a nullcheck) if 'x' is equal to our own variable's name: There's already a nullcheck here. */ {
+ Expression cond = ((IfStatement) stat).condition;
+ if (!(cond instanceof EqualExpression)) return null;
+ EqualExpression bin = (EqualExpression) cond;
+ int operatorId = ((bin.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT);
+ if (operatorId != OperatorIds.EQUAL_EQUAL) return null;
+ if (!(bin.left instanceof SingleNameReference)) return null;
+ if (!(bin.right instanceof NullLiteral)) return null;
+ return ((SingleNameReference) bin.left).token;
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleSneakyThrows.java b/src/core/lombok/javac/handlers/HandleSneakyThrows.java
index c2394fc8..c818f630 100644
--- a/src/core/lombok/javac/handlers/HandleSneakyThrows.java
+++ b/src/core/lombok/javac/handlers/HandleSneakyThrows.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -36,8 +36,6 @@ import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
-import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
@@ -114,14 +112,6 @@ public class HandleSneakyThrows extends JavacAnnotationHandler {
}
}
- private boolean isConstructorCall(final JCStatement supect) {
- if (!(supect instanceof JCExpressionStatement)) return false;
- final JCExpression supectExpression = ((JCExpressionStatement) supect).expr;
- if (!(supectExpression instanceof JCMethodInvocation)) return false;
- final String methodName = ((JCMethodInvocation) supectExpression).meth.toString();
- return "super".equals(methodName) || "this".equals(methodName);
- }
-
private JCStatement buildTryCatchBlock(JavacNode node, List contents, String exception, JCTree source) {
TreeMaker maker = node.getTreeMaker();
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index ef1a9f50..7cbaa5ac 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -50,9 +50,11 @@ import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCAssign;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCImport;
@@ -121,6 +123,7 @@ public class JavacHandlerUtil {
}
public static T recursiveSetGeneratedBy(T node, JCTree source) {
+ if (node == null) return null;
setGeneratedBy(node, source);
node.accept(new MarkingScanner(source));
@@ -543,6 +546,23 @@ public class JavacHandlerUtil {
return MemberExistsResult.NOT_EXISTS;
}
+ public static boolean isConstructorCall(final JCStatement statement) {
+ if (!(statement instanceof JCExpressionStatement)) return false;
+ JCExpression expr = ((JCExpressionStatement) statement).expr;
+ if (!(expr instanceof JCMethodInvocation)) return false;
+ JCExpression invocation = ((JCMethodInvocation) expr).meth;
+ String name;
+ if (invocation instanceof JCFieldAccess) {
+ name = ((JCFieldAccess) invocation).name.toString();
+ } else if (invocation instanceof JCIdent) {
+ name = ((JCIdent) invocation).name.toString();
+ } else {
+ name = "";
+ }
+
+ return "super".equals(name) || "this".equals(name);
+ }
+
/**
* Turns an {@code AccessLevel} instance into the flag bit used by javac.
*/
@@ -890,7 +910,8 @@ public class JavacHandlerUtil {
JCExpression npe = chainDots(variable, "java", "lang", "NullPointerException");
JCTree exception = treeMaker.NewClass(null, List.nil(), npe, List.of(treeMaker.Literal(fieldName.toString())), null);
JCStatement throwStatement = treeMaker.Throw(exception);
- return treeMaker.If(treeMaker.Binary(CTC_EQUAL, treeMaker.Ident(fieldName), treeMaker.Literal(CTC_BOT, null)), throwStatement, null);
+ JCBlock throwBlock = treeMaker.Block(0, List.of(throwStatement));
+ return treeMaker.If(treeMaker.Binary(CTC_EQUAL, treeMaker.Ident(fieldName), treeMaker.Literal(CTC_BOT, null)), throwBlock, null);
}
/**
diff --git a/src/core/lombok/javac/handlers/NonNullHandler.java b/src/core/lombok/javac/handlers/NonNullHandler.java
new file mode 100644
index 00000000..415d6032
--- /dev/null
+++ b/src/core/lombok/javac/handlers/NonNullHandler.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2013 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.javac.handlers;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCBinary;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCIf;
+import com.sun.tools.javac.tree.JCTree.JCLiteral;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCParens;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCThrow;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+
+import lombok.NonNull;
+import lombok.core.AnnotationValues;
+import lombok.core.AST.Kind;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+
+@ProviderFor(JavacAnnotationHandler.class)
+public class NonNullHandler extends JavacAnnotationHandler {
+ @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) {
+ if (annotationNode.up().getKind() == Kind.FIELD) {
+ // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc),
+ // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to
+ // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning
+ // behaviour on _OUR_ 'lombok.NonNull'.
+
+ try {
+ if (isPrimitive(((JCVariableDecl) annotationNode.up().get()).vartype)) {
+ annotationNode.addWarning("@NonNull is meaningless on a primitive.");
+ }
+ } catch (Exception ignore) {}
+
+ return;
+ }
+
+ if (annotationNode.up().getKind() != Kind.ARGUMENT) return;
+
+ JCMethodDecl declaration;
+
+ try {
+ declaration = (JCMethodDecl) annotationNode.up().up().get();
+ } catch (Exception e) {
+ return;
+ }
+
+ if (JavacHandlerUtil.isGenerated(declaration)) return;
+
+ // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter,
+ // and if they exist, create a new method in the class: 'private static T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and
+ // wrap all references to it in the super/this to a call to this method.
+
+ JCStatement nullCheck = recursiveSetGeneratedBy(generateNullCheck(annotationNode.getTreeMaker(), annotationNode.up()), ast);
+
+ if (nullCheck == null) {
+ // @NonNull applied to a primitive. Kinda pointless. Let's generate a warning.
+ annotationNode.addWarning("@NonNull is meaningless on a primitive.");
+ return;
+ }
+
+ List statements = declaration.body.stats;
+
+ String expectedName = annotationNode.up().getName();
+ for (JCStatement stat : statements) {
+ if (JavacHandlerUtil.isConstructorCall(stat)) continue;
+ String varNameOfNullCheck = returnVarNameIfNullCheck(stat);
+ if (varNameOfNullCheck == null) break;
+ if (varNameOfNullCheck.equals(expectedName)) return;
+ }
+
+ List tail = statements;
+ List head = List.nil();
+ for (JCStatement stat : statements) {
+ if (JavacHandlerUtil.isConstructorCall(stat) || JavacHandlerUtil.isGenerated(stat)) {
+ tail = tail.tail;
+ head = head.prepend(stat);
+ continue;
+ }
+ break;
+ }
+
+ List newList = tail.prepend(nullCheck);
+ for (JCStatement stat : head) newList = newList.prepend(stat);
+ declaration.body.stats = newList;
+ }
+
+ /**
+ * Checks if the statement is of the form 'if (x == null) {throw WHATEVER;},
+ * where the block braces are optional. If it is of this form, returns "x".
+ * If it is not of this form, returns null.
+ */
+ private String returnVarNameIfNullCheck(JCStatement stat) {
+ if (!(stat instanceof JCIf)) return null;
+
+ /* Check that the if's statement is a throw statement, possibly in a block. */ {
+ JCStatement then = ((JCIf) stat).thenpart;
+ if (then instanceof JCBlock) {
+ List stats = ((JCBlock) then).stats;
+ if (stats.length() == 0) return null;
+ then = stats.get(0);
+ }
+ if (!(then instanceof JCThrow)) return null;
+ }
+
+ /* Check that the if's conditional is like 'x == null'. Return from this method (don't generate
+ a nullcheck) if 'x' is equal to our own variable's name: There's already a nullcheck here. */ {
+ JCExpression cond = ((JCIf) stat).cond;
+ while (cond instanceof JCParens) cond = ((JCParens) cond).expr;
+ if (!(cond instanceof JCBinary)) return null;
+ JCBinary bin = (JCBinary) cond;
+ if (getTag(bin) != CTC_EQUAL) return null;
+ if (!(bin.lhs instanceof JCIdent)) return null;
+ if (!(bin.rhs instanceof JCLiteral)) return null;
+ if (((JCLiteral) bin.rhs).typetag != CTC_BOT) return null;
+ return ((JCIdent) bin.lhs).name.toString();
+ }
+ }
+}
diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java
index b4e58b8f..08c7c957 100644
--- a/src/utils/lombok/javac/Javac.java
+++ b/src/utils/lombok/javac/Javac.java
@@ -21,6 +21,8 @@
*/
package lombok.javac;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -129,4 +131,34 @@ public class Javac {
throw new RuntimeException(e);
}
}
+
+ private static final Field JCTREE_TAG;
+ private static final Method JCTREE_GETTAG;
+ static {
+ Field f = null;
+ try {
+ f = JCTree.class.getDeclaredField("tag");
+ } catch (NoSuchFieldException e) {}
+ JCTREE_TAG = f;
+
+ Method m = null;
+ try {
+ m = JCTree.class.getDeclaredMethod("getTag");
+ } catch (NoSuchMethodException e) {}
+ JCTREE_GETTAG = m;
+ }
+
+ public static int getTag(JCTree node) {
+ if (JCTREE_GETTAG != null) {
+ try {
+ return (Integer) JCTREE_GETTAG.invoke(node);
+ } catch (Exception e) {}
+ }
+ try {
+ return (Integer) JCTREE_TAG.get(node);
+ } catch (Exception e) {
+ throw new IllegalStateException("Can't get node tag");
+ }
+ }
+
}
diff --git a/test/transform/resource/after-delombok/DataOnLocalClass.java b/test/transform/resource/after-delombok/DataOnLocalClass.java
index ed4d30ca..abe2757b 100644
--- a/test/transform/resource/after-delombok/DataOnLocalClass.java
+++ b/test/transform/resource/after-delombok/DataOnLocalClass.java
@@ -63,7 +63,9 @@ class DataOnLocalClass2 {
String name;
@java.lang.SuppressWarnings("all")
public InnerLocal(@lombok.NonNull final String name) {
- if (name == null) throw new java.lang.NullPointerException("name");
+ if (name == null) {
+ throw new java.lang.NullPointerException("name");
+ }
this.name = name;
}
@lombok.NonNull
@@ -73,7 +75,9 @@ class DataOnLocalClass2 {
}
@java.lang.SuppressWarnings("all")
public void setName(@lombok.NonNull final String name) {
- if (name == null) throw new java.lang.NullPointerException("name");
+ if (name == null) {
+ throw new java.lang.NullPointerException("name");
+ }
this.name = name;
}
@java.lang.Override
diff --git a/test/transform/resource/after-delombok/NonNullOnParameter.java b/test/transform/resource/after-delombok/NonNullOnParameter.java
new file mode 100644
index 00000000..a27d19c9
--- /dev/null
+++ b/test/transform/resource/after-delombok/NonNullOnParameter.java
@@ -0,0 +1,48 @@
+class NonNullOnParameter extends Thread {
+ NonNullOnParameter(@lombok.NonNull String arg) {
+ this(arg, "");
+ if (arg == null) {
+ throw new java.lang.NullPointerException("arg");
+ }
+ }
+ NonNullOnParameter(@lombok.NonNull String arg, @lombok.NonNull String arg2) {
+ super(arg);
+ if (arg2 == null) {
+ throw new java.lang.NullPointerException("arg2");
+ }
+ if (arg == null) throw new NullPointerException();
+ }
+ public void test2(@lombok.NonNull String arg, @lombok.NonNull String arg2, @lombok.NonNull String arg3) {
+ if (arg == null) {
+ throw new java.lang.NullPointerException("arg");
+ }
+ if (arg3 == null) {
+ throw new java.lang.NullPointerException("arg3");
+ }
+ if (arg2 == null) {
+ throw new NullPointerException("arg2");
+ }
+ if (arg == null) System.out.println("Hello");
+ }
+ public void test3(@lombok.NonNull String arg) {
+ if (arg == null) {
+ throw new java.lang.NullPointerException("arg");
+ }
+ if (arg != null) throw new IllegalStateException();
+ }
+ public void test(@lombok.NonNull String stringArg, @lombok.NonNull String arg2, @lombok.NonNull int primitiveArg) {
+ if (stringArg == null) {
+ throw new java.lang.NullPointerException("stringArg");
+ }
+ if (arg2 == null) {
+ throw new java.lang.NullPointerException("arg2");
+ }
+ }
+ public void test(@lombok.NonNull String arg) {
+ if (arg == null) {
+ throw new java.lang.NullPointerException("arg");
+ }
+ System.out.println("Hey");
+ if (arg == null) throw new NullPointerException();
+ }
+}
diff --git a/test/transform/resource/after-delombok/NonNullPlain.java b/test/transform/resource/after-delombok/NonNullPlain.java
index 064e00b9..6b85cbf7 100644
--- a/test/transform/resource/after-delombok/NonNullPlain.java
+++ b/test/transform/resource/after-delombok/NonNullPlain.java
@@ -16,7 +16,9 @@ class NonNullPlain {
@java.beans.ConstructorProperties({"i", "s"})
@java.lang.SuppressWarnings("all")
public NonNullPlain(@lombok.NonNull final int i, @lombok.NonNull final String s) {
- if (s == null) throw new java.lang.NullPointerException("s");
+ if (s == null) {
+ throw new java.lang.NullPointerException("s");
+ }
this.i = i;
this.s = s;
}
@@ -45,7 +47,9 @@ class NonNullPlain {
@java.lang.SuppressWarnings("all")
public void setS(@lombok.NonNull final String s) {
- if (s == null) throw new java.lang.NullPointerException("s");
+ if (s == null) {
+ throw new java.lang.NullPointerException("s");
+ }
this.s = s;
}
diff --git a/test/transform/resource/after-delombok/SetterOnClass.java b/test/transform/resource/after-delombok/SetterOnClass.java
index 151bc179..7077c492 100644
--- a/test/transform/resource/after-delombok/SetterOnClass.java
+++ b/test/transform/resource/after-delombok/SetterOnClass.java
@@ -53,7 +53,9 @@ class SetterOnClass6 {
}
@java.lang.SuppressWarnings("all")
public void setNonNull(@lombok.NonNull final String nonNull) {
- if (nonNull == null) throw new java.lang.NullPointerException("nonNull");
+ if (nonNull == null) {
+ throw new java.lang.NullPointerException("nonNull");
+ }
this.nonNull = nonNull;
}
}
\ No newline at end of file
diff --git a/test/transform/resource/after-delombok/WitherOnClass.java b/test/transform/resource/after-delombok/WitherOnClass.java
index 783fede1..45d0c4b5 100644
--- a/test/transform/resource/after-delombok/WitherOnClass.java
+++ b/test/transform/resource/after-delombok/WitherOnClass.java
@@ -35,7 +35,9 @@ class WitherOnClass3 {
}
@java.lang.SuppressWarnings("all")
public WitherOnClass3 withNonNull(@lombok.NonNull final String nonNull) {
- if (nonNull == null) throw new java.lang.NullPointerException("nonNull");
+ if (nonNull == null) {
+ throw new java.lang.NullPointerException("nonNull");
+ }
return this.nonNull == nonNull ? this : new WitherOnClass3(this.couldBeNull, nonNull);
}
}
diff --git a/test/transform/resource/after-ecj/DataOnLocalClass.java b/test/transform/resource/after-ecj/DataOnLocalClass.java
index 137edf50..2f8dcca1 100644
--- a/test/transform/resource/after-ecj/DataOnLocalClass.java
+++ b/test/transform/resource/after-ecj/DataOnLocalClass.java
@@ -63,7 +63,9 @@ class DataOnLocalClass2 {
}
public @java.lang.SuppressWarnings("all") void setName(final @lombok.NonNull String name) {
if ((name == null))
- throw new java.lang.NullPointerException("name");
+ {
+ throw new java.lang.NullPointerException("name");
+ }
this.name = name;
}
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
@@ -96,7 +98,9 @@ class DataOnLocalClass2 {
public @java.lang.SuppressWarnings("all") InnerLocal(final @lombok.NonNull String name) {
super();
if ((name == null))
- throw new java.lang.NullPointerException("name");
+ {
+ throw new java.lang.NullPointerException("name");
+ }
this.name = name;
}
}
diff --git a/test/transform/resource/after-ecj/NonNullOnParameter.java b/test/transform/resource/after-ecj/NonNullOnParameter.java
new file mode 100644
index 00000000..bbceb153
--- /dev/null
+++ b/test/transform/resource/after-ecj/NonNullOnParameter.java
@@ -0,0 +1,61 @@
+class NonNullOnParameter extends Thread {
+ NonNullOnParameter(@lombok.NonNull String arg) {
+ this(arg, "");
+ if ((arg == null))
+ {
+ throw new java.lang.NullPointerException("arg");
+ }
+ }
+ NonNullOnParameter(@lombok.NonNull String arg, @lombok.NonNull String arg2) {
+ super(arg);
+ if ((arg2 == null))
+ {
+ throw new java.lang.NullPointerException("arg2");
+ }
+ if ((arg == null))
+ throw new NullPointerException();
+ }
+ public void test2(@lombok.NonNull String arg, @lombok.NonNull String arg2, @lombok.NonNull String arg3) {
+ if ((arg == null))
+ {
+ throw new java.lang.NullPointerException("arg");
+ }
+ if ((arg3 == null))
+ {
+ throw new java.lang.NullPointerException("arg3");
+ }
+ if ((arg2 == null))
+ {
+ throw new NullPointerException("arg2");
+ }
+ if ((arg == null))
+ System.out.println("Hello");
+ }
+ public void test3(@lombok.NonNull String arg) {
+ if ((arg == null))
+ {
+ throw new java.lang.NullPointerException("arg");
+ }
+ if ((arg != null))
+ throw new IllegalStateException();
+ }
+ public void test(@lombok.NonNull String stringArg, @lombok.NonNull String arg2, @lombok.NonNull int primitiveArg) {
+ if ((stringArg == null))
+ {
+ throw new java.lang.NullPointerException("stringArg");
+ }
+ if ((arg2 == null))
+ {
+ throw new java.lang.NullPointerException("arg2");
+ }
+ }
+ public void test(@lombok.NonNull String arg) {
+ if ((arg == null))
+ {
+ throw new java.lang.NullPointerException("arg");
+ }
+ System.out.println("Hey");
+ if ((arg == null))
+ throw new NullPointerException();
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/after-ecj/NonNullPlain.java b/test/transform/resource/after-ecj/NonNullPlain.java
index c9c96d0a..6e937f6a 100644
--- a/test/transform/resource/after-ecj/NonNullPlain.java
+++ b/test/transform/resource/after-ecj/NonNullPlain.java
@@ -8,7 +8,9 @@ import java.lang.annotation.*;
public @java.beans.ConstructorProperties({"i", "s"}) @java.lang.SuppressWarnings("all") NonNullPlain(final @lombok.NonNull int i, final @lombok.NonNull String s) {
super();
if ((s == null))
- throw new java.lang.NullPointerException("s");
+ {
+ throw new java.lang.NullPointerException("s");
+ }
this.i = i;
this.s = s;
}
@@ -26,7 +28,9 @@ import java.lang.annotation.*;
}
public @java.lang.SuppressWarnings("all") void setS(final @lombok.NonNull String s) {
if ((s == null))
- throw new java.lang.NullPointerException("s");
+ {
+ throw new java.lang.NullPointerException("s");
+ }
this.s = s;
}
public @java.lang.SuppressWarnings("all") void setO(final Object o) {
diff --git a/test/transform/resource/after-ecj/SetterOnClass.java b/test/transform/resource/after-ecj/SetterOnClass.java
index da928f24..aa3459bb 100644
--- a/test/transform/resource/after-ecj/SetterOnClass.java
+++ b/test/transform/resource/after-ecj/SetterOnClass.java
@@ -63,7 +63,9 @@
}
public @java.lang.SuppressWarnings("all") void setNonNull(final @lombok.NonNull String nonNull) {
if ((nonNull == null))
- throw new java.lang.NullPointerException("nonNull");
+ {
+ throw new java.lang.NullPointerException("nonNull");
+ }
this.nonNull = nonNull;
}
}
diff --git a/test/transform/resource/after-ecj/WitherOnClass.java b/test/transform/resource/after-ecj/WitherOnClass.java
index ff4566e5..82132e87 100644
--- a/test/transform/resource/after-ecj/WitherOnClass.java
+++ b/test/transform/resource/after-ecj/WitherOnClass.java
@@ -33,7 +33,9 @@
}
public @java.lang.SuppressWarnings("all") WitherOnClass3 withNonNull(final @lombok.NonNull String nonNull) {
if ((nonNull == null))
- throw new java.lang.NullPointerException("nonNull");
+ {
+ throw new java.lang.NullPointerException("nonNull");
+ }
return ((this.nonNull == nonNull) ? this : new WitherOnClass3(this.couldBeNull, nonNull));
}
}
diff --git a/test/transform/resource/before/NonNullOnParameter.java b/test/transform/resource/before/NonNullOnParameter.java
new file mode 100644
index 00000000..7eb4c565
--- /dev/null
+++ b/test/transform/resource/before/NonNullOnParameter.java
@@ -0,0 +1,30 @@
+class NonNullOnParameter extends Thread {
+ NonNullOnParameter(@lombok.NonNull String arg) {
+ this(arg, "");
+ }
+
+ NonNullOnParameter(@lombok.NonNull String arg, @lombok.NonNull String arg2) {
+ super(arg);
+ if (arg == null) throw new NullPointerException();
+ }
+
+ public void test2(@lombok.NonNull String arg, @lombok.NonNull String arg2, @lombok.NonNull String arg3) {
+ if (arg2 == null) {
+ throw new NullPointerException("arg2");
+ }
+ if (arg == null) System.out.println("Hello");
+ }
+
+ public void test3(@lombok.NonNull String arg) {
+ if (arg != null) throw new IllegalStateException();
+ }
+
+ public void test(@lombok.NonNull String stringArg, @lombok.NonNull String arg2, @lombok.NonNull int primitiveArg) {
+
+ }
+
+ public void test(@lombok.NonNull String arg) {
+ System.out.println("Hey");
+ if (arg == null) throw new NullPointerException();
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/messages-delombok/NonNullOnParameter.java.messages b/test/transform/resource/messages-delombok/NonNullOnParameter.java.messages
new file mode 100644
index 00000000..ac87adcd
--- /dev/null
+++ b/test/transform/resource/messages-delombok/NonNullOnParameter.java.messages
@@ -0,0 +1 @@
+22:89 @NonNull is meaningless on a primitive.
diff --git a/test/transform/resource/messages-delombok/NonNullPlain.java.messages b/test/transform/resource/messages-delombok/NonNullPlain.java.messages
new file mode 100644
index 00000000..67eb8abe
--- /dev/null
+++ b/test/transform/resource/messages-delombok/NonNullPlain.java.messages
@@ -0,0 +1 @@
+7:9 @NonNull is meaningless on a primitive.
\ No newline at end of file
diff --git a/test/transform/resource/messages-ecj/NonNullOnParameter.java.messages b/test/transform/resource/messages-ecj/NonNullOnParameter.java.messages
new file mode 100644
index 00000000..1539929b
--- /dev/null
+++ b/test/transform/resource/messages-ecj/NonNullOnParameter.java.messages
@@ -0,0 +1,3 @@
+15:460 Dead code
+22:683 @NonNull is meaningless on a primitive.
+28:823 Dead code
\ No newline at end of file
diff --git a/test/transform/resource/messages-ecj/NonNullPlain.java.messages b/test/transform/resource/messages-ecj/NonNullPlain.java.messages
new file mode 100644
index 00000000..96eed252
--- /dev/null
+++ b/test/transform/resource/messages-ecj/NonNullPlain.java.messages
@@ -0,0 +1 @@
+7:116 @NonNull is meaningless on a primitive.
\ No newline at end of file
diff --git a/test/transform/resource/messages-idempotent/NonNullOnParameter.java.messages b/test/transform/resource/messages-idempotent/NonNullOnParameter.java.messages
new file mode 100644
index 00000000..fd23a32a
--- /dev/null
+++ b/test/transform/resource/messages-idempotent/NonNullOnParameter.java.messages
@@ -0,0 +1 @@
+33:89 @NonNull is meaningless on a primitive.
\ No newline at end of file
diff --git a/test/transform/resource/messages-idempotent/NonNullPlain.java.messages b/test/transform/resource/messages-idempotent/NonNullPlain.java.messages
new file mode 100644
index 00000000..c48da311
--- /dev/null
+++ b/test/transform/resource/messages-idempotent/NonNullPlain.java.messages
@@ -0,0 +1,3 @@
+4:9 @NonNull is meaningless on a primitive.
+18:29 @NonNull is meaningless on a primitive.
+44:26 @NonNull is meaningless on a primitive.
\ No newline at end of file
diff --git a/usage_examples/NonNullExample_post.jpage b/usage_examples/NonNullExample_post.jpage
new file mode 100644
index 00000000..24175e06
--- /dev/null
+++ b/usage_examples/NonNullExample_post.jpage
@@ -0,0 +1,13 @@
+import lombok.NonNull;
+
+public class NonNullExample extends Something {
+ private String name;
+
+ public NonNullExample(@NonNull Person person) {
+ super("Hello");
+ if (person == null) {
+ throw new NullPointerException("person");
+ }
+ this.name = person.getName();
+ }
+}
diff --git a/usage_examples/NonNullExample_pre.jpage b/usage_examples/NonNullExample_pre.jpage
new file mode 100644
index 00000000..47556ce7
--- /dev/null
+++ b/usage_examples/NonNullExample_pre.jpage
@@ -0,0 +1,10 @@
+import lombok.NonNull;
+
+public class NonNullExample extends Something {
+ private String name;
+
+ public NonNullExample(@NonNull Person person) {
+ super("Hello");
+ this.name = person.getName();
+ }
+}
diff --git a/website/features/Cleanup.html b/website/features/Cleanup.html
index d1637dd4..a6e41f39 100644
--- a/website/features/Cleanup.html
+++ b/website/features/Cleanup.html
@@ -60,7 +60,7 @@
diff --git a/website/features/Data.html b/website/features/Data.html
index 8ace96cb..1c8510b7 100644
--- a/website/features/Data.html
+++ b/website/features/Data.html
@@ -75,7 +75,7 @@
diff --git a/website/features/Delegate.html b/website/features/Delegate.html
index 532f3f54..02cdf290 100644
--- a/website/features/Delegate.html
+++ b/website/features/Delegate.html
@@ -55,16 +55,15 @@
When passing classes to the annotation's types or excludes parameter, you cannot include generics.
This is a limitation of java. Use private inner interfaces or classes that extend the intended type including the
generics parameter to work around this problem.
-
-
+
When passing classes to the annotation, these classes do not need to be supertypes of the field. See the example.
-
-
+
@Delegate cannot be used on static fields or methods.
-
or: How I learned to stop worrying and love the NullPointerException.
+
+
Overview
+
+ NEW in Lombok 0.11.10: You can use @NonNull on the parameter of a method or constructor to have lombok generate a null-check statement for you.
+
+ Lombok has always treated any annotation named @NonNull on a field as a signal to generate a null-check if lombok generates an entire method or constructor for you, via
+ for example @Data. Now, however, using lombok's own @lombok.NonNull on a parameter results in the insertion of just the null-check
+ statement inside your own method or constructor.
+
+ The null-check looks like if (param == null) throw new NullPointerException("param"); and will be inserted at the very top of your method. For constructors, the null-check
+ will be inserted immediately following any explicit this() or super() calls.
+
+ If a null-check is already present at the top, no additional null-check will be generated.
+
+
+
+
+
With Lombok
+
@HTML_PRE@
+
+
+
+
Vanilla Java
+
@HTML_POST@
+
+
+
+
+
Small print
+
+ Lombok's detection scheme for already existing null-checks consists of scanning for if statements that look just like lombok's own. Any 'throws' statement as
+ the 'then' part of the if statement, whether in braces or not, counts. The conditional of the if statement must look exactly like PARAMNAME == null.
+ The first statement in your method that is not such a null-check stops the process of inspecting for null-checks.
+
+ While @Data and other method-generating lombok annotations will trigger on any annotation named @NonNull regardless of casing or package name,
+ this feature only triggers on lombok's own @NonNull annotation from the lombok package.
+
+ A @NonNull on a primitive parameter results in a warning. No null-check will be generated.
+
Because @SneakyThrows is an implementation detail and not part of your method signature, it is an error if you try to
declare a checked exception as sneakily thrown when you don't call any methods that throw this exception. (Doing so is perfectly legal
for throws statements to accommodate subclasses). Similarly, @SneakyThrows does not inherit.
@@ -72,7 +70,7 @@
All together now: A shortcut for @ToString, @EqualsAndHashCode,
@Getter on all fields, and @Setter on all non-final fields, and @RequiredArgsConstructor!
@Value was an experimental feature from v0.11.4 to v0.11.9 (as @lombok.experimental.Value). It has since been moved into the core package. The old annotation is still
around (and is an alias). It will eventually be removed in a future version, though.
+
+ It is not possible to use @FieldDefaults to 'undo' the private-by-default and final-by-default aspect of fields in the annotated class. Use @NonFinal and @PackagePrivate on the fields in the class to override this behaviour.
+
diff --git a/website/features/experimental/index.html b/website/features/experimental/index.html
index 31fcd5ad..16d58050 100644
--- a/website/features/experimental/index.html
+++ b/website/features/experimental/index.html
@@ -23,7 +23,7 @@
as a core feature and move out of the experimental package.