diff options
40 files changed, 674 insertions, 57 deletions
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 @@ -146,6 +146,9 @@ such as converting the changelog into HTML, and creating javadoc. <param name="transformationName" value="Delegate" /> </antcall> <antcall target="-integrateSnippet"> + <param name="transformationName" value="NonNull" /> + </antcall> + <antcall target="-integrateSnippet"> <param name="transformationName" value="experimental/Accessors" /> </antcall> <antcall target="-integrateSnippet"> 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. + * <p> + * 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 <em>only</em> 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 <strong>be deleted</strong> 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<NonNull> { + @Override public void handle(AnnotationValues<NonNull> 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> 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<SneakyThrows> { } } - 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<JCStatement> 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 extends JCTree> 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.<JCExpression>nil(), npe, List.<JCExpression>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<NonNull> { + @Override public void handle(AnnotationValues<NonNull> 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> 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<JCStatement> 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<JCStatement> tail = statements; + List<JCStatement> head = List.nil(); + for (JCStatement stat : statements) { + if (JavacHandlerUtil.isConstructorCall(stat) || JavacHandlerUtil.isGenerated(stat)) { + tail = tail.tail; + head = head.prepend(stat); + continue; + } + break; + } + + List<JCStatement> 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<JCStatement> 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 @@ </div> </div> <div class="footer"> - <a href="index.html">Back to features</a> | <a href="Data.html">Previous feature (@Data)</a> | <a href="Synchronized.html">Next feature (@Synchronized)</a><br /> + <a href="index.html">Back to features</a> | <a href="NonNull.html">Previous feature (@NonNull)</a> | <a href="GetterSetter.html">Next feature (@Getter / @Setter)</a><br /> <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> 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 @@ </div> </div> <div class="footer"> - <a href="index.html">Back to features</a> | <a href="Constructor.html">Previous feature (@<em>X</em>Constructor)</a> | <a href="Cleanup.html">Next feature (@Cleanup)</a><br /> + <a href="index.html">Back to features</a> | <a href="Constructor.html">Previous feature (@<em>X</em>Constructor)</a> | <a href="SneakyThrows.html">Next feature (@SneakyThrows)</a><br /> <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> 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 <code>types</code> or <code>excludes</code> 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. - </p> - <p> + </p><p> When passing classes to the annotation, these classes do not need to be supertypes of the field. See the example. - </p> - <p> + </p><p> <code>@Delegate</code> cannot be used on static fields or methods. - <div> + </p> + </div> </div> <div class="footer"> - <a href="index.html">Back to features</a> | <a href="val.html">Previous feature (val)</a> | <span class="disabled">Next feature</span><br /> + <a href="index.html">Back to features</a> | <a href="Log.html">Previous feature (@Log)</a> | <span class="disabled">Next feature</span><br /> <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2010-2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> diff --git a/website/features/GetterLazy.html b/website/features/GetterLazy.html index bc5ecb0c..f70c8e78 100644 --- a/website/features/GetterLazy.html +++ b/website/features/GetterLazy.html @@ -46,7 +46,7 @@ </div> </div> <div class="footer"> - <a href="index.html">Back to features</a> | <a href="GetterSetter.html">Previous feature (@Getter / @Setter)</a> | <a href="ToString.html">Next feature (@ToString)</a><br /> + <a href="index.html">Back to features</a> | <a href="Synchronized.html">Previous feature (@Synchronized)</a> | <a href="Log.html">Next feature (@Log)</a><br /> <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> diff --git a/website/features/GetterSetter.html b/website/features/GetterSetter.html index 03704119..c78b03bd 100644 --- a/website/features/GetterSetter.html +++ b/website/features/GetterSetter.html @@ -75,7 +75,7 @@ </div> </div> <div class="footer"> - <a href="index.html">Back to features</a> | <span class="disabled">Previous feature</span> | <a href="GetterLazy.html">Next feature (@Getter(lazy=true))</a><br /> + <a href="index.html">Back to features</a> | <a href="Cleanup.html">Previous feature (@Cleanup)</a> | <a href="ToString.html">Next feature (@ToString)</a><br /> <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> diff --git a/website/features/Log.html b/website/features/Log.html index 2fb91956..2d4fa375 100644 --- a/website/features/Log.html +++ b/website/features/Log.html @@ -60,7 +60,7 @@ </div> </div> <div class="footer"> - <a href="index.html">Back to features</a> | <a href="SneakyThrows.html">Previous feature (@SneakyThrows)</a> | <a href="val.html">Next feature (val)</a><br /> + <a href="index.html">Back to features</a> | <a href="GetterLazy.html">Previous feature (@Getter(lazy=true))</a> | <a href="Delegate.html">Next feature (@Delegate)</a><br /> <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> diff --git a/website/features/NonNull.html b/website/features/NonNull.html new file mode 100644 index 00000000..9faad502 --- /dev/null +++ b/website/features/NonNull.html @@ -0,0 +1,73 @@ +<!DOCTYPE html> +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <link rel="stylesheet" type="text/css" href="../logi/reset.css" /> + <link rel="stylesheet" type="text/css" href="features.css" /> + <link rel="shortcut icon" href="../favicon.ico" type="image/x-icon" /> + <meta name="description" content="Spice up your java" /> + <title>val</title> +</head><body><div id="pepper"> + <div class="minimumHeight"></div> + <div class="meat"> + <div class="header"><a href="../index.html">Project Lombok</a></div> + <h1>@NonNull</h1> + <div class="byline">or: How I learned to stop worrying and love the NullPointerException.</div> + <div class="overview"> + <h3>Overview</h3> + <p> + <em>NEW in Lombok 0.11.10: </em>You can use <code>@NonNull</code> on the parameter of a method or constructor to have lombok generate a null-check statement for you. + </p><p> + Lombok has always treated any annotation named <code>@NonNull</code> on a field as a signal to generate a null-check if lombok generates an entire method or constructor for you, via + for example <a href="Data.html"><code>@Data</code></a>. Now, however, using lombok's own <code>@lombok.NonNull</code> on a parameter results in the insertion of just the null-check + statement inside your own method or constructor. + </p><p> + The null-check looks like <code>if (param == null) throw new NullPointerException("param");</code> and will be inserted at the very top of your method. For constructors, the null-check + will be inserted immediately following any explicit <code>this()</code> or <code>super()</code> calls. + </p><p> + If a null-check is already present at the top, no additional null-check will be generated. + </p> + </div> + <div class="snippets"> + <div class="pre"> + <h3>With Lombok</h3> + <div class="snippet">@HTML_PRE@</div> + </div> + <div class="sep"></div> + <div class="post"> + <h3>Vanilla Java</h3> + <div class="snippet">@HTML_POST@</div> + </div> + </div> + <div style="clear: left;"></div> + <div class="overview"> + <h3>Small print</h3><div class="smallprint"> + <p> + 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 <em>must</em> look exactly like <code>PARAMNAME == null</code>. + The first statement in your method that is not such a null-check stops the process of inspecting for null-checks. + </p><p> + While <code>@Data</code> and other method-generating lombok annotations will trigger on any annotation named <code>@NonNull</code> regardless of casing or package name, + this feature only triggers on lombok's own <code>@NonNull</code> annotation from the <code>lombok</code> package. + </p><p> + A <code>@NonNull</code> on a primitive parameter results in a warning. No null-check will be generated. + </p> + </div> + </div> + <div class="footer"> + <a href="index.html">Back to features</a> | <a href="val.html">Previous feature (val)</a> | <a href="Cleanup.html">Next feature (@Cleanup)</a><br /> + <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> + </div> + <div style="clear: both;"></div> + </div> +</div> +<script type="text/javascript"> + var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); + document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); +</script> +<script type="text/javascript"> + try { + var pageTracker = _gat._getTracker("UA-9884254-1"); + pageTracker._trackPageview(); + } catch(err) {} +</script> +</body></html> diff --git a/website/features/SneakyThrows.html b/website/features/SneakyThrows.html index 808a7879..573bd95c 100644 --- a/website/features/SneakyThrows.html +++ b/website/features/SneakyThrows.html @@ -54,8 +54,6 @@ <div class="overview"> <h3>Small print</h3><div class="smallprint"> <p> - <code>@SneakyThrows</code> is an implementation of this feature request: <a href="http://bugs.sun.com/view_bug.do?bug_id=6534270">http://bugs.sun.com/view_bug.do?bug_id=6534270</a>. - </p><p> Because <code>@SneakyThrows</code> 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 <code>throws</code> statements to accommodate subclasses). Similarly, <code>@SneakyThrows</code> does not inherit. @@ -72,7 +70,7 @@ </div> </div> <div class="footer"> - <a href="index.html">Back to features</a> | <a href="Synchronized.html">Previous feature (@Synchronized)</a> | <a href="Log.html">Next feature (@Log)</a><br /> + <a href="index.html">Back to features</a> | <a href="Data.html">Previous feature (@Data)</a> | <a href="Synchronized.html">Next feature (@Synchronized)</a><br /> <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> diff --git a/website/features/Synchronized.html b/website/features/Synchronized.html index 4b6ef251..9ab6c87f 100644 --- a/website/features/Synchronized.html +++ b/website/features/Synchronized.html @@ -59,7 +59,7 @@ </div> </div> <div class="footer"> - <a href="index.html">Back to features</a> | <a href="Cleanup.html">Previous feature (@Cleanup)</a> | <a href="SneakyThrows.html">Next feature (@SneakyThrows)</a><br /> + <a href="index.html">Back to features</a> | <a href="SneakyThrows.html">Previous feature (@SneakyThrows)</a> | <a href="GetterLazy.html">Next feature (@Getter(lazy=true))</a><br /> <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> diff --git a/website/features/ToString.html b/website/features/ToString.html index c3b389ba..585dc72b 100644 --- a/website/features/ToString.html +++ b/website/features/ToString.html @@ -68,7 +68,7 @@ </div> </div> <div class="footer"> - <a href="index.html">Back to features</a> | <a href="GetterLazy.html">Previous feature (@Getter(lazy=true))</a> | <a href="EqualsAndHashCode.html">Next feature (@EqualsAndHashCode)</a><br /> + <a href="index.html">Back to features</a> | <a href="GetterSetter.html">Previous feature (@Getter / @Setter)</a> | <a href="EqualsAndHashCode.html">Next feature (@EqualsAndHashCode)</a><br /> <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> diff --git a/website/features/index.html b/website/features/index.html index 9751a37d..8b3765c1 100644 --- a/website/features/index.html +++ b/website/features/index.html @@ -13,10 +13,14 @@ <h1>Lombok features</h1> <div class="index overview"> <dl> + <dt><a href="val.html"><code>val</code></a></dt> + <dd>Finally! Hassle-free final local variables.</dd> + <dt><a href="NonNull.html"><code>@NonNull</code></a></dt> + <dd>or: How I learned to stop worrying and love the NullPointerException.</dd> + <dt><a href="Cleanup.html"><code>@Cleanup</code></a></dt> + <dd>Automatic resource management: Call your <code>close()</code> methods safely with no hassle.</dd> <dt><a href="GetterSetter.html"><code>@Getter</code> / <code>@Setter</code></a></dt> <dd>Never write <code>public int getFoo() {return foo;}</code> again.</dd> - <dt><a href="GetterLazy.html"><code>@Getter(lazy=true)</code></a></dt> - <dd>Laziness is a virtue!</dd> <dt><a href="ToString.html"><code>@ToString</code></a></dt> <dd>No need to start a debugger to see your fields: Just let lombok generate a <code>toString</code> for you!</dd> <dt><a href="EqualsAndHashCode.html"><code>@EqualsAndHashCode</code></a></dt> @@ -26,16 +30,14 @@ <dt><a href="Data.html"><code>@Data</code></a></dt> <dd>All together now: A shortcut for <code>@ToString</code>, <code>@EqualsAndHashCode</code>, <code>@Getter</code> on all fields, and <code>@Setter</code> on all non-final fields, and <code>@RequiredArgsConstructor</code>!</dd> - <dt><a href="Cleanup.html"><code>@Cleanup</code></a></dt> - <dd>Automatic resource management: Call your <code>close()</code> methods safely with no hassle.</dd> - <dt><a href="Synchronized.html"><code>@Synchronized</code></a></dt> - <dd><code>synchronized</code> done right: Don't expose your locks.</dd> <dt><a href="SneakyThrows.html"><code>@SneakyThrows</code></a></dt> <dd>To boldly throw checked exceptions where no one has thrown them before!</dd> + <dt><a href="Synchronized.html"><code>@Synchronized</code></a></dt> + <dd><code>synchronized</code> done right: Don't expose your locks.</dd> + <dt><a href="GetterLazy.html"><code>@Getter(lazy=true)</code></a></dt> + <dd>Laziness is a virtue!</dd> <dt><a href="Log.html"><code>@Log</code></a></dt> <dd>Captain's Log, stardate 24435.7: "What was that line again?"</dd> - <dt><a href="val.html"><code>val</code></a></dt> - <dd>Finally! Hassle-free final local variables.</dd> <dt><a href="Delegate.html"><code>@Delegate</code></a></dt> <dd>Don't lose your composition.</dd> <dt><a href="experimental/index.html">experimental features</a></dt> diff --git a/website/features/val.html b/website/features/val.html index cec799e9..c4c8ad27 100644 --- a/website/features/val.html +++ b/website/features/val.html @@ -50,7 +50,7 @@ </div> </div> <div class="footer"> - <a href="index.html">Back to features</a> | <a href="Log.html">Previous feature (@Log)</a> | <a href="Delegate.html">Next feature (@Delegate)</a><br /> + <a href="index.html">Back to features</a> | <span class="disabled">Previous feature</span> | <a href="NonNull.html">Next feature (@NonNull)</a><br /> <a href="../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2010-2013 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> |